@camstack/addon-notifiers 1.1.22 → 1.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/addon.js +1478 -133
- package/dist/addon.mjs +1478 -133
- package/package.json +4 -1
package/dist/addon.js
CHANGED
|
@@ -2,6 +2,7 @@ Object.defineProperties(exports, {
|
|
|
2
2
|
__esModule: { value: true },
|
|
3
3
|
[Symbol.toStringTag]: { value: "Module" }
|
|
4
4
|
});
|
|
5
|
+
let node_zlib = require("node:zlib");
|
|
5
6
|
//#region ../../node_modules/zod/v4/core/core.js
|
|
6
7
|
var _a$1;
|
|
7
8
|
function $constructor(name, initializer, params) {
|
|
@@ -4290,6 +4291,14 @@ function object(shape, params) {
|
|
|
4290
4291
|
...normalizeParams(params)
|
|
4291
4292
|
});
|
|
4292
4293
|
}
|
|
4294
|
+
function looseObject(shape, params) {
|
|
4295
|
+
return new ZodObject({
|
|
4296
|
+
type: "object",
|
|
4297
|
+
shape,
|
|
4298
|
+
catchall: unknown(),
|
|
4299
|
+
...normalizeParams(params)
|
|
4300
|
+
});
|
|
4301
|
+
}
|
|
4293
4302
|
var ZodUnion = /*@__PURE__*/ $constructor("ZodUnion", (inst, def) => {
|
|
4294
4303
|
$ZodUnion.init(inst, def);
|
|
4295
4304
|
ZodType.init(inst, def);
|
|
@@ -4631,7 +4640,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4631
4640
|
return inst;
|
|
4632
4641
|
}
|
|
4633
4642
|
//#endregion
|
|
4634
|
-
//#region ../types/dist/sleep-
|
|
4643
|
+
//#region ../types/dist/sleep-_sv7WKkq.mjs
|
|
4635
4644
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4636
4645
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4637
4646
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5041,10 +5050,51 @@ var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
|
5041
5050
|
EventCategory["EnrichmentEmbeddingStored"] = "enrichment.embedding.stored";
|
|
5042
5051
|
EventCategory["EnrichmentSceneStateChanged"] = "enrichment.scene.state-changed";
|
|
5043
5052
|
EventCategory["EnrichmentActivitySummary"] = "enrichment.activity.summary";
|
|
5053
|
+
/**
|
|
5054
|
+
* Unified track-lifecycle event: ONE category carrying `phase:
|
|
5055
|
+
* 'start' | 'update' | 'end'` with a rich, typed
|
|
5056
|
+
* `PipelineAnalyticsTrackLifecyclePayload`. Supersedes the thin
|
|
5057
|
+
* `PipelineAnalyticsTrackStarted` / `PipelineAnalyticsTrackEnded`
|
|
5058
|
+
* pair — a consumer subscribes once and branches on `phase`.
|
|
5059
|
+
*/
|
|
5060
|
+
EventCategory["PipelineAnalyticsTrackLifecycle"] = "pipeline-analytics.track-lifecycle";
|
|
5061
|
+
/**
|
|
5062
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5063
|
+
* (`phase:'start'`). Kept as a soft alias — no known subscribers.
|
|
5064
|
+
*/
|
|
5044
5065
|
EventCategory["PipelineAnalyticsTrackStarted"] = "pipeline-analytics.track-started";
|
|
5066
|
+
/**
|
|
5067
|
+
* @deprecated Superseded by {@link PipelineAnalyticsTrackLifecycle}
|
|
5068
|
+
* (`phase:'end'`). Kept as a soft alias — no known subscribers.
|
|
5069
|
+
*/
|
|
5045
5070
|
EventCategory["PipelineAnalyticsTrackEnded"] = "pipeline-analytics.track-ended";
|
|
5046
5071
|
EventCategory["PipelineAnalyticsDetectionEvent"] = "pipeline-analytics.detection-event";
|
|
5047
5072
|
EventCategory["PipelineAnalyticsFrameTracked"] = "pipeline-analytics.frame-tracked";
|
|
5073
|
+
/**
|
|
5074
|
+
* Fired by `addon-post-analysis` when a parked (stationary) object appears
|
|
5075
|
+
* (a track was promoted to a stationary-registry entry) or departs (the
|
|
5076
|
+
* object moved / was removed). Telemetry (D8): lossy, drives a UI refresh of
|
|
5077
|
+
* the dedicated "Stationary" section — never the live event feed. Payload:
|
|
5078
|
+
* `{ deviceId, entryId, className, phase:'appeared'|'departed', timestamp }`.
|
|
5079
|
+
*/
|
|
5080
|
+
EventCategory["PipelineAnalyticsStationaryChanged"] = "pipeline-analytics.stationary-changed";
|
|
5081
|
+
/**
|
|
5082
|
+
* Fired by `addon-post-analysis` whenever a gallery face row changes:
|
|
5083
|
+
* `kind:'buffered'` a new detected face was persisted, `'assigned'` /
|
|
5084
|
+
* `'unassigned'` its identity link changed, `'deleted'` the row was
|
|
5085
|
+
* removed. Telemetry semantics (D8): a lost event only delays a refresh,
|
|
5086
|
+
* never a correctness issue. Payload `PipelineAnalyticsFaceGalleryChangedPayload`.
|
|
5087
|
+
*/
|
|
5088
|
+
EventCategory["PipelineAnalyticsFaceGalleryChanged"] = "pipeline-analytics.face-gallery-changed";
|
|
5089
|
+
/**
|
|
5090
|
+
* Fired by `addon-post-analysis` whenever a gallery plate row changes:
|
|
5091
|
+
* `kind:'buffered'` a new read was persisted (`PlateRecognizer.onTrackEnd`),
|
|
5092
|
+
* `'assigned'` / `'unassigned'` its vehicle link changed
|
|
5093
|
+
* (`PlateGalleryProvider`), `'deleted'` the row was removed. Telemetry
|
|
5094
|
+
* semantics (D8): a lost event only delays a refresh, never a correctness
|
|
5095
|
+
* issue. Payload `PipelineAnalyticsPlateGalleryChangedPayload`.
|
|
5096
|
+
*/
|
|
5097
|
+
EventCategory["PipelineAnalyticsPlateGalleryChanged"] = "pipeline-analytics.plate-gallery-changed";
|
|
5048
5098
|
EventCategory["FrigateLiveEvent"] = "frigate.live-event";
|
|
5049
5099
|
EventCategory["CameraStreamsProfileSlotsChanged"] = "camera-streams.onProfileSlotsChanged";
|
|
5050
5100
|
/**
|
|
@@ -6873,7 +6923,8 @@ var MODEL_FORMATS = [
|
|
|
6873
6923
|
"coreml",
|
|
6874
6924
|
"openvino",
|
|
6875
6925
|
"tflite",
|
|
6876
|
-
"pt"
|
|
6926
|
+
"pt",
|
|
6927
|
+
"gguf"
|
|
6877
6928
|
];
|
|
6878
6929
|
/**
|
|
6879
6930
|
* Multi-file format payload.
|
|
@@ -6917,7 +6968,8 @@ var ModelFormatsSchema = object({
|
|
|
6917
6968
|
coreml: ModelFormatEntrySchema.optional(),
|
|
6918
6969
|
openvino: ModelFormatEntrySchema.optional(),
|
|
6919
6970
|
tflite: ModelFormatEntrySchema.optional(),
|
|
6920
|
-
pt: ModelFormatEntrySchema.optional()
|
|
6971
|
+
pt: ModelFormatEntrySchema.optional(),
|
|
6972
|
+
gguf: ModelFormatEntrySchema.optional()
|
|
6921
6973
|
});
|
|
6922
6974
|
/**
|
|
6923
6975
|
* Variant-selector grouping axes. Shared by the full `ModelCatalogEntry` and by
|
|
@@ -6958,6 +7010,15 @@ var ModelCatalogEntrySchema = object({
|
|
|
6958
7010
|
width: number(),
|
|
6959
7011
|
height: number()
|
|
6960
7012
|
}),
|
|
7013
|
+
/**
|
|
7014
|
+
* Channel count of the model input tensor. Omit ⇒ 3 (RGB), the default for
|
|
7015
|
+
* every detector / classifier / embedder. Set to 1 for a grayscale CTC text
|
|
7016
|
+
* recognizer (EasyOCR VGG plate-OCR: input `[N,1,H,W]`) so the preprocess
|
|
7017
|
+
* feeds a single-channel, EasyOCR-normalized tensor instead of the default
|
|
7018
|
+
* 3-channel RGB one. Threaded through `PoolModelConfig.inputChannels` to the
|
|
7019
|
+
* Python inference pool.
|
|
7020
|
+
*/
|
|
7021
|
+
inputChannels: number().int().positive().optional(),
|
|
6961
7022
|
labels: array(LabelDefinitionSchema).readonly(),
|
|
6962
7023
|
inputLayout: _enum(["nchw", "nhwc"]).optional(),
|
|
6963
7024
|
inputNormalization: _enum([
|
|
@@ -6967,6 +7028,16 @@ var ModelCatalogEntrySchema = object({
|
|
|
6967
7028
|
]).optional(),
|
|
6968
7029
|
preprocessMode: _enum(["letterbox", "resize"]).optional(),
|
|
6969
7030
|
/**
|
|
7031
|
+
* Per-MODEL postprocessor override. Absent ⇒ the step's own
|
|
7032
|
+
* `StepDefinition.postprocessor` applies (the normal case — every model in a
|
|
7033
|
+
* step shares its decode). Set it when a step hosts models with DIFFERENT raw
|
|
7034
|
+
* output layouts under one slot: e.g. object-detection is `'yolo'` by default,
|
|
7035
|
+
* but a Coral SSD MobileNet build emits the `TFLite_Detection_PostProcess`
|
|
7036
|
+
* 4-tensor layout and needs `'ssd'`. Threaded into `PoolModelConfig.postprocessor`
|
|
7037
|
+
* by the engine factory (`modelEntry.postprocessor ?? def.postprocessor`).
|
|
7038
|
+
*/
|
|
7039
|
+
postprocessor: custom().optional(),
|
|
7040
|
+
/**
|
|
6970
7041
|
* When true, the executor produces a landmark-aligned crop (similarity warp
|
|
6971
7042
|
* onto the canonical template) before this step runs, instead of a plain
|
|
6972
7043
|
* axis-aligned bbox crop. Required for face-recognition embedders (ArcFace):
|
|
@@ -7056,6 +7127,123 @@ var ConvertResultSchema = object({
|
|
|
7056
7127
|
})).readonly()
|
|
7057
7128
|
});
|
|
7058
7129
|
/**
|
|
7130
|
+
* Build an `IAddonRouteProvider` from a list of routes. Implements
|
|
7131
|
+
* both the operator-facing `getRoutes` (returning route descriptors
|
|
7132
|
+
* minus the handlers, which can't cross JSON) and the framework-
|
|
7133
|
+
* private `invoke` method that the hub calls when this provider lives
|
|
7134
|
+
* in a forked worker.
|
|
7135
|
+
*
|
|
7136
|
+
* Co-located addons use the returned `getRoutes` directly because
|
|
7137
|
+
* their handlers don't need to cross any wire. The `invoke` method
|
|
7138
|
+
* is present anyway so the bridge code on the hub is uniform — it
|
|
7139
|
+
* doesn't need to switch on "local vs remote provider" at the call
|
|
7140
|
+
* site.
|
|
7141
|
+
*
|
|
7142
|
+
* Example:
|
|
7143
|
+
* const routes: IAddonHttpRoute[] = [
|
|
7144
|
+
* { method: 'GET', path: '/start', access: 'public', handler: this.handleStart },
|
|
7145
|
+
* ]
|
|
7146
|
+
* return [
|
|
7147
|
+
* {
|
|
7148
|
+
* capability: addonRoutesCapability,
|
|
7149
|
+
* provider: buildAddonRouteProvider('auth-oidc', routes),
|
|
7150
|
+
* },
|
|
7151
|
+
* ]
|
|
7152
|
+
*/
|
|
7153
|
+
function buildAddonRouteProvider(id, routes) {
|
|
7154
|
+
return {
|
|
7155
|
+
id,
|
|
7156
|
+
getRoutes: () => routes,
|
|
7157
|
+
invoke: async (input) => {
|
|
7158
|
+
const match = matchRoute(routes, input.method, input.path);
|
|
7159
|
+
if (!match) return {
|
|
7160
|
+
status: 404,
|
|
7161
|
+
headers: {},
|
|
7162
|
+
redirectUrl: null,
|
|
7163
|
+
body: { error: `No route matches ${input.method} ${input.path}` }
|
|
7164
|
+
};
|
|
7165
|
+
const envelope = {
|
|
7166
|
+
status: 200,
|
|
7167
|
+
headers: {},
|
|
7168
|
+
redirectUrl: null
|
|
7169
|
+
};
|
|
7170
|
+
const reply = buildCapturingReply(envelope);
|
|
7171
|
+
const request = {
|
|
7172
|
+
params: {
|
|
7173
|
+
...input.params,
|
|
7174
|
+
...match.params
|
|
7175
|
+
},
|
|
7176
|
+
query: input.query,
|
|
7177
|
+
body: input.body,
|
|
7178
|
+
headers: input.headers,
|
|
7179
|
+
...input.user ? { user: input.user } : {},
|
|
7180
|
+
...input.scopedToken !== void 0 ? { scopedToken: input.scopedToken } : {}
|
|
7181
|
+
};
|
|
7182
|
+
await match.route.handler(request, reply);
|
|
7183
|
+
return envelope;
|
|
7184
|
+
}
|
|
7185
|
+
};
|
|
7186
|
+
}
|
|
7187
|
+
/**
|
|
7188
|
+
* Pattern matcher: same semantics as `AddonRouteRegistry.matchRoute`
|
|
7189
|
+
* but operating on a flat list and bypassing the `/addon/<id>/` prefix
|
|
7190
|
+
* — the bridge sends the post-prefix path directly so we don't need
|
|
7191
|
+
* to round-trip it through normalization.
|
|
7192
|
+
*/
|
|
7193
|
+
function matchRoute(routes, method, path) {
|
|
7194
|
+
const normalizedMethod = method.toUpperCase();
|
|
7195
|
+
for (const route of routes) {
|
|
7196
|
+
if (route.method !== normalizedMethod) continue;
|
|
7197
|
+
const params = matchPath(route.path, path);
|
|
7198
|
+
if (params !== null) return {
|
|
7199
|
+
route,
|
|
7200
|
+
params
|
|
7201
|
+
};
|
|
7202
|
+
}
|
|
7203
|
+
return null;
|
|
7204
|
+
}
|
|
7205
|
+
function matchPath(pattern, p) {
|
|
7206
|
+
const patternParts = pattern.split("/").filter(Boolean);
|
|
7207
|
+
const pathParts = p.split("/").filter(Boolean);
|
|
7208
|
+
if (patternParts.length !== pathParts.length) return null;
|
|
7209
|
+
const params = {};
|
|
7210
|
+
for (let i = 0; i < patternParts.length; i++) {
|
|
7211
|
+
const a = patternParts[i];
|
|
7212
|
+
const b = pathParts[i];
|
|
7213
|
+
if (a.startsWith(":")) params[a.slice(1)] = b;
|
|
7214
|
+
else if (a !== b) return null;
|
|
7215
|
+
}
|
|
7216
|
+
return params;
|
|
7217
|
+
}
|
|
7218
|
+
function buildCapturingReply(envelope) {
|
|
7219
|
+
const wrapper = {
|
|
7220
|
+
status(code) {
|
|
7221
|
+
envelope.status = code;
|
|
7222
|
+
return wrapper;
|
|
7223
|
+
},
|
|
7224
|
+
code(code) {
|
|
7225
|
+
envelope.status = code;
|
|
7226
|
+
return wrapper;
|
|
7227
|
+
},
|
|
7228
|
+
send(data) {
|
|
7229
|
+
envelope.body = data;
|
|
7230
|
+
},
|
|
7231
|
+
redirect(url) {
|
|
7232
|
+
envelope.redirectUrl = url;
|
|
7233
|
+
if (envelope.status === 200) envelope.status = 302;
|
|
7234
|
+
},
|
|
7235
|
+
header(name, value) {
|
|
7236
|
+
envelope.headers[name.toLowerCase()] = value;
|
|
7237
|
+
return wrapper;
|
|
7238
|
+
},
|
|
7239
|
+
type(mime) {
|
|
7240
|
+
envelope.contentType = mime;
|
|
7241
|
+
return wrapper;
|
|
7242
|
+
}
|
|
7243
|
+
};
|
|
7244
|
+
return wrapper;
|
|
7245
|
+
}
|
|
7246
|
+
/**
|
|
7059
7247
|
* Numeric day-of-week: 0 = Sunday … 6 = Saturday (matches `Date.getDay`).
|
|
7060
7248
|
* Named `RecordingWeekday` to avoid collision with the string-union
|
|
7061
7249
|
* `Weekday` exported from `interfaces/timezones.ts`.
|
|
@@ -10605,7 +10793,8 @@ var ModelFormatSchema$1 = _enum([
|
|
|
10605
10793
|
"coreml",
|
|
10606
10794
|
"openvino",
|
|
10607
10795
|
"tflite",
|
|
10608
|
-
"pt"
|
|
10796
|
+
"pt",
|
|
10797
|
+
"gguf"
|
|
10609
10798
|
]);
|
|
10610
10799
|
var PipelineSlotSchema = _enum([
|
|
10611
10800
|
"detector",
|
|
@@ -10699,7 +10888,8 @@ var EngineProvisioningSchema = object({
|
|
|
10699
10888
|
runtimeId: _enum([
|
|
10700
10889
|
"onnx",
|
|
10701
10890
|
"openvino",
|
|
10702
|
-
"coreml"
|
|
10891
|
+
"coreml",
|
|
10892
|
+
"edgetpu"
|
|
10703
10893
|
]).nullable(),
|
|
10704
10894
|
device: string().nullable(),
|
|
10705
10895
|
state: _enum([
|
|
@@ -10860,7 +11050,15 @@ method(_void(), array(PipelineEngineChoiceSchema)), method(_void(), PipelineEngi
|
|
|
10860
11050
|
image: _instanceof(Uint8Array).optional(),
|
|
10861
11051
|
referenceImage: string().optional(),
|
|
10862
11052
|
deviceId: number().optional(),
|
|
10863
|
-
sessionId: string().optional()
|
|
11053
|
+
sessionId: string().optional(),
|
|
11054
|
+
/**
|
|
11055
|
+
* Execution plane. 'full' (default) runs the whole tree — benchmark,
|
|
11056
|
+
* reference-image, and detail-subtree calls. 'frame' is the live
|
|
11057
|
+
* per-frame dispatch: ONLY root-plane steps run; crop children
|
|
11058
|
+
* (inputClasses ≠ null) are skipped and served per-track via
|
|
11059
|
+
* pipelineRunner.runDetailSubtree (two-plane design).
|
|
11060
|
+
*/
|
|
11061
|
+
plane: _enum(["full", "frame"]).optional()
|
|
10864
11062
|
}), PipelineRunResultBridge, { kind: "mutation" }), method(object({
|
|
10865
11063
|
engine: PipelineEngineChoiceSchema.optional(),
|
|
10866
11064
|
steps: array(PipelineStepInputSchema).min(1),
|
|
@@ -11003,6 +11201,28 @@ var NativeCropResultSchema = object({
|
|
|
11003
11201
|
width: number().int().positive(),
|
|
11004
11202
|
height: number().int().positive()
|
|
11005
11203
|
});
|
|
11204
|
+
/** Parent detection context passed to `runDetailSubtree` — the crop's
|
|
11205
|
+
* originating detection, in FRAME-space coordinates. Reuses
|
|
11206
|
+
* `NativeCropBboxSchema`'s `{x,y,w,h}` shape (same numeric fields; here
|
|
11207
|
+
* the coordinates are frame-space rather than getNativeCrop's
|
|
11208
|
+
* normalized [0,1] convention). */
|
|
11209
|
+
var DetailParentSchema = object({
|
|
11210
|
+
bbox: NativeCropBboxSchema,
|
|
11211
|
+
className: string()
|
|
11212
|
+
});
|
|
11213
|
+
/** One child-step result from `runDetailSubtree` — an embedding, label,
|
|
11214
|
+
* or refined detection produced by running the crop-subtree on a
|
|
11215
|
+
* single tracked detection. */
|
|
11216
|
+
var DetailResultSchema = object({
|
|
11217
|
+
stepId: string(),
|
|
11218
|
+
className: string(),
|
|
11219
|
+
score: number(),
|
|
11220
|
+
/** FRAME-space bbox (already mapped back from crop space). */
|
|
11221
|
+
bbox: NativeCropBboxSchema.optional(),
|
|
11222
|
+
embedding: string().optional(),
|
|
11223
|
+
label: string().optional(),
|
|
11224
|
+
alignedCropJpeg: string().optional()
|
|
11225
|
+
});
|
|
11006
11226
|
/**
|
|
11007
11227
|
* Per-camera tunable ranges + defaults. Single source of truth used
|
|
11008
11228
|
* by both the Zod data schema (validation + default fallback) and
|
|
@@ -11263,7 +11483,13 @@ method(RunnerCameraConfigSchema, object({ success: literal(true) }), { kind: "mu
|
|
|
11263
11483
|
handle: FrameHandleSchema,
|
|
11264
11484
|
bbox: NativeCropBboxSchema,
|
|
11265
11485
|
maxWidth: number().int().positive().optional()
|
|
11266
|
-
}), NativeCropResultSchema.nullable())
|
|
11486
|
+
}), NativeCropResultSchema.nullable()), method(object({
|
|
11487
|
+
deviceId: number(),
|
|
11488
|
+
frameHandle: FrameHandleSchema.optional(),
|
|
11489
|
+
cropJpeg: string().optional(),
|
|
11490
|
+
parent: DetailParentSchema,
|
|
11491
|
+
steps: array(string()).optional()
|
|
11492
|
+
}), object({ details: array(DetailResultSchema) }).nullable(), { kind: "mutation" });
|
|
11267
11493
|
object({
|
|
11268
11494
|
detected: boolean(),
|
|
11269
11495
|
/** Ms epoch of the last detected-true observation. Null if never detected. */
|
|
@@ -11875,6 +12101,140 @@ DeviceType.Camera, method(object({ deviceId: number() }), PtzAutotrackStatusSche
|
|
|
11875
12101
|
deviceId: number(),
|
|
11876
12102
|
status: PtzAutotrackStatusSchema
|
|
11877
12103
|
});
|
|
12104
|
+
/**
|
|
12105
|
+
* scene-monitor — device-scoped reference-region state cap. An operator marks
|
|
12106
|
+
* a rect ROI on a camera frame and names one or more states; the engine
|
|
12107
|
+
* (pipeline-analytics, designated post-processing node) reports which state is
|
|
12108
|
+
* active on an ongoing basis. Two operator-selectable check modes share every-
|
|
12109
|
+
* thing except the comparator: `similarity` (CLIP cosine at the same ROI coords
|
|
12110
|
+
* vs condition-tagged references) and `llm` (vision-LLM judgment over the crop).
|
|
12111
|
+
*
|
|
12112
|
+
* D14 device-config archetype (`deviceConfig.ui.kind:'widget'`) — the framework
|
|
12113
|
+
* derives the device-detail contribution; the provider carries NO hand-written
|
|
12114
|
+
* settings-contribution methods. `status.kind:'push'` — the engine pushes on
|
|
12115
|
+
* every hysteresis flip / availability change; consumers never poll.
|
|
12116
|
+
*/
|
|
12117
|
+
/** Extensible condition tag. Seeded 'day' | 'night'; open by design so more can
|
|
12118
|
+
* be added without a wire break (matching falls back to any-condition refs). */
|
|
12119
|
+
var SceneConditionSchema = string();
|
|
12120
|
+
/** One captured reference — condition-tagged, model-version-gated. `embedding`
|
|
12121
|
+
* is `number[]` (Float32Array does NOT survive MsgPack/UDS). */
|
|
12122
|
+
var SceneReferenceSchema = object({
|
|
12123
|
+
embedding: array(number()),
|
|
12124
|
+
modelId: string(),
|
|
12125
|
+
condition: SceneConditionSchema,
|
|
12126
|
+
capturedAt: number(),
|
|
12127
|
+
thumbnailMediaId: string().optional()
|
|
12128
|
+
});
|
|
12129
|
+
var SceneMonitorStateSchema = object({
|
|
12130
|
+
id: string(),
|
|
12131
|
+
label: string(),
|
|
12132
|
+
references: array(SceneReferenceSchema),
|
|
12133
|
+
textPrompts: array(string()).optional(),
|
|
12134
|
+
referenceCount: number(),
|
|
12135
|
+
currentlyMatched: boolean()
|
|
12136
|
+
});
|
|
12137
|
+
/** Per-mode comparator params in a discriminated union so an `llm` scene never
|
|
12138
|
+
* carries similarity knobs and vice-versa. */
|
|
12139
|
+
var SceneCheckSchema = discriminatedUnion("mode", [object({
|
|
12140
|
+
mode: literal("similarity"),
|
|
12141
|
+
threshold: number().min(0).max(1),
|
|
12142
|
+
hysteresisCount: number().int().positive()
|
|
12143
|
+
}), object({
|
|
12144
|
+
mode: literal("llm"),
|
|
12145
|
+
prompt: string(),
|
|
12146
|
+
profileId: string().optional(),
|
|
12147
|
+
hysteresisCount: number().int().positive()
|
|
12148
|
+
})]);
|
|
12149
|
+
var SceneMonitorSchema = object({
|
|
12150
|
+
id: string(),
|
|
12151
|
+
label: string(),
|
|
12152
|
+
roi: MaskRectShapeSchema,
|
|
12153
|
+
enabled: boolean(),
|
|
12154
|
+
triggerMode: _enum([
|
|
12155
|
+
"periodic",
|
|
12156
|
+
"on-motion",
|
|
12157
|
+
"both"
|
|
12158
|
+
]),
|
|
12159
|
+
checkIntervalSec: number().optional(),
|
|
12160
|
+
check: SceneCheckSchema,
|
|
12161
|
+
states: array(SceneMonitorStateSchema),
|
|
12162
|
+
currentStateId: string().nullable(),
|
|
12163
|
+
lastCheckedAt: number().nullable(),
|
|
12164
|
+
lastConfidence: number().nullable(),
|
|
12165
|
+
currentCondition: SceneConditionSchema.nullable(),
|
|
12166
|
+
availability: _enum(["ok", "unavailable"]),
|
|
12167
|
+
unavailableReason: string().nullable()
|
|
12168
|
+
});
|
|
12169
|
+
var SceneMonitorStatusSchema = object({
|
|
12170
|
+
monitors: array(SceneMonitorSchema),
|
|
12171
|
+
lastFetchedAt: number()
|
|
12172
|
+
});
|
|
12173
|
+
DeviceType.Camera, method(object({ deviceId: number() }), SceneMonitorStatusSchema), method(object({
|
|
12174
|
+
deviceId: number(),
|
|
12175
|
+
label: string(),
|
|
12176
|
+
roi: MaskRectShapeSchema,
|
|
12177
|
+
check: SceneCheckSchema,
|
|
12178
|
+
triggerMode: _enum([
|
|
12179
|
+
"periodic",
|
|
12180
|
+
"on-motion",
|
|
12181
|
+
"both"
|
|
12182
|
+
]).optional(),
|
|
12183
|
+
checkIntervalSec: number().optional()
|
|
12184
|
+
}), SceneMonitorSchema, {
|
|
12185
|
+
kind: "mutation",
|
|
12186
|
+
auth: "admin"
|
|
12187
|
+
}), method(object({
|
|
12188
|
+
deviceId: number(),
|
|
12189
|
+
monitorId: string(),
|
|
12190
|
+
patch: object({
|
|
12191
|
+
label: string().optional(),
|
|
12192
|
+
roi: MaskRectShapeSchema.optional(),
|
|
12193
|
+
enabled: boolean().optional(),
|
|
12194
|
+
triggerMode: _enum([
|
|
12195
|
+
"periodic",
|
|
12196
|
+
"on-motion",
|
|
12197
|
+
"both"
|
|
12198
|
+
]).optional(),
|
|
12199
|
+
checkIntervalSec: number().optional(),
|
|
12200
|
+
check: SceneCheckSchema.optional()
|
|
12201
|
+
})
|
|
12202
|
+
}), _void(), {
|
|
12203
|
+
kind: "mutation",
|
|
12204
|
+
auth: "admin"
|
|
12205
|
+
}), method(object({
|
|
12206
|
+
deviceId: number(),
|
|
12207
|
+
monitorId: string()
|
|
12208
|
+
}), _void(), {
|
|
12209
|
+
kind: "mutation",
|
|
12210
|
+
auth: "admin"
|
|
12211
|
+
}), method(object({
|
|
12212
|
+
deviceId: number(),
|
|
12213
|
+
monitorId: string(),
|
|
12214
|
+
stateId: string().optional(),
|
|
12215
|
+
label: string().optional(),
|
|
12216
|
+
condition: SceneConditionSchema.optional()
|
|
12217
|
+
}), object({
|
|
12218
|
+
stateId: string(),
|
|
12219
|
+
referenceCount: number()
|
|
12220
|
+
}), {
|
|
12221
|
+
kind: "mutation",
|
|
12222
|
+
auth: "admin"
|
|
12223
|
+
}), method(object({
|
|
12224
|
+
deviceId: number(),
|
|
12225
|
+
monitorId: string(),
|
|
12226
|
+
stateId: string(),
|
|
12227
|
+
index: number()
|
|
12228
|
+
}), _void(), {
|
|
12229
|
+
kind: "mutation",
|
|
12230
|
+
auth: "admin"
|
|
12231
|
+
}), method(object({
|
|
12232
|
+
deviceId: number(),
|
|
12233
|
+
monitorId: string()
|
|
12234
|
+
}), _void(), {
|
|
12235
|
+
kind: "mutation",
|
|
12236
|
+
auth: "admin"
|
|
12237
|
+
});
|
|
11878
12238
|
object({
|
|
11879
12239
|
/** Whether the script is currently executing. */
|
|
11880
12240
|
isRunning: boolean(),
|
|
@@ -12282,6 +12642,36 @@ var ZoneScopeBreakdownSchema = PerScopeBreakdownSchema.extend({
|
|
|
12282
12642
|
* with the per-track detail panel and live overlay. */
|
|
12283
12643
|
trackIds: array(string()).readonly()
|
|
12284
12644
|
});
|
|
12645
|
+
/**
|
|
12646
|
+
* A parked ("stationary") object surfaced alongside occupancy — an object that
|
|
12647
|
+
* settled and stopped moving. It is NO LONGER a tracked object (the tracker was
|
|
12648
|
+
* told to forget it so it stops re-spawning tracks/events), but it IS still
|
|
12649
|
+
* physically present, so it keeps counting toward `frame` occupancy and is
|
|
12650
|
+
* listed here so the UI can show it in a dedicated "Stationary" section instead
|
|
12651
|
+
* of flooding the live event feed.
|
|
12652
|
+
*/
|
|
12653
|
+
var StationaryObjectSchema = object({
|
|
12654
|
+
id: string(),
|
|
12655
|
+
className: string(),
|
|
12656
|
+
bbox: object({
|
|
12657
|
+
x: number(),
|
|
12658
|
+
y: number(),
|
|
12659
|
+
w: number(),
|
|
12660
|
+
h: number()
|
|
12661
|
+
}),
|
|
12662
|
+
frameWidth: number().int().nonnegative(),
|
|
12663
|
+
frameHeight: number().int().nonnegative(),
|
|
12664
|
+
/** When the source track was first seen. */
|
|
12665
|
+
firstSeenAt: number().int(),
|
|
12666
|
+
/** When the object was recognised as parked (promotion time). */
|
|
12667
|
+
becameStationaryAt: number().int(),
|
|
12668
|
+
/** Last frame a detection confirmed the object is still there. */
|
|
12669
|
+
lastConfirmedAt: number().int(),
|
|
12670
|
+
/** Enrichment label carried from the source track (identity / plate). */
|
|
12671
|
+
label: string().optional(),
|
|
12672
|
+
/** Native-resolution key-frame media key for the parked object's best image. */
|
|
12673
|
+
keyFrameMediaKey: string().optional()
|
|
12674
|
+
});
|
|
12285
12675
|
var CameraOccupancySnapshotSchema = object({
|
|
12286
12676
|
/** Frame timestamp of the inference result that produced this snapshot. */
|
|
12287
12677
|
ts: number().int(),
|
|
@@ -12290,10 +12680,15 @@ var CameraOccupancySnapshotSchema = object({
|
|
|
12290
12680
|
frameHeight: number().int().nonnegative(),
|
|
12291
12681
|
/** Per-zone breakdown — one entry per defined zone (user + onboard). */
|
|
12292
12682
|
zones: array(ZoneScopeBreakdownSchema).readonly(),
|
|
12293
|
-
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12683
|
+
/** Frame-wide aggregate (everywhere, regardless of zone membership).
|
|
12684
|
+
* INCLUDES currently-confirmed stationary objects (they are still present). */
|
|
12294
12685
|
frame: PerScopeBreakdownSchema,
|
|
12295
12686
|
/** Detections that landed outside every zone. Empty when no zones defined. */
|
|
12296
|
-
unzoned: PerScopeBreakdownSchema
|
|
12687
|
+
unzoned: PerScopeBreakdownSchema,
|
|
12688
|
+
/** Parked objects on this camera (additive — absent on legacy snapshots).
|
|
12689
|
+
* Surfaced separately so the UI shows them in a dedicated section rather
|
|
12690
|
+
* than as repeated tracks/events. */
|
|
12691
|
+
stationaryObjects: array(StationaryObjectSchema).readonly().optional()
|
|
12297
12692
|
});
|
|
12298
12693
|
/**
|
|
12299
12694
|
* Time-series resolution. The history methods return one bucket per
|
|
@@ -12663,7 +13058,26 @@ var InvokeReplyEnvelopeSchema = object({
|
|
|
12663
13058
|
/** Set when the handler called `reply.type(mime)`. */
|
|
12664
13059
|
contentType: string().optional()
|
|
12665
13060
|
});
|
|
12666
|
-
|
|
13061
|
+
var addonRoutesCapability = {
|
|
13062
|
+
name: "addon-routes",
|
|
13063
|
+
scope: "system",
|
|
13064
|
+
mode: "collection",
|
|
13065
|
+
internal: true,
|
|
13066
|
+
methods: {
|
|
13067
|
+
getRoutes: method(_void(), array(AddonHttpRouteSchema)),
|
|
13068
|
+
/**
|
|
13069
|
+
* Cross-process dispatch entry point. Forked addons implement this
|
|
13070
|
+
* (via `buildAddonRouteProvider`) so the hub's Fastify catch-all
|
|
13071
|
+
* can route through Moleculer when the handler lives in a worker.
|
|
13072
|
+
*
|
|
13073
|
+
* Local addons can implement it for free with the same helper;
|
|
13074
|
+
* the hub bypasses the wire on co-located addons.
|
|
13075
|
+
*/
|
|
13076
|
+
invoke: method(InvokeRequestSchema, InvokeReplyEnvelopeSchema, { kind: "mutation" })
|
|
13077
|
+
},
|
|
13078
|
+
/** BIG PLAN 2: declarative mount hint — read by `@camstack/system` `buildCapRouters`. */
|
|
13079
|
+
mount: { kind: "skip" }
|
|
13080
|
+
};
|
|
12667
13081
|
var ConfigTabDeclarationSchema = object({
|
|
12668
13082
|
id: string(),
|
|
12669
13083
|
label: string(),
|
|
@@ -12896,7 +13310,12 @@ var NotificationRuleConditionsSchema = object({
|
|
|
12896
13310
|
clipDescription: object({
|
|
12897
13311
|
text: string().min(1),
|
|
12898
13312
|
minSimilarity: number().min(0).max(1)
|
|
12899
|
-
}).optional()
|
|
13313
|
+
}).optional(),
|
|
13314
|
+
/** Match events whose recognized-entity label (face identity name or plate
|
|
13315
|
+
* vehicle name, propagated onto `event.data.label`) is one of these values.
|
|
13316
|
+
* Empty/absent → unaffected (back-compat). Enables "notify me when <named
|
|
13317
|
+
* vehicle/person> is seen". */
|
|
13318
|
+
labels: array(string()).readonly().optional()
|
|
12900
13319
|
});
|
|
12901
13320
|
var NotificationRuleTemplateSchema = object({
|
|
12902
13321
|
title: string(),
|
|
@@ -13154,11 +13573,19 @@ method(object({
|
|
|
13154
13573
|
* login page needs NO change.
|
|
13155
13574
|
*
|
|
13156
13575
|
* - `widget` — a Module-Federation widget the login page mounts (via
|
|
13157
|
-
* `loadRemoteBundle`) for an in-page ceremony.
|
|
13158
|
-
*
|
|
13159
|
-
*
|
|
13160
|
-
*
|
|
13161
|
-
*
|
|
13576
|
+
* `loadRemoteBundle`) for an in-page ceremony. `auth.listLoginMethods`
|
|
13577
|
+
* stamps a public `bundleUrl` from `addonId` + `bundle`. Generic
|
|
13578
|
+
* mechanism kept for future use; no shipped addon uses it on the login
|
|
13579
|
+
* page (the passkey ceremony below runs natively in the shell instead).
|
|
13580
|
+
*
|
|
13581
|
+
* - `passkey` — a declarative WebAuthn ceremony the shell renders
|
|
13582
|
+
* natively (`@simplewebauthn/browser` lives in `addon-admin-ui`, not in
|
|
13583
|
+
* a remotely-loaded bundle). Carries the addon's effective `rpId` /
|
|
13584
|
+
* `origin` (from its `resolveRpID()` / `resolveOrigin()`) so the shell
|
|
13585
|
+
* can gate visibility (IP-literal origin, hostname/rpId mismatch) WITHOUT
|
|
13586
|
+
* fetching any remote code pre-auth. Contribution stays unconditional —
|
|
13587
|
+
* enrollment state is never leaked pre-auth; visibility is a shell
|
|
13588
|
+
* decision.
|
|
13162
13589
|
*
|
|
13163
13590
|
* Every contribution carries a `stage`:
|
|
13164
13591
|
* - `primary` — shown on the first credentials screen (OIDC /
|
|
@@ -13172,30 +13599,45 @@ method(object({
|
|
|
13172
13599
|
*/
|
|
13173
13600
|
/** When a login method renders in the two-phase login flow. */
|
|
13174
13601
|
var LoginStageEnum = _enum(["primary", "second-factor"]);
|
|
13175
|
-
/** One login-method contribution — redirect button
|
|
13176
|
-
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13177
|
-
|
|
13178
|
-
|
|
13179
|
-
|
|
13180
|
-
|
|
13181
|
-
|
|
13182
|
-
|
|
13183
|
-
|
|
13184
|
-
|
|
13185
|
-
|
|
13186
|
-
|
|
13187
|
-
|
|
13188
|
-
|
|
13189
|
-
|
|
13190
|
-
|
|
13191
|
-
|
|
13192
|
-
|
|
13193
|
-
|
|
13194
|
-
|
|
13195
|
-
|
|
13196
|
-
|
|
13197
|
-
|
|
13198
|
-
|
|
13602
|
+
/** One login-method contribution — redirect button, pre-auth widget, or native passkey ceremony. */
|
|
13603
|
+
var LoginMethodContributionSchema = discriminatedUnion("kind", [
|
|
13604
|
+
object({
|
|
13605
|
+
kind: literal("redirect"),
|
|
13606
|
+
/** Stable id within the login-method set (e.g. `auth-oidc/google`). */
|
|
13607
|
+
id: string(),
|
|
13608
|
+
/** Operator-facing button label. */
|
|
13609
|
+
label: string(),
|
|
13610
|
+
/** lucide-react icon name. */
|
|
13611
|
+
icon: string().optional(),
|
|
13612
|
+
/** Addon-owned HTTP route the button navigates to (GET). */
|
|
13613
|
+
startUrl: string(),
|
|
13614
|
+
stage: LoginStageEnum
|
|
13615
|
+
}),
|
|
13616
|
+
object({
|
|
13617
|
+
kind: literal("widget"),
|
|
13618
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-login`). */
|
|
13619
|
+
id: string(),
|
|
13620
|
+
/** Owning addon id — drives the public bundle URL + the MF namespace. */
|
|
13621
|
+
addonId: string(),
|
|
13622
|
+
/** Bundle filename inside the addon's dist dir (`remoteEntry.js`). */
|
|
13623
|
+
bundle: string(),
|
|
13624
|
+
/** MF remote descriptor — `{ remoteName, exposedModule, componentKey }`. */
|
|
13625
|
+
remote: WidgetRemoteSchema,
|
|
13626
|
+
stage: LoginStageEnum
|
|
13627
|
+
}),
|
|
13628
|
+
object({
|
|
13629
|
+
kind: literal("passkey"),
|
|
13630
|
+
/** Stable id within the login-method set (e.g. `auth-webauthn/passkey-direct-login`). */
|
|
13631
|
+
id: string(),
|
|
13632
|
+
/** Operator-facing button label. */
|
|
13633
|
+
label: string(),
|
|
13634
|
+
stage: LoginStageEnum,
|
|
13635
|
+
/** Effective WebAuthn RP ID (`resolveRpID()`) — the shell gates visibility on it. */
|
|
13636
|
+
rpId: string(),
|
|
13637
|
+
/** Effective expected origin (`resolveOrigin()`), null when unconfigured. */
|
|
13638
|
+
origin: string().nullable()
|
|
13639
|
+
})
|
|
13640
|
+
]);
|
|
13199
13641
|
method(_void(), array(LoginMethodContributionSchema).readonly());
|
|
13200
13642
|
/**
|
|
13201
13643
|
* Orchestrator-side destination metadata. The orchestrator computes
|
|
@@ -15023,14 +15465,14 @@ var TargetKindCapsSchema = object({
|
|
|
15023
15465
|
* the union is large and not meant for runtime validation here; the exported
|
|
15024
15466
|
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
15025
15467
|
*/
|
|
15026
|
-
var ConfigSchemaPassthrough = unknown();
|
|
15468
|
+
var ConfigSchemaPassthrough$1 = unknown();
|
|
15027
15469
|
var TargetKindSchema = object({
|
|
15028
15470
|
kind: string(),
|
|
15029
15471
|
label: string(),
|
|
15030
15472
|
icon: string(),
|
|
15031
15473
|
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
15032
15474
|
addonId: string(),
|
|
15033
|
-
configSchema: ConfigSchemaPassthrough,
|
|
15475
|
+
configSchema: ConfigSchemaPassthrough$1,
|
|
15034
15476
|
supportsDiscovery: boolean(),
|
|
15035
15477
|
caps: TargetKindCapsSchema
|
|
15036
15478
|
});
|
|
@@ -15097,101 +15539,393 @@ var notificationOutputCapability = {
|
|
|
15097
15539
|
}
|
|
15098
15540
|
};
|
|
15099
15541
|
/**
|
|
15100
|
-
*
|
|
15101
|
-
*
|
|
15102
|
-
*
|
|
15103
|
-
* stored in the settings-store backend and retrieved via tRPC. They
|
|
15104
|
-
* replace the manual TypeScript interfaces with schema-derived types,
|
|
15105
|
-
* enabling runtime validation when reading from the backend.
|
|
15542
|
+
* Shared LLM generate contracts — imported by BOTH `llm.cap.ts` (consumer
|
|
15543
|
+
* surface) and `llm-runtime.cap.ts` (node-side managed executor) so the two
|
|
15544
|
+
* caps stay wire-compatible without a circular cap→cap import.
|
|
15106
15545
|
*
|
|
15107
|
-
*
|
|
15108
|
-
*
|
|
15546
|
+
* Errors are a discriminated-union RESULT, never thrown: the shape survives
|
|
15547
|
+
* every transport tier structurally, and failed calls still write usage rows.
|
|
15548
|
+
* Token counts only in v1 — no costUsd (operator decision, 2026-07-15).
|
|
15109
15549
|
*/
|
|
15550
|
+
var LlmUsageSchema = object({
|
|
15551
|
+
inputTokens: number(),
|
|
15552
|
+
outputTokens: number()
|
|
15553
|
+
});
|
|
15554
|
+
var LlmErrorCodeSchema = _enum([
|
|
15555
|
+
"timeout",
|
|
15556
|
+
"rate-limited",
|
|
15557
|
+
"auth",
|
|
15558
|
+
"refusal",
|
|
15559
|
+
"bad-request",
|
|
15560
|
+
"unavailable",
|
|
15561
|
+
"no-profile",
|
|
15562
|
+
"budget-exceeded",
|
|
15563
|
+
"adapter-error"
|
|
15564
|
+
]);
|
|
15565
|
+
var LlmGenerateResultSchema = discriminatedUnion("ok", [object({
|
|
15566
|
+
ok: literal(true),
|
|
15567
|
+
text: string(),
|
|
15568
|
+
model: string(),
|
|
15569
|
+
usage: LlmUsageSchema,
|
|
15570
|
+
truncated: boolean(),
|
|
15571
|
+
latencyMs: number()
|
|
15572
|
+
}), object({
|
|
15573
|
+
ok: literal(false),
|
|
15574
|
+
code: LlmErrorCodeSchema,
|
|
15575
|
+
message: string(),
|
|
15576
|
+
retryAfterMs: number().optional()
|
|
15577
|
+
})]);
|
|
15110
15578
|
/**
|
|
15111
|
-
*
|
|
15112
|
-
*
|
|
15113
|
-
*
|
|
15114
|
-
* - `isAdmin: true` — full bypass. Admin scope set is ignored.
|
|
15115
|
-
* - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
|
|
15116
|
-
*
|
|
15117
|
-
* No role enum. There used to be `viewer / agent / scoped` and earlier
|
|
15118
|
-
* `super_admin` — collapsed onto the single boolean because every
|
|
15119
|
-
* non-admin caller is governed by the same scope-check rules; the
|
|
15120
|
-
* distinction was only ever used to route role-specific procedures
|
|
15121
|
-
* which we no longer have. Agents are admin sessions issued via
|
|
15122
|
-
* `createServiceToken`; CLI / webhook integrations are `cst_*` tokens
|
|
15123
|
-
* with `isAdmin: false` + an explicit scope list.
|
|
15579
|
+
* `Uint8Array` is the sanctioned binary convention — superjson + the UDS
|
|
15580
|
+
* MsgPack channel round-trip typed arrays (embedding-encoder.cap.ts:29,
|
|
15581
|
+
* notification-output.cap.ts:27-31 precedents).
|
|
15124
15582
|
*/
|
|
15125
|
-
|
|
15126
|
-
|
|
15127
|
-
|
|
15128
|
-
|
|
15129
|
-
|
|
15130
|
-
|
|
15583
|
+
var LlmImageSchema = object({
|
|
15584
|
+
bytes: _instanceof(Uint8Array),
|
|
15585
|
+
mimeType: string()
|
|
15586
|
+
});
|
|
15587
|
+
var LlmGenerateBaseInputSchema = object({
|
|
15588
|
+
/** Collection routing (the notification-output posture). */
|
|
15589
|
+
addonId: string().optional(),
|
|
15590
|
+
/** Explicit profile; else the resolution chain (spec §3). */
|
|
15591
|
+
profileId: string().optional(),
|
|
15592
|
+
/** MANDATORY usage tag: 'ai-summary', 'notifier-rules', 'adhoc-ui', … */
|
|
15593
|
+
consumer: string(),
|
|
15594
|
+
system: string().optional(),
|
|
15595
|
+
/** v1: single-turn. `messages[]` is a v2 additive field. */
|
|
15596
|
+
prompt: string(),
|
|
15597
|
+
/** Structured output — adapter-mapped (response_format / forced tool / responseSchema). */
|
|
15598
|
+
jsonSchema: record(string(), unknown()).optional(),
|
|
15599
|
+
/** Per-call override of the profile default. */
|
|
15600
|
+
maxTokens: number().int().positive().optional(),
|
|
15601
|
+
temperature: number().optional()
|
|
15602
|
+
});
|
|
15131
15603
|
/**
|
|
15132
|
-
*
|
|
15133
|
-
*
|
|
15134
|
-
*
|
|
15135
|
-
*
|
|
15136
|
-
*
|
|
15137
|
-
* automatically — adding a device cap to the codebase makes it
|
|
15138
|
-
* reachable to every user with `category:device [...]` without a
|
|
15139
|
-
* re-grant. Use this for "family viewer" / "operator" tiers.
|
|
15140
|
-
*
|
|
15141
|
-
* - `addon` — every cap that ships inside a given addon id. Niche;
|
|
15142
|
-
* useful when an integration needs to interact with one specific
|
|
15143
|
-
* addon's surface regardless of how many caps it exports.
|
|
15144
|
-
*
|
|
15145
|
-
* - `capability` — the most precise form. Single cap name. Use for
|
|
15146
|
-
* integration tokens where you want to grant `addons.installPackage`
|
|
15147
|
-
* and nothing else.
|
|
15604
|
+
* `llm-runtime` — node-side managed llama.cpp executor (spec §4). Registered
|
|
15605
|
+
* on EVERY node where `addon-ai` is installed; the hub `llm` provider reaches
|
|
15606
|
+
* a specific node's runtime with `nodePin(profile.runtime.nodeId)` — normal
|
|
15607
|
+
* cap routing, zero bespoke plumbing. `internal: true`: the operator reaches
|
|
15608
|
+
* this only through the `llm` cap's methods.
|
|
15148
15609
|
*
|
|
15149
|
-
*
|
|
15150
|
-
*
|
|
15610
|
+
* One running llama-server child per node in v1 (models are RAM-heavy).
|
|
15611
|
+
* Resource ceiling = llama-server flags + idleStopMinutes ONLY (no RSS
|
|
15612
|
+
* watchdog — operator decision #3).
|
|
15151
15613
|
*/
|
|
15152
|
-
var
|
|
15153
|
-
var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
15154
|
-
var CapScopeSchema = _enum(["device", "system"]);
|
|
15155
|
-
var TokenScopeSchema = discriminatedUnion("type", [
|
|
15614
|
+
var ManagedModelRefSchema = discriminatedUnion("kind", [
|
|
15156
15615
|
object({
|
|
15157
|
-
|
|
15158
|
-
|
|
15159
|
-
access: array(MethodAccessSchema).min(1)
|
|
15160
|
-
}),
|
|
15161
|
-
object({
|
|
15162
|
-
type: literal("capability"),
|
|
15163
|
-
target: string(),
|
|
15164
|
-
access: array(MethodAccessSchema).min(1)
|
|
15616
|
+
kind: literal("catalog"),
|
|
15617
|
+
catalogId: string()
|
|
15165
15618
|
}),
|
|
15166
15619
|
object({
|
|
15167
|
-
|
|
15168
|
-
|
|
15169
|
-
|
|
15620
|
+
kind: literal("url"),
|
|
15621
|
+
url: string(),
|
|
15622
|
+
sha256: string().optional()
|
|
15170
15623
|
}),
|
|
15171
15624
|
object({
|
|
15172
|
-
|
|
15173
|
-
|
|
15174
|
-
* One or more deviceIds (serialised as strings for wire-format
|
|
15175
|
-
* consistency with the rest of the union). Matcher accepts if
|
|
15176
|
-
* `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
|
|
15177
|
-
* of one scope-per-device when granting access to a set of cameras.
|
|
15178
|
-
*/
|
|
15179
|
-
targets: array(string()).min(1),
|
|
15180
|
-
access: array(MethodAccessSchema).min(1)
|
|
15625
|
+
kind: literal("path"),
|
|
15626
|
+
path: string()
|
|
15181
15627
|
})
|
|
15182
15628
|
]);
|
|
15183
|
-
object({
|
|
15184
|
-
|
|
15185
|
-
|
|
15186
|
-
|
|
15187
|
-
|
|
15188
|
-
|
|
15189
|
-
|
|
15190
|
-
|
|
15191
|
-
|
|
15192
|
-
|
|
15193
|
-
|
|
15194
|
-
|
|
15629
|
+
var ManagedRuntimeConfigSchema = object({
|
|
15630
|
+
/** WHERE the runtime lives — hub or any agent. */
|
|
15631
|
+
nodeId: string(),
|
|
15632
|
+
/** Closed for v1; 'ollama' is a v2 candidate. */
|
|
15633
|
+
engine: _enum(["llama-cpp"]),
|
|
15634
|
+
model: ManagedModelRefSchema,
|
|
15635
|
+
contextSize: number().int().default(4096),
|
|
15636
|
+
/** 0 = CPU-only. */
|
|
15637
|
+
gpuLayers: number().int().default(0),
|
|
15638
|
+
/** Default: cpus-2, clamped ≥1 (resolved node-side). */
|
|
15639
|
+
threads: number().int().optional(),
|
|
15640
|
+
/** Concurrent slots. */
|
|
15641
|
+
parallel: number().int().default(1),
|
|
15642
|
+
/** Else lazy: first generate boots it. */
|
|
15643
|
+
autoStart: boolean().default(false),
|
|
15644
|
+
/** 0 = never; frees RAM after quiet periods. */
|
|
15645
|
+
idleStopMinutes: number().int().default(30)
|
|
15646
|
+
});
|
|
15647
|
+
var LlmRuntimeStatusSchema = object({
|
|
15648
|
+
/** Status is ALWAYS node-qualified. */
|
|
15649
|
+
nodeId: string(),
|
|
15650
|
+
state: _enum([
|
|
15651
|
+
"stopped",
|
|
15652
|
+
"downloading",
|
|
15653
|
+
"starting",
|
|
15654
|
+
"ready",
|
|
15655
|
+
"crashed",
|
|
15656
|
+
"failed"
|
|
15657
|
+
]),
|
|
15658
|
+
pid: number().optional(),
|
|
15659
|
+
port: number().optional(),
|
|
15660
|
+
modelPath: string().optional(),
|
|
15661
|
+
modelId: string().optional(),
|
|
15662
|
+
downloadProgress: number().min(0).max(1).optional(),
|
|
15663
|
+
lastError: string().optional(),
|
|
15664
|
+
crashesInWindow: number(),
|
|
15665
|
+
/** Child RSS (sampled best-effort). */
|
|
15666
|
+
memoryBytes: number().optional(),
|
|
15667
|
+
vramBytes: number().optional()
|
|
15668
|
+
});
|
|
15669
|
+
var LlmNodeModelSchema = object({
|
|
15670
|
+
file: string(),
|
|
15671
|
+
sizeBytes: number(),
|
|
15672
|
+
catalogId: string().optional(),
|
|
15673
|
+
installedAt: number().optional()
|
|
15674
|
+
});
|
|
15675
|
+
var LlmRuntimeDiskUsageSchema = object({
|
|
15676
|
+
nodeId: string(),
|
|
15677
|
+
modelsBytes: number(),
|
|
15678
|
+
freeBytes: number().optional()
|
|
15679
|
+
});
|
|
15680
|
+
method(LlmGenerateBaseInputSchema.extend({
|
|
15681
|
+
images: array(LlmImageSchema).optional(),
|
|
15682
|
+
runtime: ManagedRuntimeConfigSchema,
|
|
15683
|
+
/** The managed profile's timeout, threaded by the hub provider. */
|
|
15684
|
+
timeoutMs: number().int().positive().optional()
|
|
15685
|
+
}), LlmGenerateResultSchema, { kind: "mutation" }), method(object({ runtime: ManagedRuntimeConfigSchema }), LlmRuntimeStatusSchema, {
|
|
15686
|
+
kind: "mutation",
|
|
15687
|
+
auth: "admin"
|
|
15688
|
+
}), method(object({}), _void(), {
|
|
15689
|
+
kind: "mutation",
|
|
15690
|
+
auth: "admin"
|
|
15691
|
+
}), method(object({}), LlmRuntimeStatusSchema), method(object({ model: ManagedModelRefSchema }), _void(), {
|
|
15692
|
+
kind: "mutation",
|
|
15693
|
+
auth: "admin"
|
|
15694
|
+
}), method(object({ file: string() }), _void(), {
|
|
15695
|
+
kind: "mutation",
|
|
15696
|
+
auth: "admin"
|
|
15697
|
+
}), method(object({}), array(LlmNodeModelSchema)), method(object({}), LlmRuntimeDiskUsageSchema);
|
|
15698
|
+
/**
|
|
15699
|
+
* `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
|
|
15700
|
+
* methods concat-fan across providers; single-row methods route to ONE
|
|
15701
|
+
* provider by the `addonId` in the call input (the notification-output
|
|
15702
|
+
* posture, notification-output.cap.ts:215-250). Provided by `addon-ai`
|
|
15703
|
+
* (hub-placed); the cap stays open for future providers.
|
|
15704
|
+
*
|
|
15705
|
+
* Profiles are ROWS (data), not addons: one row = one usable model endpoint.
|
|
15706
|
+
* `apiKey` is a password field — providers REDACT it on read and merge on
|
|
15707
|
+
* write; a stored key NEVER round-trips to a client.
|
|
15708
|
+
*/
|
|
15709
|
+
var LlmProfileKindSchema = _enum([
|
|
15710
|
+
"openai-compatible",
|
|
15711
|
+
"openai",
|
|
15712
|
+
"anthropic",
|
|
15713
|
+
"google",
|
|
15714
|
+
"managed-local"
|
|
15715
|
+
]);
|
|
15716
|
+
var LlmProfileSchema = object({
|
|
15717
|
+
id: string(),
|
|
15718
|
+
name: string(),
|
|
15719
|
+
kind: LlmProfileKindSchema,
|
|
15720
|
+
/** Stamped by the provider — keeps the fanned catalog routable. */
|
|
15721
|
+
addonId: string(),
|
|
15722
|
+
enabled: boolean(),
|
|
15723
|
+
/** Vendor model id, or the managed runtime's loaded model. */
|
|
15724
|
+
model: string(),
|
|
15725
|
+
/** Required for openai-compatible; override for cloud kinds. */
|
|
15726
|
+
baseUrl: string().optional(),
|
|
15727
|
+
/** ConfigUISchema type:'password' — never round-trips (spec §5). */
|
|
15728
|
+
apiKey: string().optional(),
|
|
15729
|
+
supportsVision: boolean(),
|
|
15730
|
+
temperature: number().min(0).max(2).optional(),
|
|
15731
|
+
maxTokens: number().int().positive().optional(),
|
|
15732
|
+
timeoutMs: number().int().positive().default(6e4),
|
|
15733
|
+
extraHeaders: record(string(), string()).optional(),
|
|
15734
|
+
/** kind === 'managed-local' only (spec §4). */
|
|
15735
|
+
runtime: ManagedRuntimeConfigSchema.optional()
|
|
15736
|
+
});
|
|
15737
|
+
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
15738
|
+
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
15739
|
+
* notification-output.cap.ts:151); the exported TS type re-tightens it. */
|
|
15740
|
+
var ConfigSchemaPassthrough = unknown();
|
|
15741
|
+
var LlmProfileKindDescriptorSchema = object({
|
|
15742
|
+
kind: LlmProfileKindSchema,
|
|
15743
|
+
label: string(),
|
|
15744
|
+
icon: string(),
|
|
15745
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
15746
|
+
addonId: string(),
|
|
15747
|
+
configSchema: ConfigSchemaPassthrough
|
|
15748
|
+
});
|
|
15749
|
+
var LlmDefaultSelectorSchema = union([object({ consumer: string() }), object({ purpose: _enum(["text", "vision"]) })]);
|
|
15750
|
+
var LlmDefaultSchema = object({
|
|
15751
|
+
selector: LlmDefaultSelectorSchema,
|
|
15752
|
+
profileId: string()
|
|
15753
|
+
});
|
|
15754
|
+
/** Server-side rollup row — getUsage never dumps raw call rows (spec §6). */
|
|
15755
|
+
var LlmUsageRollupSchema = object({
|
|
15756
|
+
day: string(),
|
|
15757
|
+
consumer: string(),
|
|
15758
|
+
profileId: string(),
|
|
15759
|
+
calls: number(),
|
|
15760
|
+
okCalls: number(),
|
|
15761
|
+
errorCalls: number(),
|
|
15762
|
+
inputTokens: number(),
|
|
15763
|
+
outputTokens: number(),
|
|
15764
|
+
avgLatencyMs: number()
|
|
15765
|
+
});
|
|
15766
|
+
/** LLM-facing view over the reused ModelCatalogEntry mechanism (spec §4.2). */
|
|
15767
|
+
var ManagedModelCatalogEntrySchema = object({
|
|
15768
|
+
id: string(),
|
|
15769
|
+
label: string(),
|
|
15770
|
+
family: string(),
|
|
15771
|
+
purpose: _enum(["text", "vision"]),
|
|
15772
|
+
url: string(),
|
|
15773
|
+
sha256: string(),
|
|
15774
|
+
sizeBytes: number(),
|
|
15775
|
+
quantization: string(),
|
|
15776
|
+
/** Load-time guidance shown in the picker. */
|
|
15777
|
+
minRamBytes: number(),
|
|
15778
|
+
contextSizeDefault: number().int(),
|
|
15779
|
+
/** Vision models: companion projector file. */
|
|
15780
|
+
mmprojUrl: string().optional()
|
|
15781
|
+
});
|
|
15782
|
+
var LlmRuntimeNodeSchema = object({
|
|
15783
|
+
nodeId: string(),
|
|
15784
|
+
reachable: boolean(),
|
|
15785
|
+
status: LlmRuntimeStatusSchema.optional(),
|
|
15786
|
+
disk: LlmRuntimeDiskUsageSchema.optional(),
|
|
15787
|
+
error: string().optional()
|
|
15788
|
+
});
|
|
15789
|
+
var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
|
|
15790
|
+
var ProfileRefInputSchema = object({
|
|
15791
|
+
addonId: string(),
|
|
15792
|
+
profileId: string()
|
|
15793
|
+
});
|
|
15794
|
+
method(LlmGenerateBaseInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(GenerateVisionInputSchema, LlmGenerateResultSchema, { kind: "mutation" }), method(object({}), array(LlmProfileKindDescriptorSchema)), method(object({}), array(LlmProfileSchema)), method(object({ profile: LlmProfileSchema }), LlmProfileSchema, {
|
|
15795
|
+
kind: "mutation",
|
|
15796
|
+
auth: "admin"
|
|
15797
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15798
|
+
kind: "mutation",
|
|
15799
|
+
auth: "admin"
|
|
15800
|
+
}), method(ProfileRefInputSchema, LlmGenerateResultSchema, {
|
|
15801
|
+
kind: "mutation",
|
|
15802
|
+
auth: "admin"
|
|
15803
|
+
}), method(ProfileRefInputSchema, array(string())), method(object({}), array(LlmDefaultSchema)), method(object({
|
|
15804
|
+
selector: LlmDefaultSelectorSchema,
|
|
15805
|
+
profileId: string().nullable()
|
|
15806
|
+
}), _void(), {
|
|
15807
|
+
kind: "mutation",
|
|
15808
|
+
auth: "admin"
|
|
15809
|
+
}), method(object({
|
|
15810
|
+
since: number().optional(),
|
|
15811
|
+
until: number().optional(),
|
|
15812
|
+
consumer: string().optional(),
|
|
15813
|
+
profileId: string().optional()
|
|
15814
|
+
}), array(LlmUsageRollupSchema)), method(object({}), array(ManagedModelCatalogEntrySchema)), method(object({}), array(LlmRuntimeNodeSchema)), method(object({ nodeId: string() }), array(LlmNodeModelSchema)), method(object({
|
|
15815
|
+
nodeId: string(),
|
|
15816
|
+
model: ManagedModelRefSchema
|
|
15817
|
+
}), _void(), {
|
|
15818
|
+
kind: "mutation",
|
|
15819
|
+
auth: "admin"
|
|
15820
|
+
}), method(object({
|
|
15821
|
+
nodeId: string(),
|
|
15822
|
+
file: string()
|
|
15823
|
+
}), _void(), {
|
|
15824
|
+
kind: "mutation",
|
|
15825
|
+
auth: "admin"
|
|
15826
|
+
}), method(ProfileRefInputSchema, LlmRuntimeStatusSchema), method(ProfileRefInputSchema, LlmRuntimeStatusSchema, {
|
|
15827
|
+
kind: "mutation",
|
|
15828
|
+
auth: "admin"
|
|
15829
|
+
}), method(ProfileRefInputSchema, _void(), {
|
|
15830
|
+
kind: "mutation",
|
|
15831
|
+
auth: "admin"
|
|
15832
|
+
});
|
|
15833
|
+
/**
|
|
15834
|
+
* Zod schemas for persisted record types.
|
|
15835
|
+
*
|
|
15836
|
+
* These schemas serve as the single source of truth for types that are
|
|
15837
|
+
* stored in the settings-store backend and retrieved via tRPC. They
|
|
15838
|
+
* replace the manual TypeScript interfaces with schema-derived types,
|
|
15839
|
+
* enabling runtime validation when reading from the backend.
|
|
15840
|
+
*
|
|
15841
|
+
* Convention: each schema is named `<Type>Schema`, the inferred TS type
|
|
15842
|
+
* keeps the original name for backward compatibility.
|
|
15843
|
+
*/
|
|
15844
|
+
/**
|
|
15845
|
+
* Auth model (v2 — 2026-05-12 caps-only):
|
|
15846
|
+
*
|
|
15847
|
+
* Two principals:
|
|
15848
|
+
* - `isAdmin: true` — full bypass. Admin scope set is ignored.
|
|
15849
|
+
* - `isAdmin: false` — every call gated by `scopes` (see TokenScope).
|
|
15850
|
+
*
|
|
15851
|
+
* No role enum. There used to be `viewer / agent / scoped` and earlier
|
|
15852
|
+
* `super_admin` — collapsed onto the single boolean because every
|
|
15853
|
+
* non-admin caller is governed by the same scope-check rules; the
|
|
15854
|
+
* distinction was only ever used to route role-specific procedures
|
|
15855
|
+
* which we no longer have. Agents are admin sessions issued via
|
|
15856
|
+
* `createServiceToken`; CLI / webhook integrations are `cst_*` tokens
|
|
15857
|
+
* with `isAdmin: false` + an explicit scope list.
|
|
15858
|
+
*/
|
|
15859
|
+
/** Per-method access flavour. Materialised on every cap method via codegen. */
|
|
15860
|
+
var MethodAccessSchema = _enum([
|
|
15861
|
+
"view",
|
|
15862
|
+
"create",
|
|
15863
|
+
"delete"
|
|
15864
|
+
]);
|
|
15865
|
+
/**
|
|
15866
|
+
* A single scope grant. Three forms — checked in matcher with OR
|
|
15867
|
+
* semantics; the first match decides whether the call passes:
|
|
15868
|
+
*
|
|
15869
|
+
* - `category` — broadest. `target: 'device' | 'system'` selects every
|
|
15870
|
+
* cap with the matching `scope` declaration. New caps inherit grants
|
|
15871
|
+
* automatically — adding a device cap to the codebase makes it
|
|
15872
|
+
* reachable to every user with `category:device [...]` without a
|
|
15873
|
+
* re-grant. Use this for "family viewer" / "operator" tiers.
|
|
15874
|
+
*
|
|
15875
|
+
* - `addon` — every cap that ships inside a given addon id. Niche;
|
|
15876
|
+
* useful when an integration needs to interact with one specific
|
|
15877
|
+
* addon's surface regardless of how many caps it exports.
|
|
15878
|
+
*
|
|
15879
|
+
* - `capability` — the most precise form. Single cap name. Use for
|
|
15880
|
+
* integration tokens where you want to grant `addons.installPackage`
|
|
15881
|
+
* and nothing else.
|
|
15882
|
+
*
|
|
15883
|
+
* `access` is REQUIRED with min 1 element. `admin` bypass is the
|
|
15884
|
+
* "give me everything" escape; there is no wildcard scope.
|
|
15885
|
+
*/
|
|
15886
|
+
var AllowedProviderSchema = union([literal("*"), array(string())]);
|
|
15887
|
+
var AllowedDevicesSchema = record(string(), union([literal("*"), array(string())]));
|
|
15888
|
+
var CapScopeSchema = _enum(["device", "system"]);
|
|
15889
|
+
var TokenScopeSchema = discriminatedUnion("type", [
|
|
15890
|
+
object({
|
|
15891
|
+
type: literal("category"),
|
|
15892
|
+
target: CapScopeSchema,
|
|
15893
|
+
access: array(MethodAccessSchema).min(1)
|
|
15894
|
+
}),
|
|
15895
|
+
object({
|
|
15896
|
+
type: literal("capability"),
|
|
15897
|
+
target: string(),
|
|
15898
|
+
access: array(MethodAccessSchema).min(1)
|
|
15899
|
+
}),
|
|
15900
|
+
object({
|
|
15901
|
+
type: literal("addon"),
|
|
15902
|
+
target: string(),
|
|
15903
|
+
access: array(MethodAccessSchema).min(1)
|
|
15904
|
+
}),
|
|
15905
|
+
object({
|
|
15906
|
+
type: literal("device"),
|
|
15907
|
+
/**
|
|
15908
|
+
* One or more deviceIds (serialised as strings for wire-format
|
|
15909
|
+
* consistency with the rest of the union). Matcher accepts if
|
|
15910
|
+
* `input.deviceId` ∈ `targets`. Array shape avoids the row-explosion
|
|
15911
|
+
* of one scope-per-device when granting access to a set of cameras.
|
|
15912
|
+
*/
|
|
15913
|
+
targets: array(string()).min(1),
|
|
15914
|
+
access: array(MethodAccessSchema).min(1)
|
|
15915
|
+
})
|
|
15916
|
+
]);
|
|
15917
|
+
object({
|
|
15918
|
+
id: string(),
|
|
15919
|
+
username: string(),
|
|
15920
|
+
passwordHash: string(),
|
|
15921
|
+
/**
|
|
15922
|
+
* Admin bypass. When true, the middleware skips the scope-access
|
|
15923
|
+
* check entirely. There is no other axis of privilege; the legacy
|
|
15924
|
+
* role enum collapsed onto this boolean in v2.
|
|
15925
|
+
*/
|
|
15926
|
+
isAdmin: boolean().default(false),
|
|
15927
|
+
allowedProviders: AllowedProviderSchema,
|
|
15928
|
+
allowedDevices: AllowedDevicesSchema,
|
|
15195
15929
|
/**
|
|
15196
15930
|
* Scopes granted to this user. Admins bypass; their `scopes` is
|
|
15197
15931
|
* ignored. Non-admins without scopes are locked out of every
|
|
@@ -15298,6 +16032,22 @@ var TrackSnapshotSchema = object({
|
|
|
15298
16032
|
/** MediaStore key; resolve via `getTrackMedia({ trackId })`. */
|
|
15299
16033
|
mediaKey: string()
|
|
15300
16034
|
});
|
|
16035
|
+
/**
|
|
16036
|
+
* One audio-classification label heard on the track's camera while the
|
|
16037
|
+
* track was alive, aggregated per label. An "episode" is one persisted
|
|
16038
|
+
* audio event (the confident-classification path: score ≥ the device's
|
|
16039
|
+
* `classificationMinScore`, class-change-or-heartbeat coalesced) — NOT
|
|
16040
|
+
* one 32 ms inference chunk, so counts stay human-scaled.
|
|
16041
|
+
*/
|
|
16042
|
+
var TrackAudioLabelSchema = object({
|
|
16043
|
+
label: string(),
|
|
16044
|
+
/** Highest classification score observed across the label's episodes. */
|
|
16045
|
+
peakScore: number(),
|
|
16046
|
+
/** Number of coalesced audio-event episodes carrying this label. */
|
|
16047
|
+
count: number(),
|
|
16048
|
+
firstAt: number(),
|
|
16049
|
+
lastAt: number()
|
|
16050
|
+
});
|
|
15301
16051
|
var TrackSchema = object({
|
|
15302
16052
|
trackId: string(),
|
|
15303
16053
|
deviceId: number(),
|
|
@@ -15312,6 +16062,10 @@ var TrackSchema = object({
|
|
|
15312
16062
|
snapshots: array(TrackSnapshotSchema).readonly(),
|
|
15313
16063
|
/** Deduplicated zones the track has entered at least once. */
|
|
15314
16064
|
zonesVisited: array(string()).readonly(),
|
|
16065
|
+
/** Deduplicated set of detector classes observed for this track over its
|
|
16066
|
+
* life (a track may be reclassified, e.g. person→vehicle). Absent on
|
|
16067
|
+
* legacy rows written before class accumulation shipped. */
|
|
16068
|
+
classes: array(string()).readonly().optional(),
|
|
15315
16069
|
/** Cumulative normalized distance travelled (0..1 units = full frame width). */
|
|
15316
16070
|
totalDistance: number(),
|
|
15317
16071
|
state: TrackStateSchema,
|
|
@@ -15325,7 +16079,11 @@ var TrackSchema = object({
|
|
|
15325
16079
|
bestEventId: string().optional(),
|
|
15326
16080
|
/** Tag of the importance sub-signal that dominated the score
|
|
15327
16081
|
* (identity|dwell|proximity|class|confidence|travel|zone). */
|
|
15328
|
-
importanceReason: string().optional()
|
|
16082
|
+
importanceReason: string().optional(),
|
|
16083
|
+
/** Audio-classification labels heard on the camera during the track's
|
|
16084
|
+
* life (score ≥ device `classificationMinScore`), aggregated per label.
|
|
16085
|
+
* Absent on legacy rows / tracks with no confident audio. */
|
|
16086
|
+
audioLabels: array(TrackAudioLabelSchema).readonly().optional()
|
|
15329
16087
|
});
|
|
15330
16088
|
var BaseEventFields = {
|
|
15331
16089
|
id: string(),
|
|
@@ -15473,7 +16231,7 @@ var KeyEventSchema = object({
|
|
|
15473
16231
|
/** Track lifetime in ms (lastSeen - firstSeen). */
|
|
15474
16232
|
windowMs: number().optional()
|
|
15475
16233
|
});
|
|
15476
|
-
|
|
16234
|
+
object({
|
|
15477
16235
|
trackId: string(),
|
|
15478
16236
|
className: string(),
|
|
15479
16237
|
confidence: number(),
|
|
@@ -15481,6 +16239,23 @@ var TrackedDetectionSchema = object({
|
|
|
15481
16239
|
zones: array(string()).readonly(),
|
|
15482
16240
|
state: TrackStateSchema
|
|
15483
16241
|
});
|
|
16242
|
+
var OverlayDetectionSchema = looseObject({
|
|
16243
|
+
id: string(),
|
|
16244
|
+
kind: _enum(["first-level", "detail"]),
|
|
16245
|
+
macroClass: string(),
|
|
16246
|
+
score: number(),
|
|
16247
|
+
bbox: object({
|
|
16248
|
+
x: number(),
|
|
16249
|
+
y: number(),
|
|
16250
|
+
width: number(),
|
|
16251
|
+
height: number()
|
|
16252
|
+
}),
|
|
16253
|
+
labels: array(looseObject({
|
|
16254
|
+
label: string(),
|
|
16255
|
+
score: number()
|
|
16256
|
+
})).readonly(),
|
|
16257
|
+
parentId: string().optional()
|
|
16258
|
+
});
|
|
15484
16259
|
var ScoredObjectEventSchema = ObjectEventSchema.extend({ score: number() });
|
|
15485
16260
|
var SearchObjectEventsInput = object({
|
|
15486
16261
|
text: string(),
|
|
@@ -15491,6 +16266,20 @@ var SearchObjectEventsInput = object({
|
|
|
15491
16266
|
limit: number().default(50),
|
|
15492
16267
|
minScore: number().min(0).max(1).default(.2)
|
|
15493
16268
|
});
|
|
16269
|
+
var TrackCascadeCountsSchema = object({
|
|
16270
|
+
/** Persisted track roots deleted (authoritative). */
|
|
16271
|
+
tracks: number().int(),
|
|
16272
|
+
/** Object events removed with their tracks (best-effort; see note above). */
|
|
16273
|
+
events: number().int(),
|
|
16274
|
+
/** Track/face/plate-owned media removed (best-effort). Never identity media. */
|
|
16275
|
+
media: number().int(),
|
|
16276
|
+
/** Unassigned (non-enrolled) face reads removed (best-effort). */
|
|
16277
|
+
faces: number().int(),
|
|
16278
|
+
/** Unassigned plate reads removed (best-effort; plate leg deferred to plate-intelligence). */
|
|
16279
|
+
plates: number().int(),
|
|
16280
|
+
/** Per-track CLIP search vectors removed (best-effort). */
|
|
16281
|
+
embeddings: number().int()
|
|
16282
|
+
});
|
|
15494
16283
|
DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).readonly()), method(object({
|
|
15495
16284
|
deviceId: number(),
|
|
15496
16285
|
trackId: string()
|
|
@@ -15522,6 +16311,24 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15522
16311
|
}), {
|
|
15523
16312
|
kind: "mutation",
|
|
15524
16313
|
auth: "admin"
|
|
16314
|
+
}), method(object({
|
|
16315
|
+
deviceId: number(),
|
|
16316
|
+
cutoffMs: number()
|
|
16317
|
+
}), TrackCascadeCountsSchema, {
|
|
16318
|
+
kind: "mutation",
|
|
16319
|
+
auth: "admin"
|
|
16320
|
+
}), method(object({ deviceId: number() }), TrackCascadeCountsSchema, {
|
|
16321
|
+
kind: "mutation",
|
|
16322
|
+
auth: "admin"
|
|
16323
|
+
}), method(object({
|
|
16324
|
+
deviceId: number(),
|
|
16325
|
+
trackIds: array(string()).min(1)
|
|
16326
|
+
}), object({
|
|
16327
|
+
deleted: number().int(),
|
|
16328
|
+
failed: array(string()).readonly()
|
|
16329
|
+
}), {
|
|
16330
|
+
kind: "mutation",
|
|
16331
|
+
auth: "admin"
|
|
15525
16332
|
}), method(object({
|
|
15526
16333
|
eventId: string(),
|
|
15527
16334
|
kind: MediaFileKindEnum.optional()
|
|
@@ -15530,7 +16337,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
15530
16337
|
timestamp: number(),
|
|
15531
16338
|
frameWidth: number(),
|
|
15532
16339
|
frameHeight: number(),
|
|
15533
|
-
detections: array(
|
|
16340
|
+
detections: array(OverlayDetectionSchema).readonly()
|
|
15534
16341
|
}), object({
|
|
15535
16342
|
deviceId: number(),
|
|
15536
16343
|
trackId: string(),
|
|
@@ -18299,6 +19106,24 @@ DeviceType.Camera, method(object({
|
|
|
18299
19106
|
deviceId: number(),
|
|
18300
19107
|
status: OsdStatusSchema
|
|
18301
19108
|
});
|
|
19109
|
+
var VehicleSchema = object({
|
|
19110
|
+
id: string(),
|
|
19111
|
+
name: string(),
|
|
19112
|
+
sampleCount: number().int().nonnegative(),
|
|
19113
|
+
coverMediaKey: string().optional(),
|
|
19114
|
+
/** Inline base64 of the cover sample's plate crop, resolved from `coverMediaKey`. */
|
|
19115
|
+
coverBase64: string().optional()
|
|
19116
|
+
});
|
|
19117
|
+
var VehicleSampleInfoSchema = object({
|
|
19118
|
+
id: string(),
|
|
19119
|
+
/** The plate text this sample enrolled (self-labeling — no embedding). */
|
|
19120
|
+
text: string(),
|
|
19121
|
+
/** OCR confidence of the enrolled read (0..1). */
|
|
19122
|
+
score: number(),
|
|
19123
|
+
addedAt: number().int(),
|
|
19124
|
+
deviceId: number().int().optional(),
|
|
19125
|
+
base64: string().optional()
|
|
19126
|
+
});
|
|
18302
19127
|
var PlateInfoSchema = object({
|
|
18303
19128
|
plateId: string(),
|
|
18304
19129
|
deviceId: number().int(),
|
|
@@ -18310,6 +19135,16 @@ var PlateInfoSchema = object({
|
|
|
18310
19135
|
score: number(),
|
|
18311
19136
|
/** True when the text was manually corrected (exempt from retention). */
|
|
18312
19137
|
corrected: boolean(),
|
|
19138
|
+
/** Recognized vehicle id when a matcher lookup named this read (SQL NULL → absent). */
|
|
19139
|
+
recognizedVehicleId: string().optional(),
|
|
19140
|
+
/** Resolved vehicle name for `recognizedVehicleId` (UI convenience). */
|
|
19141
|
+
vehicleName: string().optional(),
|
|
19142
|
+
/** True once the read was assigned to a vehicle (enrolled as a sample). */
|
|
19143
|
+
assigned: boolean(),
|
|
19144
|
+
/** keyFrame parity: the plate bbox (pixel space) on the native key frame. */
|
|
19145
|
+
plateBbox: BoundingBoxSchema.optional(),
|
|
19146
|
+
/** keyFrame parity: MediaStore key of the track's native-resolution key frame. */
|
|
19147
|
+
keyFrameMediaKey: string().optional(),
|
|
18313
19148
|
base64: string().optional()
|
|
18314
19149
|
});
|
|
18315
19150
|
var MediaFileLiteSchema = object({
|
|
@@ -18349,6 +19184,48 @@ method(object({
|
|
|
18349
19184
|
}), method(object({ plateId: string() }), _void(), {
|
|
18350
19185
|
kind: "mutation",
|
|
18351
19186
|
auth: "admin"
|
|
19187
|
+
}), method(_void(), array(VehicleSchema).readonly()), method(object({ name: string().min(1) }), VehicleSchema, {
|
|
19188
|
+
kind: "mutation",
|
|
19189
|
+
auth: "admin"
|
|
19190
|
+
}), method(object({
|
|
19191
|
+
id: string(),
|
|
19192
|
+
name: string().min(1)
|
|
19193
|
+
}), _void(), {
|
|
19194
|
+
kind: "mutation",
|
|
19195
|
+
auth: "admin"
|
|
19196
|
+
}), method(object({ id: string() }), _void(), {
|
|
19197
|
+
kind: "mutation",
|
|
19198
|
+
auth: "admin"
|
|
19199
|
+
}), method(object({ vehicleId: string() }), array(VehicleSampleInfoSchema).readonly()), method(object({
|
|
19200
|
+
vehicleId: string(),
|
|
19201
|
+
sampleId: string()
|
|
19202
|
+
}), _void(), {
|
|
19203
|
+
kind: "mutation",
|
|
19204
|
+
auth: "admin"
|
|
19205
|
+
}), method(object({
|
|
19206
|
+
plateId: string(),
|
|
19207
|
+
vehicleId: string()
|
|
19208
|
+
}), _void(), {
|
|
19209
|
+
kind: "mutation",
|
|
19210
|
+
auth: "admin"
|
|
19211
|
+
}), method(object({ plateId: string() }), _void(), {
|
|
19212
|
+
kind: "mutation",
|
|
19213
|
+
auth: "admin"
|
|
19214
|
+
}), method(object({
|
|
19215
|
+
plateIds: array(string()).min(1),
|
|
19216
|
+
vehicleId: string()
|
|
19217
|
+
}), object({
|
|
19218
|
+
assigned: number().int(),
|
|
19219
|
+
failed: array(string()).readonly()
|
|
19220
|
+
}), {
|
|
19221
|
+
kind: "mutation",
|
|
19222
|
+
auth: "admin"
|
|
19223
|
+
}), method(object({ plateIds: array(string()).min(1) }), object({
|
|
19224
|
+
unassigned: number().int(),
|
|
19225
|
+
failed: array(string()).readonly()
|
|
19226
|
+
}), {
|
|
19227
|
+
kind: "mutation",
|
|
19228
|
+
auth: "admin"
|
|
18352
19229
|
});
|
|
18353
19230
|
var ModelFormatSchema = _enum(MODEL_FORMATS);
|
|
18354
19231
|
var HwAccelBackendInputSchema = _enum([
|
|
@@ -18427,6 +19304,10 @@ var GpuInfoSchema = object({
|
|
|
18427
19304
|
memoryMB: number().optional()
|
|
18428
19305
|
});
|
|
18429
19306
|
var NpuInfoSchema = object({ type: _enum(["apple-ane", "intel-npu"]) });
|
|
19307
|
+
var CoralInfoSchema = object({
|
|
19308
|
+
type: literal("coral-edgetpu"),
|
|
19309
|
+
bus: string().optional()
|
|
19310
|
+
});
|
|
18430
19311
|
var HardwareInfoSchema = object({
|
|
18431
19312
|
platform: HardwarePlatformSchema,
|
|
18432
19313
|
arch: HardwareArchSchema,
|
|
@@ -18435,7 +19316,8 @@ var HardwareInfoSchema = object({
|
|
|
18435
19316
|
totalRAM_MB: number(),
|
|
18436
19317
|
availableRAM_MB: number(),
|
|
18437
19318
|
gpu: GpuInfoSchema.nullable(),
|
|
18438
|
-
npu: NpuInfoSchema.nullable()
|
|
19319
|
+
npu: NpuInfoSchema.nullable(),
|
|
19320
|
+
coral: CoralInfoSchema.nullable().optional()
|
|
18439
19321
|
});
|
|
18440
19322
|
var PlatformScoreSchema = object({
|
|
18441
19323
|
runtime: _enum(["node", "python"]),
|
|
@@ -20894,6 +21776,168 @@ Object.freeze({
|
|
|
20894
21776
|
addonId: null,
|
|
20895
21777
|
access: "create"
|
|
20896
21778
|
},
|
|
21779
|
+
"llm.deleteModel": {
|
|
21780
|
+
capName: "llm",
|
|
21781
|
+
capScope: "system",
|
|
21782
|
+
addonId: null,
|
|
21783
|
+
access: "delete"
|
|
21784
|
+
},
|
|
21785
|
+
"llm.deleteProfile": {
|
|
21786
|
+
capName: "llm",
|
|
21787
|
+
capScope: "system",
|
|
21788
|
+
addonId: null,
|
|
21789
|
+
access: "delete"
|
|
21790
|
+
},
|
|
21791
|
+
"llm.generate": {
|
|
21792
|
+
capName: "llm",
|
|
21793
|
+
capScope: "system",
|
|
21794
|
+
addonId: null,
|
|
21795
|
+
access: "create"
|
|
21796
|
+
},
|
|
21797
|
+
"llm.generateVision": {
|
|
21798
|
+
capName: "llm",
|
|
21799
|
+
capScope: "system",
|
|
21800
|
+
addonId: null,
|
|
21801
|
+
access: "create"
|
|
21802
|
+
},
|
|
21803
|
+
"llm.getDefaults": {
|
|
21804
|
+
capName: "llm",
|
|
21805
|
+
capScope: "system",
|
|
21806
|
+
addonId: null,
|
|
21807
|
+
access: "view"
|
|
21808
|
+
},
|
|
21809
|
+
"llm.getRuntimeStatus": {
|
|
21810
|
+
capName: "llm",
|
|
21811
|
+
capScope: "system",
|
|
21812
|
+
addonId: null,
|
|
21813
|
+
access: "view"
|
|
21814
|
+
},
|
|
21815
|
+
"llm.getUsage": {
|
|
21816
|
+
capName: "llm",
|
|
21817
|
+
capScope: "system",
|
|
21818
|
+
addonId: null,
|
|
21819
|
+
access: "view"
|
|
21820
|
+
},
|
|
21821
|
+
"llm.installModel": {
|
|
21822
|
+
capName: "llm",
|
|
21823
|
+
capScope: "system",
|
|
21824
|
+
addonId: null,
|
|
21825
|
+
access: "create"
|
|
21826
|
+
},
|
|
21827
|
+
"llm.listModelCatalog": {
|
|
21828
|
+
capName: "llm",
|
|
21829
|
+
capScope: "system",
|
|
21830
|
+
addonId: null,
|
|
21831
|
+
access: "view"
|
|
21832
|
+
},
|
|
21833
|
+
"llm.listModels": {
|
|
21834
|
+
capName: "llm",
|
|
21835
|
+
capScope: "system",
|
|
21836
|
+
addonId: null,
|
|
21837
|
+
access: "view"
|
|
21838
|
+
},
|
|
21839
|
+
"llm.listNodeModels": {
|
|
21840
|
+
capName: "llm",
|
|
21841
|
+
capScope: "system",
|
|
21842
|
+
addonId: null,
|
|
21843
|
+
access: "view"
|
|
21844
|
+
},
|
|
21845
|
+
"llm.listProfileKinds": {
|
|
21846
|
+
capName: "llm",
|
|
21847
|
+
capScope: "system",
|
|
21848
|
+
addonId: null,
|
|
21849
|
+
access: "view"
|
|
21850
|
+
},
|
|
21851
|
+
"llm.listProfiles": {
|
|
21852
|
+
capName: "llm",
|
|
21853
|
+
capScope: "system",
|
|
21854
|
+
addonId: null,
|
|
21855
|
+
access: "view"
|
|
21856
|
+
},
|
|
21857
|
+
"llm.listRuntimeNodes": {
|
|
21858
|
+
capName: "llm",
|
|
21859
|
+
capScope: "system",
|
|
21860
|
+
addonId: null,
|
|
21861
|
+
access: "view"
|
|
21862
|
+
},
|
|
21863
|
+
"llm.setDefault": {
|
|
21864
|
+
capName: "llm",
|
|
21865
|
+
capScope: "system",
|
|
21866
|
+
addonId: null,
|
|
21867
|
+
access: "create"
|
|
21868
|
+
},
|
|
21869
|
+
"llm.startRuntime": {
|
|
21870
|
+
capName: "llm",
|
|
21871
|
+
capScope: "system",
|
|
21872
|
+
addonId: null,
|
|
21873
|
+
access: "create"
|
|
21874
|
+
},
|
|
21875
|
+
"llm.stopRuntime": {
|
|
21876
|
+
capName: "llm",
|
|
21877
|
+
capScope: "system",
|
|
21878
|
+
addonId: null,
|
|
21879
|
+
access: "create"
|
|
21880
|
+
},
|
|
21881
|
+
"llm.testProfile": {
|
|
21882
|
+
capName: "llm",
|
|
21883
|
+
capScope: "system",
|
|
21884
|
+
addonId: null,
|
|
21885
|
+
access: "create"
|
|
21886
|
+
},
|
|
21887
|
+
"llm.upsertProfile": {
|
|
21888
|
+
capName: "llm",
|
|
21889
|
+
capScope: "system",
|
|
21890
|
+
addonId: null,
|
|
21891
|
+
access: "create"
|
|
21892
|
+
},
|
|
21893
|
+
"llmRuntime.complete": {
|
|
21894
|
+
capName: "llm-runtime",
|
|
21895
|
+
capScope: "system",
|
|
21896
|
+
addonId: null,
|
|
21897
|
+
access: "create"
|
|
21898
|
+
},
|
|
21899
|
+
"llmRuntime.deleteModel": {
|
|
21900
|
+
capName: "llm-runtime",
|
|
21901
|
+
capScope: "system",
|
|
21902
|
+
addonId: null,
|
|
21903
|
+
access: "delete"
|
|
21904
|
+
},
|
|
21905
|
+
"llmRuntime.ensureStarted": {
|
|
21906
|
+
capName: "llm-runtime",
|
|
21907
|
+
capScope: "system",
|
|
21908
|
+
addonId: null,
|
|
21909
|
+
access: "create"
|
|
21910
|
+
},
|
|
21911
|
+
"llmRuntime.getDiskUsage": {
|
|
21912
|
+
capName: "llm-runtime",
|
|
21913
|
+
capScope: "system",
|
|
21914
|
+
addonId: null,
|
|
21915
|
+
access: "view"
|
|
21916
|
+
},
|
|
21917
|
+
"llmRuntime.installModel": {
|
|
21918
|
+
capName: "llm-runtime",
|
|
21919
|
+
capScope: "system",
|
|
21920
|
+
addonId: null,
|
|
21921
|
+
access: "create"
|
|
21922
|
+
},
|
|
21923
|
+
"llmRuntime.listLocalModels": {
|
|
21924
|
+
capName: "llm-runtime",
|
|
21925
|
+
capScope: "system",
|
|
21926
|
+
addonId: null,
|
|
21927
|
+
access: "view"
|
|
21928
|
+
},
|
|
21929
|
+
"llmRuntime.status": {
|
|
21930
|
+
capName: "llm-runtime",
|
|
21931
|
+
capScope: "system",
|
|
21932
|
+
addonId: null,
|
|
21933
|
+
access: "view"
|
|
21934
|
+
},
|
|
21935
|
+
"llmRuntime.stop": {
|
|
21936
|
+
capName: "llm-runtime",
|
|
21937
|
+
capScope: "system",
|
|
21938
|
+
addonId: null,
|
|
21939
|
+
access: "create"
|
|
21940
|
+
},
|
|
20897
21941
|
"localNetwork.getAllowedAddresses": {
|
|
20898
21942
|
capName: "local-network",
|
|
20899
21943
|
capScope: "system",
|
|
@@ -21524,6 +22568,12 @@ Object.freeze({
|
|
|
21524
22568
|
addonId: null,
|
|
21525
22569
|
access: "delete"
|
|
21526
22570
|
},
|
|
22571
|
+
"pipelineAnalytics.deleteTracks": {
|
|
22572
|
+
capName: "pipeline-analytics",
|
|
22573
|
+
capScope: "device",
|
|
22574
|
+
addonId: null,
|
|
22575
|
+
access: "delete"
|
|
22576
|
+
},
|
|
21527
22577
|
"pipelineAnalytics.getActiveTracks": {
|
|
21528
22578
|
capName: "pipeline-analytics",
|
|
21529
22579
|
capScope: "device",
|
|
@@ -21590,12 +22640,24 @@ Object.freeze({
|
|
|
21590
22640
|
addonId: null,
|
|
21591
22641
|
access: "create"
|
|
21592
22642
|
},
|
|
22643
|
+
"pipelineAnalytics.pruneTracksBefore": {
|
|
22644
|
+
capName: "pipeline-analytics",
|
|
22645
|
+
capScope: "device",
|
|
22646
|
+
addonId: null,
|
|
22647
|
+
access: "create"
|
|
22648
|
+
},
|
|
21593
22649
|
"pipelineAnalytics.searchObjectEvents": {
|
|
21594
22650
|
capName: "pipeline-analytics",
|
|
21595
22651
|
capScope: "device",
|
|
21596
22652
|
addonId: null,
|
|
21597
22653
|
access: "view"
|
|
21598
22654
|
},
|
|
22655
|
+
"pipelineAnalytics.wipeAllAnalytics": {
|
|
22656
|
+
capName: "pipeline-analytics",
|
|
22657
|
+
capScope: "device",
|
|
22658
|
+
addonId: null,
|
|
22659
|
+
access: "delete"
|
|
22660
|
+
},
|
|
21599
22661
|
"pipelineExecutor.cacheFrameInPool": {
|
|
21600
22662
|
capName: "pipeline-executor",
|
|
21601
22663
|
capScope: "system",
|
|
@@ -22094,18 +23156,48 @@ Object.freeze({
|
|
|
22094
23156
|
addonId: null,
|
|
22095
23157
|
access: "create"
|
|
22096
23158
|
},
|
|
23159
|
+
"pipelineRunner.runDetailSubtree": {
|
|
23160
|
+
capName: "pipeline-runner",
|
|
23161
|
+
capScope: "system",
|
|
23162
|
+
addonId: null,
|
|
23163
|
+
access: "create"
|
|
23164
|
+
},
|
|
23165
|
+
"plateGallery.assignPlate": {
|
|
23166
|
+
capName: "plate-gallery",
|
|
23167
|
+
capScope: "system",
|
|
23168
|
+
addonId: null,
|
|
23169
|
+
access: "create"
|
|
23170
|
+
},
|
|
23171
|
+
"plateGallery.assignPlates": {
|
|
23172
|
+
capName: "plate-gallery",
|
|
23173
|
+
capScope: "system",
|
|
23174
|
+
addonId: null,
|
|
23175
|
+
access: "create"
|
|
23176
|
+
},
|
|
22097
23177
|
"plateGallery.correctPlateText": {
|
|
22098
23178
|
capName: "plate-gallery",
|
|
22099
23179
|
capScope: "system",
|
|
22100
23180
|
addonId: null,
|
|
22101
23181
|
access: "create"
|
|
22102
23182
|
},
|
|
23183
|
+
"plateGallery.createVehicle": {
|
|
23184
|
+
capName: "plate-gallery",
|
|
23185
|
+
capScope: "system",
|
|
23186
|
+
addonId: null,
|
|
23187
|
+
access: "create"
|
|
23188
|
+
},
|
|
22103
23189
|
"plateGallery.deletePlate": {
|
|
22104
23190
|
capName: "plate-gallery",
|
|
22105
23191
|
capScope: "system",
|
|
22106
23192
|
addonId: null,
|
|
22107
23193
|
access: "delete"
|
|
22108
23194
|
},
|
|
23195
|
+
"plateGallery.deleteVehicle": {
|
|
23196
|
+
capName: "plate-gallery",
|
|
23197
|
+
capScope: "system",
|
|
23198
|
+
addonId: null,
|
|
23199
|
+
access: "delete"
|
|
23200
|
+
},
|
|
22109
23201
|
"plateGallery.getPlateByTrack": {
|
|
22110
23202
|
capName: "plate-gallery",
|
|
22111
23203
|
capScope: "system",
|
|
@@ -22124,6 +23216,30 @@ Object.freeze({
|
|
|
22124
23216
|
addonId: null,
|
|
22125
23217
|
access: "view"
|
|
22126
23218
|
},
|
|
23219
|
+
"plateGallery.listVehicles": {
|
|
23220
|
+
capName: "plate-gallery",
|
|
23221
|
+
capScope: "system",
|
|
23222
|
+
addonId: null,
|
|
23223
|
+
access: "view"
|
|
23224
|
+
},
|
|
23225
|
+
"plateGallery.listVehicleSamples": {
|
|
23226
|
+
capName: "plate-gallery",
|
|
23227
|
+
capScope: "system",
|
|
23228
|
+
addonId: null,
|
|
23229
|
+
access: "view"
|
|
23230
|
+
},
|
|
23231
|
+
"plateGallery.removeVehicleSample": {
|
|
23232
|
+
capName: "plate-gallery",
|
|
23233
|
+
capScope: "system",
|
|
23234
|
+
addonId: null,
|
|
23235
|
+
access: "delete"
|
|
23236
|
+
},
|
|
23237
|
+
"plateGallery.renameVehicle": {
|
|
23238
|
+
capName: "plate-gallery",
|
|
23239
|
+
capScope: "system",
|
|
23240
|
+
addonId: null,
|
|
23241
|
+
access: "create"
|
|
23242
|
+
},
|
|
22127
23243
|
"plateGallery.searchPlates": {
|
|
22128
23244
|
capName: "plate-gallery",
|
|
22129
23245
|
capScope: "system",
|
|
@@ -22136,6 +23252,18 @@ Object.freeze({
|
|
|
22136
23252
|
addonId: null,
|
|
22137
23253
|
access: "view"
|
|
22138
23254
|
},
|
|
23255
|
+
"plateGallery.unassignPlate": {
|
|
23256
|
+
capName: "plate-gallery",
|
|
23257
|
+
capScope: "system",
|
|
23258
|
+
addonId: null,
|
|
23259
|
+
access: "create"
|
|
23260
|
+
},
|
|
23261
|
+
"plateGallery.unassignPlates": {
|
|
23262
|
+
capName: "plate-gallery",
|
|
23263
|
+
capScope: "system",
|
|
23264
|
+
addonId: null,
|
|
23265
|
+
access: "create"
|
|
23266
|
+
},
|
|
22139
23267
|
"platformProbe.getCapabilities": {
|
|
22140
23268
|
capName: "platform-probe",
|
|
22141
23269
|
capScope: "system",
|
|
@@ -22328,6 +23456,48 @@ Object.freeze({
|
|
|
22328
23456
|
addonId: null,
|
|
22329
23457
|
access: "create"
|
|
22330
23458
|
},
|
|
23459
|
+
"sceneMonitor.captureReference": {
|
|
23460
|
+
capName: "scene-monitor",
|
|
23461
|
+
capScope: "device",
|
|
23462
|
+
addonId: null,
|
|
23463
|
+
access: "create"
|
|
23464
|
+
},
|
|
23465
|
+
"sceneMonitor.createScene": {
|
|
23466
|
+
capName: "scene-monitor",
|
|
23467
|
+
capScope: "device",
|
|
23468
|
+
addonId: null,
|
|
23469
|
+
access: "create"
|
|
23470
|
+
},
|
|
23471
|
+
"sceneMonitor.deleteReference": {
|
|
23472
|
+
capName: "scene-monitor",
|
|
23473
|
+
capScope: "device",
|
|
23474
|
+
addonId: null,
|
|
23475
|
+
access: "delete"
|
|
23476
|
+
},
|
|
23477
|
+
"sceneMonitor.deleteScene": {
|
|
23478
|
+
capName: "scene-monitor",
|
|
23479
|
+
capScope: "device",
|
|
23480
|
+
addonId: null,
|
|
23481
|
+
access: "delete"
|
|
23482
|
+
},
|
|
23483
|
+
"sceneMonitor.listScenes": {
|
|
23484
|
+
capName: "scene-monitor",
|
|
23485
|
+
capScope: "device",
|
|
23486
|
+
addonId: null,
|
|
23487
|
+
access: "view"
|
|
23488
|
+
},
|
|
23489
|
+
"sceneMonitor.recheckNow": {
|
|
23490
|
+
capName: "scene-monitor",
|
|
23491
|
+
capScope: "device",
|
|
23492
|
+
addonId: null,
|
|
23493
|
+
access: "create"
|
|
23494
|
+
},
|
|
23495
|
+
"sceneMonitor.updateScene": {
|
|
23496
|
+
capName: "scene-monitor",
|
|
23497
|
+
capScope: "device",
|
|
23498
|
+
addonId: null,
|
|
23499
|
+
access: "create"
|
|
23500
|
+
},
|
|
22331
23501
|
"scriptRunner.run": {
|
|
22332
23502
|
capName: "script-runner",
|
|
22333
23503
|
capScope: "device",
|
|
@@ -23412,6 +24582,7 @@ Object.freeze({
|
|
|
23412
24582
|
Object.freeze({
|
|
23413
24583
|
"broker": "broker",
|
|
23414
24584
|
"device-export": "device-export",
|
|
24585
|
+
"llm": "ai",
|
|
23415
24586
|
"mesh-network": "mesh",
|
|
23416
24587
|
"mqtt-broker": "broker",
|
|
23417
24588
|
"network-access": "ingress",
|
|
@@ -23443,6 +24614,143 @@ object({
|
|
|
23443
24614
|
bootAttempts: number(),
|
|
23444
24615
|
schemaVersion: literal(1)
|
|
23445
24616
|
});
|
|
24617
|
+
var NOTIFIER_ICONS = {
|
|
24618
|
+
telegram: {
|
|
24619
|
+
contentType: "image/svg+xml",
|
|
24620
|
+
body: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"#26A5E4\"><path d=\"M11.944 0A12 12 0 0 0 0 12a12 12 0 0 0 12 12 12 12 0 0 0 12-12A12 12 0 0 0 12 0a12 12 0 0 0-.056 0zm4.962 7.224c.1-.002.321.023.465.14a.506.506 0 0 1 .171.325c.016.093.036.306.02.472-.18 1.898-.962 6.502-1.36 8.627-.168.9-.499 1.201-.82 1.23-.696.065-1.225-.46-1.9-.902-1.056-.693-1.653-1.124-2.678-1.8-1.185-.78-.417-1.21.258-1.91.177-.184 3.247-2.977 3.307-3.23.007-.032.014-.15-.056-.212s-.174-.041-.249-.024c-.106.024-1.793 1.14-5.061 3.345-.48.33-.913.49-1.302.48-.428-.008-1.252-.241-1.865-.44-.752-.245-1.349-.374-1.297-.789.027-.216.325-.437.893-.663 3.498-1.524 5.83-2.529 6.998-3.014 3.332-1.386 4.025-1.627 4.476-1.635z\"/></svg>"
|
|
24621
|
+
},
|
|
24622
|
+
discord: {
|
|
24623
|
+
contentType: "image/svg+xml",
|
|
24624
|
+
body: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"#5865F2\"><path d=\"M20.317 4.3698a19.7913 19.7913 0 00-4.8851-1.5152.0741.0741 0 00-.0785.0371c-.211.3753-.4447.8648-.6083 1.2495-1.8447-.2762-3.68-.2762-5.4868 0-.1636-.3933-.4058-.8742-.6177-1.2495a.077.077 0 00-.0785-.037 19.7363 19.7363 0 00-4.8852 1.515.0699.0699 0 00-.0321.0277C.5334 9.0458-.319 13.5799.0992 18.0578a.0824.0824 0 00.0312.0561c2.0528 1.5076 4.0413 2.4228 5.9929 3.0294a.0777.0777 0 00.0842-.0276c.4616-.6304.8731-1.2952 1.226-1.9942a.076.076 0 00-.0416-.1057c-.6528-.2476-1.2743-.5495-1.8722-.8923a.077.077 0 01-.0076-.1277c.1258-.0943.2517-.1923.3718-.2914a.0743.0743 0 01.0776-.0105c3.9278 1.7933 8.18 1.7933 12.0614 0a.0739.0739 0 01.0785.0095c.1202.099.246.1981.3728.2924a.077.077 0 01-.0066.1276 12.2986 12.2986 0 01-1.873.8914.0766.0766 0 00-.0407.1067c.3604.698.7719 1.3628 1.225 1.9932a.076.076 0 00.0842.0286c1.961-.6067 3.9495-1.5219 6.0023-3.0294a.077.077 0 00.0313-.0552c.5004-5.177-.8382-9.6739-3.5485-13.6604a.061.061 0 00-.0312-.0286zM8.02 15.3312c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9555-2.4189 2.157-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.9555 2.4189-2.1569 2.4189zm7.9748 0c-1.1825 0-2.1569-1.0857-2.1569-2.419 0-1.3332.9554-2.4189 2.1569-2.4189 1.2108 0 2.1757 1.0952 2.1568 2.419 0 1.3332-.946 2.4189-2.1568 2.4189Z\"/></svg>"
|
|
24625
|
+
},
|
|
24626
|
+
whatsapp: {
|
|
24627
|
+
contentType: "image/svg+xml",
|
|
24628
|
+
body: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"#25D366\"><path d=\"M17.472 14.382c-.297-.149-1.758-.867-2.03-.967-.273-.099-.471-.148-.67.15-.197.297-.767.966-.94 1.164-.173.199-.347.223-.644.075-.297-.15-1.255-.463-2.39-1.475-.883-.788-1.48-1.761-1.653-2.059-.173-.297-.018-.458.13-.606.134-.133.298-.347.446-.52.149-.174.198-.298.298-.497.099-.198.05-.371-.025-.52-.075-.149-.669-1.612-.916-2.207-.242-.579-.487-.5-.669-.51-.173-.008-.371-.01-.57-.01-.198 0-.52.074-.792.372-.272.297-1.04 1.016-1.04 2.479 0 1.462 1.065 2.875 1.213 3.074.149.198 2.096 3.2 5.077 4.487.709.306 1.262.489 1.694.625.712.227 1.36.195 1.871.118.571-.085 1.758-.719 2.006-1.413.248-.694.248-1.289.173-1.413-.074-.124-.272-.198-.57-.347m-5.421 7.403h-.004a9.87 9.87 0 01-5.031-1.378l-.361-.214-3.741.982.998-3.648-.235-.374a9.86 9.86 0 01-1.51-5.26c.001-5.45 4.436-9.884 9.888-9.884 2.64 0 5.122 1.03 6.988 2.898a9.825 9.825 0 012.893 6.994c-.003 5.45-4.437 9.884-9.885 9.884m8.413-18.297A11.815 11.815 0 0012.05 0C5.495 0 .16 5.335.157 11.892c0 2.096.547 4.142 1.588 5.945L.057 24l6.305-1.654a11.882 11.882 0 005.683 1.448h.005c6.554 0 11.89-5.335 11.893-11.893a11.821 11.821 0 00-3.48-8.413Z\"/></svg>"
|
|
24629
|
+
},
|
|
24630
|
+
ntfy: {
|
|
24631
|
+
contentType: "image/svg+xml",
|
|
24632
|
+
body: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"#317F6F\"><path d=\"M12.597 13.693v2.156h6.205v-2.156ZM5.183 6.549v2.363l3.591 1.901.023.01-.023.009-3.591 1.901v2.35l.386-.211 5.456-2.969V9.729ZM3.659 2.037C1.915 2.037.42 3.41.42 5.154v.002L.438 18.73 0 21.963l5.956-1.583h14.806c1.744 0 3.238-1.374 3.238-3.118V5.154c0-1.744-1.493-3.116-3.237-3.117h-.001zm0 2.2h17.104c.613.001 1.037.447 1.037.917v12.108c0 .47-.424.916-1.038.916H5.633l-3.026.915.031-.179-.017-13.76c0-.47.424-.917 1.038-.917z\"/></svg>"
|
|
24633
|
+
},
|
|
24634
|
+
pushover: {
|
|
24635
|
+
contentType: "image/svg+xml",
|
|
24636
|
+
body: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"#249DF1\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M6.16 10.985c-.83 -1.935 1.53 -7.985 8.195 -7.985c3.333 0 4.645 1.382 4.645 3.9c0 2.597 -2.612 6.1 -9 6.1\" /><path d=\"M12.5 6l-5.5 15\" /></svg>"
|
|
24637
|
+
},
|
|
24638
|
+
webhook: {
|
|
24639
|
+
contentType: "image/svg+xml",
|
|
24640
|
+
body: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 24\" width=\"24\" height=\"24\" fill=\"none\" stroke=\"#7C3AED\" stroke-width=\"2\" stroke-linecap=\"round\" stroke-linejoin=\"round\"><path d=\"M4.876 13.61a4 4 0 1 0 6.124 3.39h6\" /><path d=\"M15.066 20.502a4 4 0 1 0 1.934 -7.502c-.706 0 -1.424 .179 -2 .5l-3 -5.5\" /><path d=\"M16 8a4 4 0 1 0 -8 0c0 1.506 .77 2.818 2 3.5l-3 5.5\" /></svg>"
|
|
24641
|
+
},
|
|
24642
|
+
gotify: {
|
|
24643
|
+
contentType: "image/svg+xml",
|
|
24644
|
+
body: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 500 500\" width=\"500\" height=\"500\"><style type=\"text/css\" id=\"style1\"> .st0{fill:#DDCBA2;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;} .st1{fill:#71CAEE;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;} .st2{fill:#FFFFFF;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;} .st3{fill:#888E93;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;} .st4{fill:#F0F0F0;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;} .st5{fill:none;stroke:#000000;stroke-width:2;stroke-linecap:round;stroke-miterlimit:10;} .st6{fill:url(#SVGID_1_);} .st7{fill:url(#SVGID_2_);} .st8{fill:#FFFFFF;} .st9{fill:url(#SVGID_3_);} .st10{fill:url(#SVGID_4_);} .st11{fill:url(#SVGID_5_);} .st12{fill:none;stroke:#FFFFFF;stroke-width:63;stroke-linecap:round;stroke-miterlimit:10;} .st13{fill:none;stroke:#FFFFFF;stroke-width:56;stroke-linecap:round;stroke-miterlimit:10;} .st14{fill:none;stroke:#A4C639;stroke-width:45.1385;stroke-linecap:round;stroke-miterlimit:10;} .st15{fill:none;stroke:#FFFFFF;stroke-width:16;} .st16{fill:#A4C639;} .st17{fill:#E6E6E6;stroke:#FFFFFF;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;} .st18{fill:none;stroke:#FFFFFF;stroke-width:4;stroke-linecap:round;stroke-linejoin:round;stroke-miterlimit:10;} .st19{fill:url(#SVGID_6_);} .st20{fill:url(#SVGID_7_);} .st21{fill:url(#SVGID_8_);} </style><g transform=\"matrix(2.3327641,0,0,2.3327641,-431.80457,-323.21807)\"><g id=\"g48\" transform=\"translate(-24.881692,25.94805)\"><g id=\"g47\"><path class=\"st1\" d=\"m 258.9,119.7 c -3,-0.9 -6,-1.8 -9,-2.7 -4.6,-1.4 -9.2,-2.8 -14,-2.5 -2.8,0.2 -6.1,1.3 -6.9,4 -0.6,2 -1.6,7.3 -1.3,7.9 1.5,3.4 13.9,6.7 18.3,6.7\" id=\"path2\" /><path d=\"m 392.6,177.9 c -1.4,1.4 -2.2,3.5 -2.5,5.5 -0.2,1.4 -0.1,3 0.5,4.3 0.6,1.3 1.8,2.3 3.1,3 1.3,0.6 2.8,0.9 4.3,0.9 1.1,0 2.3,-0.1 3.1,-0.9 0.6,-0.7 0.8,-1.6 0.9,-2.5 0.2,-2.3 -0.1,-4.7 -0.9,-6.9 -0.4,-1.1 -0.9,-2.3 -1.8,-3.1 -1.7,-1.8 -4.5,-2.2 -6.4,-0.5 -0.1,0 -0.2,0.1 -0.3,0.2 z\" id=\"path3\" /><path class=\"st2\" d=\"m 358.5,164.2 c -1,-1 0,-2.7 1,-3.7 5.8,-5.2 15.1,-4.6 21.8,-0.6 10.9,6.6 15.6,19.9 17.2,32.5 0.6,5.2 0.9,10.6 -0.5,15.7 -1.4,5.1 -4.6,9.9 -9.3,12.1 -1.1,0.5 -2.3,0.9 -3.4,0.5 -1.1,-0.4 -1.9,-1.8 -1.2,-2.8 -9.4,-13.6 -19,-26.8 -20.9,-43.2 -0.5,-4.1 -1.8,-7.4 -4.7,-10.5 z\" id=\"path4\" /><path class=\"st1\" d=\"m 220.05771,133 c 34.58546,-18 79.30862,-19.6 112.16964,-8.7 23.69194,7.9 41.3449,26.7 49.4515,50 7.11442,20.6 7.1441,43.6 3.04307,65.7 -7.4942,40.2 -26.19597,77.9 -48.96683,112.6 -12.56244,19 -24.57093,36 -44.23305,48.5 -38.69347,24.6 -88.90104,22.1 -129.28516,11.5 -19.50745,-5.1 -38.36842,-17.3 -44.32901,-37.3 -3.81479,-12.8 -2.07563,-27.6 4.5788,-40 13.50886,-24.8 46.15565,-38.4 50.82627,-67.9 1.36964,-8.7 -0.26842,-17.3 -1.62187,-25.7 -3.7989,-23.4 -5.37477,-45.8 6.68784,-68.7 9.46237,-17.7 24.28607,-31 41.6788,-40 z\" id=\"path6\" /><path class=\"st2\" d=\"m 264.5,174.9 c -0.5,0.5 -0.9,1 -1.3,1.6 -9,11.6 -12,27.9 -9.3,42.1 1.7,9 5.9,17.9 13.2,23.4 19.3,14.6 51.5,13.5 68.4,-1.5 24.4,-21.7 13,-67.6 -14,-78.8 -17.6,-7.2 -43.7,-1.6 -57,13.2 z\" id=\"path9\" /><g id=\"g11\"><path class=\"st2\" d=\"m 382.1,237.1 c 1.4,-0.1 2.9,-0.1 4.3,0.1 0.3,0 0.7,0.1 1,0.4 0.2,0.3 0.4,0.7 0.5,1.1 1,3.9 0.5,8.2 0.1,12.4 -0.1,0.9 -0.2,1.8 -0.6,2.6 -1,2.1 -3.1,2.7 -4.7,2.7 -0.1,0 -0.2,0 -0.3,-0.1 -0.3,-0.2 -0.3,-0.7 -0.2,-1.2 0.3,-5.9 -0.1,-11.9 -0.1,-18 0,0.1 0,0.1 0,0 z\" id=\"path10\" /><path class=\"st2\" d=\"m 378.7,236.8 c -1.4,0.4 -2.5,2 -2.8,4.4 -0.5,4.4 -0.7,8.9 -0.5,13.4 0,0.9 0.1,1.9 0.5,2.4 0.2,0.3 0.5,0.4 0.8,0.4 1.6,0.3 4.1,-0.6 5.6,-1 0,0 0,-5.2 -0.1,-8 -0.1,-2.8 -0.1,-6.1 -0.2,-8.9 0,-0.6 0,-1.5 0,-2.2 0.1,-0.7 -2.6,-0.7 -3.3,-0.5 z\" id=\"path11\" /></g><path class=\"st0\" d=\"m 358.3,231.8 c -0.3,2.2 0.1,4.7 1.7,7.4 2.6,4.4 7,6.1 11.9,5.8 8.9,-0.6 25.3,-5.4 27.5,-15.7 0.6,-3 -0.3,-6.1 -2.2,-8.5 -6.2,-7.8 -17.8,-5.7 -25.6,-2 -5.9,2.7 -12.4,7 -13.3,13 z\" id=\"path12\" /><path class=\"st3\" d=\"m 386.4,208.6 c 2.2,1.4 3.7,3.8 4,7 0.3,3.6 -1.4,7.5 -5,8.8 -2.9,1.1 -6.2,0.6 -9.1,-0.4 -2.9,-1 -5.8,-2.8 -6.8,-5.7 -0.7,-2 -0.3,-4.3 0.7,-6.1 1.1,-1.8 2.8,-3.2 4.7,-4.1 3.9,-1.8 8.4,-1.6 11.5,0.5 z\" id=\"path13\" /><path class=\"st0\" d=\"m 414.7,262.6 c 2.4,0.6 4.8,2.1 5.6,4.4 0.8,2.3 0.1,4.9 -1.6,6.7 -1.7,1.8 -4.2,2.5 -6.6,2.5 -0.8,0 -1.7,-0.1 -2.4,-0.5 -2.5,-1.1 -3.5,-4 -4.2,-6.6 -1.8,-6.8 3.6,-7.8 9.2,-6.5 z\" id=\"path14\" /><g id=\"g17\"><path class=\"st4\" d=\"m 267.1,284.7 c 2.3,-4.5 141.3,-36.2 144.7,-31.6 3.4,4.5 15.8,88.2 9,90.4 -6.8,2.3 -119.8,37.3 -126.6,35 -6.8,-2.3 -29.4,-89.3 -27.1,-93.8 z\" id=\"path15\" /><path class=\"st5\" d=\"m 294.2,378.5 c 0,0 54.3,-74.6 59.9,-76.9 5.7,-2.3 67.3,41.3 67.3,41.3\" id=\"path16\" /><path class=\"st4\" d=\"m 267,287.7 c 0,0 86,38.8 91.6,36.6 5.7,-2.3 53.1,-71.2 53.1,-71.2\" id=\"path17\" /></g><g id=\"g33\"><linearGradient id=\"SVGID_2_\" gradientUnits=\"userSpaceOnUse\" x1=\"265.03339\" y1=\"280.2951\" x2=\"275.3009\" y2=\"302.31369\"><stop offset=\"5.152266e-07\" style=\"stop-color:#71CAEE\" id=\"stop25\" /><stop offset=\"4.104413e-02\" style=\"stop-color:#83CAE2\" id=\"stop26\" /><stop offset=\"0.1206\" style=\"stop-color:#9FCACE\" id=\"stop27\" /><stop offset=\"0.2107\" style=\"stop-color:#B6CBBE\" id=\"stop28\" /><stop offset=\"0.3139\" style=\"stop-color:#C7CBB1\" id=\"stop29\" /><stop offset=\"0.4384\" style=\"stop-color:#D4CBA8\" id=\"stop30\" /><stop offset=\"0.6058\" style=\"stop-color:#DBCBA3\" id=\"stop31\" /><stop offset=\"1\" style=\"stop-color:#DDCBA2\" id=\"stop32\" /></linearGradient><path class=\"st7\" d=\"m 261.9,283.5 c -0.1,4.2 4.3,7.3 8.4,7.6 4.1,0.3 8.2,-1.3 12.2,-2.6 1.4,-0.4 2.9,-0.8 4.2,-0.2 1.8,0.9 2.7,4.1 1.8,5.9 -0.9,1.8 -3.4,3.5 -5.3,4.4 -6.5,3 -12.9,3.6 -19.9,2 -5.3,-1.2 -11.3,-4.3 -13,-13.5\" id=\"path32\" style=\"fill:url(#SVGID_2_)\" /><path class=\"st5\" d=\"m 261.9,283.5 c -0.1,4.2 4.3,7.3 8.4,7.6 4.1,0.3 8.2,-1.3 12.2,-2.6 1.4,-0.4 2.9,-0.8 4.2,-0.2 1.8,0.9 2.7,4.1 1.8,5.9 -0.9,1.8 -3.4,3.5 -5.3,4.4 -6.5,3 -12.9,3.6 -19.9,2 -5.3,-1.2 -11.3,-4.3 -13,-13.5\" id=\"path33\" /></g><path d=\"m 318.4,198.4 c -2,-0.3 -4.1,0.1 -5.9,1.3 -3.2,2.1 -4.7,6.2 -4.7,9.9 0,1.9 0.4,3.8 1.4,5.3 1.2,1.7 3.1,2.9 5.2,3.4 3.4,0.8 8.2,0.7 10.5,-2.5 1,-1.5 1.4,-3.3 1.5,-5.1 0.5,-5.7 -1.8,-11.4 -8,-12.3 z\" id=\"path34\" /><path class=\"st8\" d=\"m 320.4,203.3 c 0.9,0.3 1.7,0.8 2.1,1.7 0.4,0.8 0.4,1.7 0.3,2.5 -0.1,1 -0.6,2 -1.5,2.7 -0.7,0.5 -1.7,0.7 -2.6,0.5 -0.9,-0.2 -1.7,-0.8 -2.2,-1.6 -1.1,-1.6 -0.9,-4.4 0.9,-5.5 0.9,-0.4 2,-0.6 3,-0.3 z\" id=\"path35\" /></g></g></g></svg>"
|
|
24645
|
+
},
|
|
24646
|
+
zentik: {
|
|
24647
|
+
contentType: "image/png",
|
|
24648
|
+
body: Uint8Array.from(Buffer.from("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", "base64"))
|
|
24649
|
+
}
|
|
24650
|
+
};
|
|
24651
|
+
//#endregion
|
|
24652
|
+
//#region src/icon-routes.ts
|
|
24653
|
+
/**
|
|
24654
|
+
* Serves the bundled per-kind brand icons (`icons-data.ts`) over the addon's
|
|
24655
|
+
* own HTTP surface via the `addon-routes` cap. One public GET route per kind:
|
|
24656
|
+
*
|
|
24657
|
+
* GET /addon/notifiers/icons/<kind> → the bundled brand icon
|
|
24658
|
+
*
|
|
24659
|
+
* Each route sets the icon's OWN content-type (`image/svg+xml` for the vector
|
|
24660
|
+
* icons, `image/png` for the raster zentik logo) so a browser `<img src>`
|
|
24661
|
+
* renders it — an SVG that wraps a raster `data:` URI is blocked by the strict
|
|
24662
|
+
* `<img>` SVG mode, so zentik ships as its real PNG bytes instead.
|
|
24663
|
+
*
|
|
24664
|
+
* The bytes ship INSIDE the addon — no external CDN / hotlink — so the icons are
|
|
24665
|
+
* self-contained and CSP-safe. `listTargetKinds` stamps each kind with the
|
|
24666
|
+
* matching `iconUrl` (see `iconUrlFor`), letting the admin UI render
|
|
24667
|
+
* `<img src=iconUrl>` instead of a text label.
|
|
24668
|
+
*
|
|
24669
|
+
* We register a STATIC route per kind rather than one `/:kind` param route: the
|
|
24670
|
+
* forked-addon route bridge re-matches the pattern against itself, which would
|
|
24671
|
+
* clobber a `:kind` path param with the literal token `':kind'`. Static paths
|
|
24672
|
+
* carry no param, so they route correctly across the process boundary.
|
|
24673
|
+
*
|
|
24674
|
+
* ── Why the addon pre-encodes and owns `content-encoding` ────────────────────
|
|
24675
|
+
* The hub serves an addon route by piping the response THROUGH its global
|
|
24676
|
+
* `@fastify/compress`. For a forked addon, our reply body crosses the UDS route
|
|
24677
|
+
* bridge as opaque bytes and the hub then re-encodes them — and on that bridged
|
|
24678
|
+
* path the compressor emits an EMPTY brotli stream for any compressible body
|
|
24679
|
+
* over its 1 KiB threshold (`content-encoding: br` + `content-length: 0`), so a
|
|
24680
|
+
* browser `<img>` decodes nothing and shows a broken icon. Small icons (< 1 KiB)
|
|
24681
|
+
* and the raster PNG (a non-compressible type) slip under the compressor and
|
|
24682
|
+
* render, which is why only the larger SVGs broke.
|
|
24683
|
+
*
|
|
24684
|
+
* The fix is to make the compressor SKIP us: it leaves a response untouched when
|
|
24685
|
+
* `content-encoding` is already set to a non-identity coding. So each route
|
|
24686
|
+
* negotiates the request's `Accept-Encoding`, serves a pre-computed br/gzip/
|
|
24687
|
+
* deflate variant (or identity when the client accepts none), and stamps the
|
|
24688
|
+
* matching `content-encoding` itself. Bytes we own, correctly framed, reach the
|
|
24689
|
+
* browser intact. Mirrors the `compress: false` treatment the hub's own binary
|
|
24690
|
+
* pull route already uses for the same class of bug.
|
|
24691
|
+
*/
|
|
24692
|
+
/** The addon id — also the `/addon/<id>/…` route prefix. Mirrors `NOTIFIERS_ADDON_ID`. */
|
|
24693
|
+
var NOTIFIERS_ROUTE_ID = "notifiers";
|
|
24694
|
+
/** One day; icons are immutable per addon version. */
|
|
24695
|
+
var CACHE_CONTROL = "public, max-age=86400, immutable";
|
|
24696
|
+
var ICON_KINDS = new Set(Object.keys(NOTIFIER_ICONS));
|
|
24697
|
+
/** `true` when a bundled brand icon exists for `kind`. */
|
|
24698
|
+
function hasIcon(kind) {
|
|
24699
|
+
return ICON_KINDS.has(kind);
|
|
24700
|
+
}
|
|
24701
|
+
/**
|
|
24702
|
+
* The stable per-kind icon URL, or `undefined` when no icon is bundled. Stamped
|
|
24703
|
+
* onto each descriptor by `listTargetKinds`. Root-relative so it resolves
|
|
24704
|
+
* against whatever origin the admin UI is served from.
|
|
24705
|
+
*/
|
|
24706
|
+
function iconUrlFor(kind) {
|
|
24707
|
+
return hasIcon(kind) ? `/addon/${NOTIFIERS_ROUTE_ID}/icons/${kind}` : void 0;
|
|
24708
|
+
}
|
|
24709
|
+
/** Pre-encode one bundled icon into every content coding. */
|
|
24710
|
+
function encodeIcon(icon) {
|
|
24711
|
+
const identity = typeof icon.body === "string" ? Buffer.from(icon.body, "utf8") : Buffer.from(icon.body);
|
|
24712
|
+
return {
|
|
24713
|
+
contentType: icon.contentType,
|
|
24714
|
+
identity,
|
|
24715
|
+
br: (0, node_zlib.brotliCompressSync)(identity),
|
|
24716
|
+
gzip: (0, node_zlib.gzipSync)(identity),
|
|
24717
|
+
deflate: (0, node_zlib.deflateSync)(identity)
|
|
24718
|
+
};
|
|
24719
|
+
}
|
|
24720
|
+
/** Pick the best coding the client accepts (br › gzip › deflate), or `null` for
|
|
24721
|
+
* identity when the header names none of them. Token-matched so `gzipx` or a
|
|
24722
|
+
* `q=0` refusal don't false-positive. */
|
|
24723
|
+
function negotiateEncoding(acceptEncoding) {
|
|
24724
|
+
const tokens = acceptEncoding.toLowerCase().split(",").map((part) => part.trim().split(";")[0]?.trim());
|
|
24725
|
+
for (const coding of [
|
|
24726
|
+
"br",
|
|
24727
|
+
"gzip",
|
|
24728
|
+
"deflate"
|
|
24729
|
+
]) if (tokens.includes(coding)) return coding;
|
|
24730
|
+
return null;
|
|
24731
|
+
}
|
|
24732
|
+
/** One static GET route serving the bundled icon for a single kind, with the
|
|
24733
|
+
* icon's own content-type (svg vs png) and a client-negotiated content-coding
|
|
24734
|
+
* the addon owns end-to-end (see the file header for why). */
|
|
24735
|
+
function iconRoute(kind) {
|
|
24736
|
+
const encoded = encodeIcon(NOTIFIER_ICONS[kind]);
|
|
24737
|
+
return {
|
|
24738
|
+
method: "GET",
|
|
24739
|
+
path: `/icons/${kind}`,
|
|
24740
|
+
access: "public",
|
|
24741
|
+
description: `Official brand icon (${encoded.contentType}) for the '${kind}' notifier kind.`,
|
|
24742
|
+
handler: async (request, reply) => {
|
|
24743
|
+
const coding = negotiateEncoding(String(request.headers["accept-encoding"] ?? ""));
|
|
24744
|
+
reply.code(200).type(encoded.contentType).header("cache-control", CACHE_CONTROL).header("vary", "accept-encoding");
|
|
24745
|
+
if (coding !== null) reply.header("content-encoding", coding);
|
|
24746
|
+
reply.send(coding !== null ? encoded[coding] : encoded.identity);
|
|
24747
|
+
}
|
|
24748
|
+
};
|
|
24749
|
+
}
|
|
24750
|
+
/** Build the `addon-routes` provider that serves the bundled icons. */
|
|
24751
|
+
function createIconRouteProvider() {
|
|
24752
|
+
return buildAddonRouteProvider(NOTIFIERS_ROUTE_ID, Object.keys(NOTIFIER_ICONS).map(iconRoute));
|
|
24753
|
+
}
|
|
23446
24754
|
//#endregion
|
|
23447
24755
|
//#region src/adapters/discord.ts
|
|
23448
24756
|
/** Discord message `content` hard limit. */
|
|
@@ -25883,11 +27191,19 @@ function createNotificationOutputProvider(deps) {
|
|
|
25883
27191
|
renderedAs: prepared.renderedAs
|
|
25884
27192
|
};
|
|
25885
27193
|
}
|
|
27194
|
+
const iconUrlFor = deps.iconUrlFor;
|
|
25886
27195
|
return {
|
|
25887
|
-
listTargetKinds: async () => deps.adapters.map((a) =>
|
|
25888
|
-
|
|
25889
|
-
|
|
25890
|
-
|
|
27196
|
+
listTargetKinds: async () => deps.adapters.map((a) => {
|
|
27197
|
+
const descriptor = {
|
|
27198
|
+
...a.descriptor,
|
|
27199
|
+
addonId
|
|
27200
|
+
};
|
|
27201
|
+
const iconUrl = iconUrlFor?.(a.descriptor.kind);
|
|
27202
|
+
return iconUrl !== void 0 ? {
|
|
27203
|
+
...descriptor,
|
|
27204
|
+
iconUrl
|
|
27205
|
+
} : descriptor;
|
|
27206
|
+
}),
|
|
25891
27207
|
listTargets: async () => {
|
|
25892
27208
|
return (await store.list()).map((target) => {
|
|
25893
27209
|
const adapter = adapterByKind.get(target.kind);
|
|
@@ -25984,6 +27300,12 @@ function createApiSettingsStorePort(api) {
|
|
|
25984
27300
|
collection,
|
|
25985
27301
|
key: id
|
|
25986
27302
|
});
|
|
27303
|
+
},
|
|
27304
|
+
declareCollection: async (collection, columns) => {
|
|
27305
|
+
await api.settingsStore.declareCollection.mutate({
|
|
27306
|
+
collection,
|
|
27307
|
+
columns: columns.map((c) => ({ ...c }))
|
|
27308
|
+
});
|
|
25987
27309
|
}
|
|
25988
27310
|
};
|
|
25989
27311
|
}
|
|
@@ -26014,6 +27336,9 @@ function createMemorySettingsStorePort() {
|
|
|
26014
27336
|
},
|
|
26015
27337
|
remove: async (collection, id) => {
|
|
26016
27338
|
bucket(collection).delete(id);
|
|
27339
|
+
},
|
|
27340
|
+
declareCollection: async (collection) => {
|
|
27341
|
+
bucket(collection);
|
|
26017
27342
|
}
|
|
26018
27343
|
};
|
|
26019
27344
|
}
|
|
@@ -26034,6 +27359,13 @@ function createMemorySettingsStorePort() {
|
|
|
26034
27359
|
*/
|
|
26035
27360
|
/** The default collection name (see `settings-store` addonId-prefix note). */
|
|
26036
27361
|
var TARGETS_COLLECTION = "notification-targets";
|
|
27362
|
+
/** KV JSON-blob shape — a single `data` column routes the row through the
|
|
27363
|
+
* settings backend's canonical key/value path (id TEXT PK, data TEXT). */
|
|
27364
|
+
var KV_BLOB_COLUMNS = [{
|
|
27365
|
+
name: "data",
|
|
27366
|
+
type: "TEXT",
|
|
27367
|
+
notNull: true
|
|
27368
|
+
}];
|
|
26037
27369
|
function rowToTarget(row) {
|
|
26038
27370
|
const candidate = {
|
|
26039
27371
|
id: row.id,
|
|
@@ -26058,6 +27390,12 @@ var TargetStore = class {
|
|
|
26058
27390
|
this.store = store;
|
|
26059
27391
|
this.collection = collection;
|
|
26060
27392
|
}
|
|
27393
|
+
/** Declare the backing collection before any read/write. The SQLite settings
|
|
27394
|
+
* backend rejects undeclared collections (fail-fast), so this MUST run before
|
|
27395
|
+
* `list`/`getById`/`upsert`/`delete` — mirrors addon-ai's ProfileStore.init. */
|
|
27396
|
+
async init() {
|
|
27397
|
+
await this.store.declareCollection(this.collection, KV_BLOB_COLUMNS);
|
|
27398
|
+
}
|
|
26061
27399
|
async list() {
|
|
26062
27400
|
const rows = await this.store.query(this.collection);
|
|
26063
27401
|
const targets = [];
|
|
@@ -26129,15 +27467,22 @@ var NotifiersAddon = class extends BaseAddon {
|
|
|
26129
27467
|
const api = this.ctx.api;
|
|
26130
27468
|
const port = api ? createApiSettingsStorePort(api) : createMemorySettingsStorePort();
|
|
26131
27469
|
if (!api) this.ctx.logger.warn("notifiers: no ctx.api — targets persist in-memory only");
|
|
27470
|
+
const store = new TargetStore(port);
|
|
27471
|
+
await store.init();
|
|
26132
27472
|
const provider = createNotificationOutputProvider({
|
|
26133
|
-
store
|
|
27473
|
+
store,
|
|
26134
27474
|
adapters: ADAPTERS,
|
|
26135
|
-
logger: this.ctx.logger.child("notification-output")
|
|
27475
|
+
logger: this.ctx.logger.child("notification-output"),
|
|
27476
|
+
iconUrlFor
|
|
26136
27477
|
});
|
|
27478
|
+
const iconRouteProvider = createIconRouteProvider();
|
|
26137
27479
|
this.ctx.logger.info("NotifiersAddon initialized", { meta: { adapterCount: ADAPTERS.length } });
|
|
26138
27480
|
return [{
|
|
26139
27481
|
capability: notificationOutputCapability,
|
|
26140
27482
|
provider
|
|
27483
|
+
}, {
|
|
27484
|
+
capability: addonRoutesCapability,
|
|
27485
|
+
provider: iconRouteProvider
|
|
26141
27486
|
}];
|
|
26142
27487
|
}
|
|
26143
27488
|
async onShutdown() {
|