@camstack/addon-provider-amcrest 0.2.120 → 0.2.121
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 +877 -117
- package/dist/addon.mjs +877 -117
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -5373,7 +5373,7 @@ var ZodIssueCode = {
|
|
|
5373
5373
|
var ZodFirstPartyTypeKind;
|
|
5374
5374
|
ZodFirstPartyTypeKind || (ZodFirstPartyTypeKind = {});
|
|
5375
5375
|
//#endregion
|
|
5376
|
-
//#region ../types/dist/sleep-
|
|
5376
|
+
//#region ../types/dist/sleep-D_4W7xuQ.mjs
|
|
5377
5377
|
/**
|
|
5378
5378
|
* The audio chunk plane's byte format, and the ONE expansion from a coded
|
|
5379
5379
|
* window to float samples (D455).
|
|
@@ -7354,11 +7354,17 @@ var RawStateResultSchema = object({
|
|
|
7354
7354
|
method(object({ deviceId: number() }), array(StreamSourceEntrySchema)), method(object({ deviceId: number() }), array(ConfigEntrySchema$1)), method(object({
|
|
7355
7355
|
deviceId: number(),
|
|
7356
7356
|
values: record(string(), unknown())
|
|
7357
|
-
}), _void(), {
|
|
7357
|
+
}), _void(), {
|
|
7358
|
+
kind: "mutation",
|
|
7359
|
+
auth: "admin"
|
|
7360
|
+
}), method(object({
|
|
7358
7361
|
deviceId: number(),
|
|
7359
7362
|
action: string().min(1),
|
|
7360
7363
|
input: unknown()
|
|
7361
|
-
}), unknown(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), {
|
|
7364
|
+
}), unknown(), { kind: "mutation" }), method(object({ deviceId: number() }), _void(), {
|
|
7365
|
+
kind: "mutation",
|
|
7366
|
+
auth: "admin"
|
|
7367
|
+
}), method(object({ deviceId: number() }), unknown().nullable()), method(object({ deviceId: number() }), RawStateResultSchema.nullable(), { auth: "protected" });
|
|
7362
7368
|
new Set([
|
|
7363
7369
|
"track",
|
|
7364
7370
|
"summary",
|
|
@@ -11582,7 +11588,7 @@ method(ListInputSchema, array(BrokerInfoSchema$1)), method(GetInputSchema, Broke
|
|
|
11582
11588
|
}), method(UnsubscribeInputSchema, _void(), {
|
|
11583
11589
|
kind: "mutation",
|
|
11584
11590
|
auth: "admin"
|
|
11585
|
-
}), method(GetStateInputSchema, unknown().nullable()), method(_void(), RegistryStatusSchema);
|
|
11591
|
+
}), method(GetStateInputSchema, unknown().nullable(), { auth: "admin" }), method(_void(), RegistryStatusSchema);
|
|
11586
11592
|
DeviceType.Camera;
|
|
11587
11593
|
/** Stream delivery format. (Relocated from the retired `streaming-engine` cap.) */
|
|
11588
11594
|
var StreamFormatSchema = _enum([
|
|
@@ -12765,17 +12771,20 @@ method(object({
|
|
|
12765
12771
|
namespace: string().optional(),
|
|
12766
12772
|
collection: string(),
|
|
12767
12773
|
key: string()
|
|
12768
|
-
}), unknown()), method(object({
|
|
12774
|
+
}), unknown(), { auth: "admin" }), method(object({
|
|
12769
12775
|
namespace: string().optional(),
|
|
12770
12776
|
collection: string(),
|
|
12771
12777
|
key: string(),
|
|
12772
12778
|
value: unknown()
|
|
12773
|
-
}), _void(), {
|
|
12779
|
+
}), _void(), {
|
|
12780
|
+
kind: "mutation",
|
|
12781
|
+
auth: "admin"
|
|
12782
|
+
}), method(object({
|
|
12774
12783
|
namespace: string().optional(),
|
|
12775
12784
|
collection: string(),
|
|
12776
12785
|
filter: QueryFilterSchema.optional(),
|
|
12777
12786
|
columns: array(string()).readonly().optional()
|
|
12778
|
-
}), array(SettingsRecordSchema).readonly()), method(object({
|
|
12787
|
+
}), array(SettingsRecordSchema).readonly(), { auth: "admin" }), method(object({
|
|
12779
12788
|
namespace: string().optional(),
|
|
12780
12789
|
collection: string(),
|
|
12781
12790
|
/** Declared columns identifying the slot; must be covered by a UNIQUE
|
|
@@ -12785,7 +12794,10 @@ method(object({
|
|
|
12785
12794
|
}), object({
|
|
12786
12795
|
/** The id the row HAS: assigned by SQLite on an insert, or the existing
|
|
12787
12796
|
* row's own id on an update. */
|
|
12788
|
-
id: union([string(), number()]) }), {
|
|
12797
|
+
id: union([string(), number()]) }), {
|
|
12798
|
+
kind: "mutation",
|
|
12799
|
+
auth: "admin"
|
|
12800
|
+
}), method(object({
|
|
12789
12801
|
namespace: string().optional(),
|
|
12790
12802
|
collection: string(),
|
|
12791
12803
|
record: object({
|
|
@@ -12798,38 +12810,56 @@ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
|
|
|
12798
12810
|
* for an absent one, or the ROWID SQLite assigned on an `INTEGER`
|
|
12799
12811
|
* primary key — which is the only place an auto key is knowable.
|
|
12800
12812
|
*/
|
|
12801
|
-
id: union([string(), number()]) }), {
|
|
12813
|
+
id: union([string(), number()]) }), {
|
|
12814
|
+
kind: "mutation",
|
|
12815
|
+
auth: "admin"
|
|
12816
|
+
}), method(object({
|
|
12802
12817
|
namespace: string().optional(),
|
|
12803
12818
|
collection: string(),
|
|
12804
12819
|
records: array(BulkRecordSchema).readonly()
|
|
12805
|
-
}), object({ inserted: number().int() }), {
|
|
12820
|
+
}), object({ inserted: number().int() }), {
|
|
12821
|
+
kind: "mutation",
|
|
12822
|
+
auth: "admin"
|
|
12823
|
+
}), method(object({
|
|
12806
12824
|
namespace: string().optional(),
|
|
12807
12825
|
collection: string(),
|
|
12808
12826
|
id: string(),
|
|
12809
12827
|
data: record(string(), unknown())
|
|
12810
|
-
}), _void(), {
|
|
12828
|
+
}), _void(), {
|
|
12829
|
+
kind: "mutation",
|
|
12830
|
+
auth: "admin"
|
|
12831
|
+
}), method(object({
|
|
12811
12832
|
namespace: string().optional(),
|
|
12812
12833
|
collection: string(),
|
|
12813
12834
|
key: string()
|
|
12814
|
-
}), _void(), {
|
|
12835
|
+
}), _void(), {
|
|
12836
|
+
kind: "mutation",
|
|
12837
|
+
auth: "admin"
|
|
12838
|
+
}), method(object({
|
|
12815
12839
|
namespace: string().optional(),
|
|
12816
12840
|
collection: string(),
|
|
12817
12841
|
filter: MutationFilterSchema
|
|
12818
|
-
}), object({ deleted: number().int() }), {
|
|
12842
|
+
}), object({ deleted: number().int() }), {
|
|
12843
|
+
kind: "mutation",
|
|
12844
|
+
auth: "admin"
|
|
12845
|
+
}), method(object({
|
|
12819
12846
|
namespace: string().optional(),
|
|
12820
12847
|
collection: string(),
|
|
12821
12848
|
filter: MutationFilterSchema,
|
|
12822
12849
|
data: record(string(), unknown())
|
|
12823
|
-
}), object({ updated: number().int() }), {
|
|
12850
|
+
}), object({ updated: number().int() }), {
|
|
12851
|
+
kind: "mutation",
|
|
12852
|
+
auth: "admin"
|
|
12853
|
+
}), method(object({
|
|
12824
12854
|
namespace: string().optional(),
|
|
12825
12855
|
collection: string(),
|
|
12826
12856
|
filter: QueryFilterSchema.optional()
|
|
12827
|
-
}), number()), method(object({
|
|
12857
|
+
}), number(), { auth: "admin" }), method(object({
|
|
12828
12858
|
namespace: string().optional(),
|
|
12829
12859
|
collection: string(),
|
|
12830
12860
|
fields: array(AggregateFieldSchema).readonly(),
|
|
12831
12861
|
filter: QueryFilterSchema.optional()
|
|
12832
|
-
}), AggregateResultSchema), method(object({
|
|
12862
|
+
}), AggregateResultSchema, { auth: "admin" }), method(object({
|
|
12833
12863
|
namespace: string().optional(),
|
|
12834
12864
|
collection: string(),
|
|
12835
12865
|
field: string(),
|
|
@@ -12839,15 +12869,18 @@ id: union([string(), number()]) }), { kind: "mutation" }), method(object({
|
|
|
12839
12869
|
}), array(object({
|
|
12840
12870
|
bucket: number().int(),
|
|
12841
12871
|
count: number().int()
|
|
12842
|
-
})).readonly()), method(object({
|
|
12872
|
+
})).readonly(), { auth: "admin" }), method(object({
|
|
12843
12873
|
namespace: string().optional(),
|
|
12844
12874
|
collection: string()
|
|
12845
|
-
}), boolean()), method(object({
|
|
12875
|
+
}), boolean(), { auth: "admin" }), method(object({
|
|
12846
12876
|
namespace: string().optional(),
|
|
12847
12877
|
collection: string(),
|
|
12848
12878
|
columns: array(CollectionColumnSchema).readonly(),
|
|
12849
12879
|
indexes: array(CollectionIndexSchema).readonly().optional()
|
|
12850
|
-
}), _void(), {
|
|
12880
|
+
}), _void(), {
|
|
12881
|
+
kind: "mutation",
|
|
12882
|
+
auth: "admin"
|
|
12883
|
+
});
|
|
12851
12884
|
/**
|
|
12852
12885
|
* What one engine says about itself. The orchestrator uses `kind` to pick
|
|
12853
12886
|
* a registrant for a collection; `engineId` is what a log line names when
|
|
@@ -13751,7 +13784,13 @@ var ExposeInputSchema = object({
|
|
|
13751
13784
|
capabilities: array(string()).optional()
|
|
13752
13785
|
});
|
|
13753
13786
|
var UnexposeInputSchema = object({ deviceId: string() });
|
|
13754
|
-
method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchema)), method(_void(), array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
|
|
13787
|
+
method(_void(), DeviceExportStatusSchema), method(_void(), array(DeviceKindSchema)), method(_void(), array(ExposedDeviceSchema)), method(ExposeInputSchema, _void(), {
|
|
13788
|
+
kind: "mutation",
|
|
13789
|
+
auth: "admin"
|
|
13790
|
+
}), method(UnexposeInputSchema, _void(), {
|
|
13791
|
+
kind: "mutation",
|
|
13792
|
+
auth: "admin"
|
|
13793
|
+
});
|
|
13755
13794
|
/** Wire/storage schema for one icon id. */
|
|
13756
13795
|
var LocationIconIdSchema = _enum([
|
|
13757
13796
|
"map-pin",
|
|
@@ -14336,7 +14375,10 @@ method(object({
|
|
|
14336
14375
|
kind: "mutation",
|
|
14337
14376
|
auth: "admin",
|
|
14338
14377
|
timeoutMs: 12 * 6e4
|
|
14339
|
-
}), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), {
|
|
14378
|
+
}), method(DeviceRegisterPayloadSchema, _void(), { kind: "mutation" }), method(DeviceRemovePayloadSchema, _void(), { kind: "mutation" }), method(DevicePersistConfigPayloadSchema, _void(), {
|
|
14379
|
+
kind: "mutation",
|
|
14380
|
+
auth: "admin"
|
|
14381
|
+
}), method(object({ deviceId: number() }), record(string(), unknown()), { auth: "admin" }), method(object({ deviceId: number() }), record(string(), unknown()), { auth: "admin" }), method(object({ deviceId: number() }), DeviceMetaSchema.nullable()), method(object({
|
|
14340
14382
|
deviceId: number(),
|
|
14341
14383
|
name: string()
|
|
14342
14384
|
}), _void(), {
|
|
@@ -14541,7 +14583,7 @@ method(object({
|
|
|
14541
14583
|
}), method(object({ deviceId: number() }), array(StreamProbeResultSchema), {
|
|
14542
14584
|
kind: "mutation",
|
|
14543
14585
|
auth: "admin"
|
|
14544
|
-
}), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({
|
|
14586
|
+
}), method(object({ deviceId: number() }), DeviceBindingsForDeviceSchema), method(object({ deviceIds: array(number()) }), array(DeviceBindingsForDeviceSchema)), method(object({}), array(DeviceBindingsForDeviceSchema)), method(object({ capNames: array(string().min(1)).min(1).max(16) }), array(number())), method(object({
|
|
14545
14587
|
deviceId: number(),
|
|
14546
14588
|
capName: string(),
|
|
14547
14589
|
wrapperAddonId: string(),
|
|
@@ -14896,18 +14938,30 @@ var LlmErrorCodeSchema = _enum([
|
|
|
14896
14938
|
"budget-exceeded",
|
|
14897
14939
|
"adapter-error"
|
|
14898
14940
|
]);
|
|
14899
|
-
var
|
|
14941
|
+
var LlmGenerateOkSchema = object({
|
|
14900
14942
|
ok: literal(true),
|
|
14901
14943
|
text: string(),
|
|
14902
14944
|
model: string(),
|
|
14903
14945
|
usage: LlmUsageSchema,
|
|
14904
14946
|
truncated: boolean(),
|
|
14905
14947
|
latencyMs: number()
|
|
14906
|
-
})
|
|
14948
|
+
});
|
|
14949
|
+
/**
|
|
14950
|
+
* WHO refused, on a `refusal`. `guardrail` = a safety filter in front of the
|
|
14951
|
+
* model rejected the request before it answered (Apple Foundation Models
|
|
14952
|
+
* refuses surveillance-framed prompts in 0.1 s); `model` = the model itself
|
|
14953
|
+
* declined. They are different faults: a guardrail refusal is a property of
|
|
14954
|
+
* the BACKEND, so the provider falls back to the next profile; a model refusal
|
|
14955
|
+
* is an answer. Absent on every other code, and on a refusal a wire cannot
|
|
14956
|
+
* attribute.
|
|
14957
|
+
*/
|
|
14958
|
+
var LlmRefusalSourceSchema = _enum(["guardrail", "model"]);
|
|
14959
|
+
var LlmGenerateResultSchema = discriminatedUnion("ok", [LlmGenerateOkSchema, object({
|
|
14907
14960
|
ok: literal(false),
|
|
14908
14961
|
code: LlmErrorCodeSchema,
|
|
14909
14962
|
message: string(),
|
|
14910
|
-
retryAfterMs: number().optional()
|
|
14963
|
+
retryAfterMs: number().optional(),
|
|
14964
|
+
refusalSource: LlmRefusalSourceSchema.optional()
|
|
14911
14965
|
})]);
|
|
14912
14966
|
/**
|
|
14913
14967
|
* `Uint8Array` is the sanctioned binary convention — superjson + the UDS
|
|
@@ -15137,12 +15191,78 @@ var LlmRuntimeDiskUsageSchema = object({
|
|
|
15137
15191
|
modelsBytes: number(),
|
|
15138
15192
|
freeBytes: number().optional()
|
|
15139
15193
|
});
|
|
15140
|
-
|
|
15194
|
+
var LlmRuntimeCompleteInputSchema = LlmGenerateBaseInputSchema.extend({
|
|
15141
15195
|
images: array(LlmImageSchema).optional(),
|
|
15142
15196
|
runtime: ManagedRuntimeConfigSchema,
|
|
15143
15197
|
/** The managed profile's timeout, threaded by the hub provider. */
|
|
15144
15198
|
timeoutMs: number().int().positive().optional()
|
|
15145
|
-
})
|
|
15199
|
+
});
|
|
15200
|
+
/**
|
|
15201
|
+
* Why Apple Foundation Models cannot run on a node that has it in principle.
|
|
15202
|
+
* The first three are Apple's own `UnavailableReason`s, verbatim; the rest are
|
|
15203
|
+
* ours: the OS predates the framework (macOS < 26), the helper binary is not
|
|
15204
|
+
* in the installed addon, or it crashed past the respawn bound.
|
|
15205
|
+
*/
|
|
15206
|
+
var AppleFmUnavailableReasonSchema = _enum([
|
|
15207
|
+
"deviceNotEligible",
|
|
15208
|
+
"appleIntelligenceNotEnabled",
|
|
15209
|
+
"modelNotReady",
|
|
15210
|
+
"osTooOld",
|
|
15211
|
+
"helperMissing",
|
|
15212
|
+
"helperCrashed"
|
|
15213
|
+
]);
|
|
15214
|
+
/**
|
|
15215
|
+
* Apple Foundation Models on ONE node — four states, never a boolean (D315).
|
|
15216
|
+
*
|
|
15217
|
+
* - `available` — the helper probed it; what it CAN do is reported
|
|
15218
|
+
* here (`imageInput`, `contextSize`, languages),
|
|
15219
|
+
* never typed by an operator.
|
|
15220
|
+
* - `unavailable` — a known absence, with the reason to act on.
|
|
15221
|
+
* - `unsupported-platform` — not a Mac; there is nothing to enable.
|
|
15222
|
+
* - `unknown` — the probe did not answer (not yet run, timed
|
|
15223
|
+
* out, helper crashed mid-probe). A failed
|
|
15224
|
+
* measurement is NEVER `unavailable` (D393).
|
|
15225
|
+
*/
|
|
15226
|
+
var AppleFmAvailabilitySchema = discriminatedUnion("state", [
|
|
15227
|
+
object({
|
|
15228
|
+
state: literal("available"),
|
|
15229
|
+
osVersion: string(),
|
|
15230
|
+
/** Tokens, as the framework reports it. Read at runtime, never assumed. */
|
|
15231
|
+
contextSize: number().int().positive(),
|
|
15232
|
+
/** BCP-47-ish tags (`it-IT`) of the languages the model supports. */
|
|
15233
|
+
languages: array(string()),
|
|
15234
|
+
/** Whether prompts may carry images. `false` on a macOS 26 build. */
|
|
15235
|
+
imageInput: boolean()
|
|
15236
|
+
}),
|
|
15237
|
+
object({
|
|
15238
|
+
state: literal("unavailable"),
|
|
15239
|
+
reason: AppleFmUnavailableReasonSchema,
|
|
15240
|
+
osVersion: string().nullable(),
|
|
15241
|
+
detail: string()
|
|
15242
|
+
}),
|
|
15243
|
+
object({
|
|
15244
|
+
state: literal("unsupported-platform"),
|
|
15245
|
+
platform: string()
|
|
15246
|
+
}),
|
|
15247
|
+
object({
|
|
15248
|
+
state: literal("unknown"),
|
|
15249
|
+
error: string()
|
|
15250
|
+
})
|
|
15251
|
+
]);
|
|
15252
|
+
/**
|
|
15253
|
+
* A generation on the node's Apple Foundation Models helper. A separate verb
|
|
15254
|
+
* from `complete`, whose `runtime` is a llama.cpp config: overloading it would
|
|
15255
|
+
* drag a GGUF model reference into a backend that has none. `images` is part
|
|
15256
|
+
* of the contract so vision needs no cap change later; a node whose model has
|
|
15257
|
+
* no image input REFUSES them (`unavailable`), never drops them.
|
|
15258
|
+
*/
|
|
15259
|
+
var LlmRuntimeAppleCompleteInputSchema = LlmGenerateBaseInputSchema.extend({
|
|
15260
|
+
images: array(LlmImageSchema).optional(),
|
|
15261
|
+
/** The profile's timeout, threaded by the hub provider. */
|
|
15262
|
+
timeoutMs: number().int().positive().optional()
|
|
15263
|
+
});
|
|
15264
|
+
var LlmRuntimeCancelInputSchema = object({ requestId: string() });
|
|
15265
|
+
method(LlmRuntimeCompleteInputSchema, LlmGenerateResultSchema, {
|
|
15146
15266
|
kind: "mutation",
|
|
15147
15267
|
auth: "admin",
|
|
15148
15268
|
timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
|
|
@@ -15160,7 +15280,14 @@ method(LlmGenerateBaseInputSchema.extend({
|
|
|
15160
15280
|
}), method(object({ file: string() }), _void(), {
|
|
15161
15281
|
kind: "mutation",
|
|
15162
15282
|
auth: "admin"
|
|
15163
|
-
}), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" })
|
|
15283
|
+
}), method(object({}), array(LlmNodeModelSchema), { auth: "admin" }), method(object({}), LlmRuntimeDiskUsageSchema, { auth: "admin" }), method(LlmRuntimeCancelInputSchema, _void(), {
|
|
15284
|
+
kind: "mutation",
|
|
15285
|
+
auth: "admin"
|
|
15286
|
+
}), method(object({}), AppleFmAvailabilitySchema, { auth: "admin" }), method(LlmRuntimeAppleCompleteInputSchema, LlmGenerateResultSchema, {
|
|
15287
|
+
kind: "mutation",
|
|
15288
|
+
auth: "admin",
|
|
15289
|
+
timeoutMs: AI_CAP_TRANSPORT_TIMEOUT_MS
|
|
15290
|
+
});
|
|
15164
15291
|
/**
|
|
15165
15292
|
* `llm` — consumer-facing LLM surface (spec §1-§3). Collection-mode: array
|
|
15166
15293
|
* methods concat-fan across providers; single-row methods route to ONE
|
|
@@ -15177,7 +15304,8 @@ var LlmProfileKindSchema = _enum([
|
|
|
15177
15304
|
"openai",
|
|
15178
15305
|
"anthropic",
|
|
15179
15306
|
"google",
|
|
15180
|
-
"managed-local"
|
|
15307
|
+
"managed-local",
|
|
15308
|
+
"apple-foundation"
|
|
15181
15309
|
]);
|
|
15182
15310
|
var LlmProfileSchema = object({
|
|
15183
15311
|
id: string(),
|
|
@@ -15235,7 +15363,14 @@ var LlmProfileSchema = object({
|
|
|
15235
15363
|
toolsEnabled: boolean().default(false),
|
|
15236
15364
|
extraHeaders: record(string(), string()).optional(),
|
|
15237
15365
|
/** kind === 'managed-local' only (spec §4). */
|
|
15238
|
-
runtime: ManagedRuntimeConfigSchema.optional()
|
|
15366
|
+
runtime: ManagedRuntimeConfigSchema.optional(),
|
|
15367
|
+
/**
|
|
15368
|
+
* kind === 'apple-foundation' only: WHERE it runs — the only thing there is
|
|
15369
|
+
* to configure (no key, no URL, no download; `model` is `apple-system`).
|
|
15370
|
+
* `supportsVision` on such a row is stamped by the provider from the node's
|
|
15371
|
+
* reported `imageInput`, never taken from the operator (D62).
|
|
15372
|
+
*/
|
|
15373
|
+
appleFoundation: object({ nodeId: string() }).optional()
|
|
15239
15374
|
});
|
|
15240
15375
|
/** ConfigUISchema tree passed through untyped on the wire (the
|
|
15241
15376
|
* notification-output `ConfigSchemaPassthrough` precedent at
|
|
@@ -15317,7 +15452,9 @@ var LlmRuntimeNodeSchema = object({
|
|
|
15317
15452
|
reachable: boolean(),
|
|
15318
15453
|
status: LlmRuntimeStatusSchema.optional(),
|
|
15319
15454
|
disk: LlmRuntimeDiskUsageSchema.optional(),
|
|
15320
|
-
error: string().optional()
|
|
15455
|
+
error: string().optional(),
|
|
15456
|
+
/** Absent = the node did not answer the question (render as unknown). */
|
|
15457
|
+
apple: AppleFmAvailabilitySchema.optional()
|
|
15321
15458
|
});
|
|
15322
15459
|
var GenerateVisionInputSchema = LlmGenerateBaseInputSchema.extend({ images: array(LlmImageSchema).min(1) });
|
|
15323
15460
|
var ProfileRefInputSchema = object({
|
|
@@ -16088,7 +16225,22 @@ var StatusSchema = object({
|
|
|
16088
16225
|
brokerCount: number(),
|
|
16089
16226
|
embeddedRunning: boolean()
|
|
16090
16227
|
});
|
|
16091
|
-
method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema
|
|
16228
|
+
method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema, { auth: "admin" }), method(AddBrokerInputSchema, AddBrokerResultSchema, {
|
|
16229
|
+
kind: "mutation",
|
|
16230
|
+
auth: "admin"
|
|
16231
|
+
}), method(IdInputSchema, _void(), {
|
|
16232
|
+
kind: "mutation",
|
|
16233
|
+
auth: "admin"
|
|
16234
|
+
}), method(IdInputSchema, TestResultSchema$1, {
|
|
16235
|
+
kind: "mutation",
|
|
16236
|
+
auth: "admin"
|
|
16237
|
+
}), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, {
|
|
16238
|
+
kind: "mutation",
|
|
16239
|
+
auth: "admin"
|
|
16240
|
+
}), method(IdInputSchema, _void(), {
|
|
16241
|
+
kind: "mutation",
|
|
16242
|
+
auth: "admin"
|
|
16243
|
+
}), method(_void(), StatusSchema);
|
|
16092
16244
|
var NetworkEndpointSchema = object({
|
|
16093
16245
|
url: string(),
|
|
16094
16246
|
hostname: string(),
|
|
@@ -16383,6 +16535,18 @@ var TargetKindSchema = object({
|
|
|
16383
16535
|
*/
|
|
16384
16536
|
iconMediaType: string().optional(),
|
|
16385
16537
|
configSchema: ConfigSchemaPassthrough,
|
|
16538
|
+
/**
|
|
16539
|
+
* The config keys that decide WHERE this kind's secrets are sent (server,
|
|
16540
|
+
* base URL, instance, chat, recipient). A save that changes any of them
|
|
16541
|
+
* while leaving a secret redacted is refused by the provider (D634), so an
|
|
16542
|
+
* editor uses this to ask for the secret on exactly those edits instead of
|
|
16543
|
+
* on every edit.
|
|
16544
|
+
*
|
|
16545
|
+
* Optional: absent from a provider that holds no secret (the Home Assistant
|
|
16546
|
+
* kinds) and from a provider older than this field — an editor that finds
|
|
16547
|
+
* it absent must assume ANY edit may need the secret.
|
|
16548
|
+
*/
|
|
16549
|
+
destinationKeys: array(string()).optional(),
|
|
16386
16550
|
supportsDiscovery: boolean(),
|
|
16387
16551
|
caps: TargetKindCapsSchema
|
|
16388
16552
|
});
|
|
@@ -16421,19 +16585,34 @@ var SendResultSchema = object({
|
|
|
16421
16585
|
});
|
|
16422
16586
|
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
16423
16587
|
var TestResultSchema = SendResultSchema;
|
|
16424
|
-
method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema)), method(object({
|
|
16588
|
+
method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema), { auth: "admin" }), method(object({
|
|
16425
16589
|
kind: string(),
|
|
16426
16590
|
config: record(string(), unknown()).optional()
|
|
16427
|
-
}), array(DiscoveredTargetSchema)), method(object({
|
|
16591
|
+
}), array(DiscoveredTargetSchema), { auth: "admin" }), method(object({
|
|
16428
16592
|
targetId: string(),
|
|
16429
16593
|
notification: NotificationSchema
|
|
16430
|
-
}), SendResultSchema, {
|
|
16594
|
+
}), SendResultSchema, {
|
|
16595
|
+
kind: "mutation",
|
|
16596
|
+
auth: "admin"
|
|
16597
|
+
}), method(object({
|
|
16431
16598
|
targetId: string(),
|
|
16432
16599
|
sample: NotificationSchema.optional()
|
|
16433
|
-
}), TestResultSchema, {
|
|
16600
|
+
}), TestResultSchema, {
|
|
16601
|
+
kind: "mutation",
|
|
16602
|
+
auth: "admin"
|
|
16603
|
+
}), method(object({ target: TargetSchema }), TargetSchema, {
|
|
16604
|
+
kind: "mutation",
|
|
16605
|
+
auth: "admin"
|
|
16606
|
+
}), method(object({ targetId: string() }), _void(), {
|
|
16607
|
+
kind: "mutation",
|
|
16608
|
+
auth: "admin"
|
|
16609
|
+
}), method(object({
|
|
16434
16610
|
targetId: string(),
|
|
16435
16611
|
enabled: boolean()
|
|
16436
|
-
}), _void(), {
|
|
16612
|
+
}), _void(), {
|
|
16613
|
+
kind: "mutation",
|
|
16614
|
+
auth: "admin"
|
|
16615
|
+
});
|
|
16437
16616
|
new Set([
|
|
16438
16617
|
{
|
|
16439
16618
|
id: "person",
|
|
@@ -16954,6 +17133,7 @@ sub("presence", "sensor", "sensor", "#22c55e", "presence", "Presence");
|
|
|
16954
17133
|
sub("enum-sensor", "sensor", "sensor", TAXONOMY_COLORS.sensor, "generic", "Sensor state");
|
|
16955
17134
|
sub("device-event", "sensor", "sensor", "#10b981", "button", "Device event");
|
|
16956
17135
|
sub("lock", "control", "control", "#0ea5e9", "lock", "Lock");
|
|
17136
|
+
sub("cover", "control", "control", "#0ea5e9", "door", "Cover");
|
|
16957
17137
|
sub("switch", "control", "control", TAXONOMY_COLORS.control, "switch", "Switch");
|
|
16958
17138
|
sub("siren", "control", "control", "#dc2626", "siren", "Siren");
|
|
16959
17139
|
sub("button", "control", "control", "#10b981", "button", "Button");
|
|
@@ -17272,6 +17452,10 @@ var NcSystemEventKindSchema = _enum([
|
|
|
17272
17452
|
"alarm-disarmed",
|
|
17273
17453
|
"alarm-arming",
|
|
17274
17454
|
"alarm-arm-refused",
|
|
17455
|
+
"alarm-pending",
|
|
17456
|
+
"alarm-rearmed",
|
|
17457
|
+
"alarm-sensor-bypass",
|
|
17458
|
+
"alarm-check-failed",
|
|
17275
17459
|
"addon-updated",
|
|
17276
17460
|
"server-updated",
|
|
17277
17461
|
"export-completed",
|
|
@@ -17309,6 +17493,19 @@ var NcSystemEventConditionSchema = object({
|
|
|
17309
17493
|
* does not carry) matches no `deviceTypes` list.
|
|
17310
17494
|
*/
|
|
17311
17495
|
deviceTypes: array(string().min(1)).min(1).optional(),
|
|
17496
|
+
/**
|
|
17497
|
+
* Devices this rule NEVER speaks about, whatever else matched. Scoped to the
|
|
17498
|
+
* same kind family as `deviceIds` (`systemEventFilterApplies(kind,
|
|
17499
|
+
* 'deviceIds')`) and applied AFTER it: include first, then exclude. A
|
|
17500
|
+
* consumable subject (`consumable:<id>:<key>`) is excluded by its device id.
|
|
17501
|
+
*
|
|
17502
|
+
* It never fails closed: a subject that carries no device id is not in the
|
|
17503
|
+
* list, so it is not excluded. "Every battery except the one thermometer that
|
|
17504
|
+
* reads 0 % forever" is the question this answers — expressed as an include
|
|
17505
|
+
* list it would have to name every other battery device, and every device
|
|
17506
|
+
* adopted later would be silently left out (D636).
|
|
17507
|
+
*/
|
|
17508
|
+
excludeDeviceIds: array(number().int()).min(1).optional(),
|
|
17312
17509
|
nodeIds: array(string().min(1)).min(1).optional(),
|
|
17313
17510
|
packageNames: array(string().min(1)).min(1).optional()
|
|
17314
17511
|
});
|
|
@@ -17867,6 +18064,19 @@ var NcMediaFrameSchema = _enum([
|
|
|
17867
18064
|
"full",
|
|
17868
18065
|
"boxed"
|
|
17869
18066
|
]);
|
|
18067
|
+
/**
|
|
18068
|
+
* How a SENSOR notification shows the cameras that link the sensor.
|
|
18069
|
+
*
|
|
18070
|
+
* `mosaic` — one image composed of every linked camera's photograph, taken at
|
|
18071
|
+
* the trigger. `each` — one image per camera where the target takes several
|
|
18072
|
+
* attachments; a target that takes one keeps the first camera's (the degrade
|
|
18073
|
+
* engine drops the rest by order). Either way ONE notification per event.
|
|
18074
|
+
* Absent = `mosaic`, applied by the Notification Center and deliberately NOT
|
|
18075
|
+
* a Zod default (see NcRulePatchSchema: a default materialises on every
|
|
18076
|
+
* partial patch). Meaningless for a rule whose subject is not a sensor, and a
|
|
18077
|
+
* sensor linked to one camera ships that camera's plain snapshot either way.
|
|
18078
|
+
*/
|
|
18079
|
+
var NcLinkedCameraModeSchema = _enum(["mosaic", "each"]);
|
|
17870
18080
|
var NcMediaPolicySchema = object({
|
|
17871
18081
|
attach: _enum([
|
|
17872
18082
|
"best",
|
|
@@ -17912,7 +18122,9 @@ var NcMediaPolicySchema = object({
|
|
|
17912
18122
|
* A profile that is not assigned falls back to the cheapest, and the render
|
|
17913
18123
|
* reports which one actually ran.
|
|
17914
18124
|
*/
|
|
17915
|
-
profile: CamProfileSchema.optional()
|
|
18125
|
+
profile: CamProfileSchema.optional(),
|
|
18126
|
+
/** See {@link NcLinkedCameraModeSchema}. */
|
|
18127
|
+
linkedCameras: NcLinkedCameraModeSchema.optional()
|
|
17916
18128
|
});
|
|
17917
18129
|
/**
|
|
17918
18130
|
* Cooldown GRANULARITY over the subject's class — how much a fired
|
|
@@ -18032,7 +18244,7 @@ var NcRuleInputSchema = object({
|
|
|
18032
18244
|
cooldownSec: 60,
|
|
18033
18245
|
scope: "rule-device"
|
|
18034
18246
|
}),
|
|
18035
|
-
/** `{{var}}`
|
|
18247
|
+
/** `{{var}}` title/body. The variables are declared in `NC_TEMPLATE_VARS` (@camstack/types) and depend on the rule kind — see `templateVarsFor`. */
|
|
18036
18248
|
template: object({
|
|
18037
18249
|
title: string().max(500).optional(),
|
|
18038
18250
|
body: string().max(2e3).optional()
|
|
@@ -18212,6 +18424,22 @@ var NcRulePatchSchema = NcRuleInputSchema.partial().extend({
|
|
|
18212
18424
|
throttle: NcThrottleSchema.optional(),
|
|
18213
18425
|
priority: number().int().min(1).max(5).optional()
|
|
18214
18426
|
});
|
|
18427
|
+
var NcRuleClearableKeySchema = _enum([
|
|
18428
|
+
"template",
|
|
18429
|
+
"schedule",
|
|
18430
|
+
"targetUsers",
|
|
18431
|
+
"snoozeOptions",
|
|
18432
|
+
"snoozeAllowGlobal",
|
|
18433
|
+
"waitForEnhancement",
|
|
18434
|
+
"actions",
|
|
18435
|
+
"confirm",
|
|
18436
|
+
"groupIdleSec"
|
|
18437
|
+
]);
|
|
18438
|
+
var NcRuleUpdateInputSchema = object({
|
|
18439
|
+
ruleId: string(),
|
|
18440
|
+
patch: NcRulePatchSchema,
|
|
18441
|
+
clear: array(NcRuleClearableKeySchema).optional()
|
|
18442
|
+
});
|
|
18215
18443
|
/** A persisted rule. */
|
|
18216
18444
|
var NcRuleSchema = NcRuleInputSchema.extend({
|
|
18217
18445
|
id: string(),
|
|
@@ -18243,6 +18471,85 @@ var NcTestResultSchema = object({
|
|
|
18243
18471
|
className: string().optional(),
|
|
18244
18472
|
label: string().optional()
|
|
18245
18473
|
});
|
|
18474
|
+
/** The five rule kinds, as data. `NcRuleKind` in `notification/rule-kinds.ts`
|
|
18475
|
+
* is the same union; the template-vars spec pins the two together. */
|
|
18476
|
+
var NcRuleKindSchema = _enum([
|
|
18477
|
+
"detection",
|
|
18478
|
+
"sensor",
|
|
18479
|
+
"occupancy",
|
|
18480
|
+
"sound",
|
|
18481
|
+
"system"
|
|
18482
|
+
]);
|
|
18483
|
+
/** Which producer renders a template: an ordinary rule, a timelapse, a digest. */
|
|
18484
|
+
var NcTemplateFamilySchema = _enum([
|
|
18485
|
+
"rule",
|
|
18486
|
+
"timelapse",
|
|
18487
|
+
"summary"
|
|
18488
|
+
]);
|
|
18489
|
+
/** Which text field of that producer. `previewText` is the timelapse frame
|
|
18490
|
+
* caption, `captionText` the digest mosaic caption — both see fewer vars. */
|
|
18491
|
+
var NcTemplateFieldSchema = _enum([
|
|
18492
|
+
"title",
|
|
18493
|
+
"body",
|
|
18494
|
+
"previewText",
|
|
18495
|
+
"captionText"
|
|
18496
|
+
]);
|
|
18497
|
+
var NcTemplateVarGroupSchema = _enum([
|
|
18498
|
+
"subject",
|
|
18499
|
+
"place",
|
|
18500
|
+
"time",
|
|
18501
|
+
"rule",
|
|
18502
|
+
"occupancy",
|
|
18503
|
+
"sound",
|
|
18504
|
+
"sensor",
|
|
18505
|
+
"system",
|
|
18506
|
+
"digest",
|
|
18507
|
+
"ai"
|
|
18508
|
+
]);
|
|
18509
|
+
/**
|
|
18510
|
+
* ONE `{{var}}` a notification template may name.
|
|
18511
|
+
*
|
|
18512
|
+
* `families` / `fields` / `kinds` / `deliveries` / `systemEventKinds` say WHERE
|
|
18513
|
+
* it has a value; absent = no restriction on that axis. `systemEventKinds` is
|
|
18514
|
+
* read only for a `system` rule. `dynamic` declares a FAMILY of names
|
|
18515
|
+
* (`count_<class>`): the descriptor's `name` is the example member.
|
|
18516
|
+
*/
|
|
18517
|
+
var NcTemplateVarDescriptorSchema = object({
|
|
18518
|
+
name: string().regex(/^\w+$/),
|
|
18519
|
+
label: string(),
|
|
18520
|
+
description: string().optional(),
|
|
18521
|
+
example: string(),
|
|
18522
|
+
group: NcTemplateVarGroupSchema,
|
|
18523
|
+
families: array(NcTemplateFamilySchema).min(1),
|
|
18524
|
+
fields: array(NcTemplateFieldSchema).optional(),
|
|
18525
|
+
kinds: array(NcRuleKindSchema).optional(),
|
|
18526
|
+
deliveries: array(NcDeliverySchema).optional(),
|
|
18527
|
+
systemEventKinds: array(NcSystemEventKindSchema).optional(),
|
|
18528
|
+
dynamic: object({
|
|
18529
|
+
prefix: literal("count_"),
|
|
18530
|
+
from: literal("classes")
|
|
18531
|
+
}).optional()
|
|
18532
|
+
});
|
|
18533
|
+
/** Input to `previewTemplate` — the editor's own context, plus the draft text. */
|
|
18534
|
+
var NcTemplatePreviewInputSchema = object({
|
|
18535
|
+
context: object({
|
|
18536
|
+
family: NcTemplateFamilySchema,
|
|
18537
|
+
field: NcTemplateFieldSchema,
|
|
18538
|
+
kind: NcRuleKindSchema.optional(),
|
|
18539
|
+
delivery: NcDeliverySchema.optional(),
|
|
18540
|
+
systemEventKinds: array(NcSystemEventKindSchema).optional()
|
|
18541
|
+
}),
|
|
18542
|
+
template: object({
|
|
18543
|
+
title: string().max(500).optional(),
|
|
18544
|
+
body: string().max(2e3).optional()
|
|
18545
|
+
})
|
|
18546
|
+
});
|
|
18547
|
+
var NcTemplatePreviewSchema = object({
|
|
18548
|
+
title: string().nullable(),
|
|
18549
|
+
body: string().nullable(),
|
|
18550
|
+
/** Names the template uses that this context never fills — they render empty. */
|
|
18551
|
+
unknown: array(string())
|
|
18552
|
+
});
|
|
18246
18553
|
var NcConditionDescriptorSchema = object({
|
|
18247
18554
|
/** Field id inside `NcConditions` (or `'schedule'` for the rule-level group). */
|
|
18248
18555
|
id: string(),
|
|
@@ -18508,8 +18815,17 @@ object({
|
|
|
18508
18815
|
lastAt: number()
|
|
18509
18816
|
});
|
|
18510
18817
|
/**
|
|
18511
|
-
* The
|
|
18512
|
-
*
|
|
18818
|
+
* The per-mode lists, at most one per mode. Two lists for one mode have no
|
|
18819
|
+
* meaning a reader could agree on (union? the last one?), so they are refused
|
|
18820
|
+
* at the boundary rather than interpreted.
|
|
18821
|
+
*/
|
|
18822
|
+
var NcAlarmNonBlockingListSchema = array(object({
|
|
18823
|
+
mode: AlarmArmModeSchema,
|
|
18824
|
+
deviceIds: array(number().int()).max(200)
|
|
18825
|
+
})).max(8).refine((lists) => new Set(lists.map((l) => l.mode)).size === lists.length, { message: "nonBlocking: at most one list per arm mode" });
|
|
18826
|
+
/**
|
|
18827
|
+
* The three durations the panel's state machine runs on, who hears about its
|
|
18828
|
+
* transitions, and which openings each mode tolerates.
|
|
18513
18829
|
*
|
|
18514
18830
|
* They live on the NOTIFICATION-RULES cap, not on `alarm-panel`, on purpose:
|
|
18515
18831
|
* `alarm-panel` is `deviceNative` and its other provider mirrors somebody
|
|
@@ -18528,21 +18844,39 @@ var NcAlarmSettingsSchema = object({
|
|
|
18528
18844
|
* existed, and therefore what an untouched install keeps doing.
|
|
18529
18845
|
*/
|
|
18530
18846
|
triggeredDurationSec: number().int().min(0).max(3600),
|
|
18531
|
-
/** Send a notification when a mode takes effect. */
|
|
18532
|
-
announceArm: boolean(),
|
|
18533
18847
|
/**
|
|
18534
|
-
*
|
|
18848
|
+
* Deprecated by D630 — every transition is announced. Parsed, never read;
|
|
18849
|
+
* remove after one release. Optional so an older admin's patch (which still
|
|
18850
|
+
* sends it) parses rather than failing the whole save.
|
|
18851
|
+
*/
|
|
18852
|
+
announceArm: boolean().optional(),
|
|
18853
|
+
/**
|
|
18854
|
+
* Target ids from `notification-output`: every alarm transition goes here.
|
|
18535
18855
|
*
|
|
18536
|
-
* Explicit rather than "everyone": an
|
|
18856
|
+
* Explicit rather than "everyone": an alarm announcement is a household
|
|
18537
18857
|
* message, and broadcasting it to every configured endpoint (including a
|
|
18538
18858
|
* webhook wired to something else) is not a default anybody would choose.
|
|
18539
|
-
* Empty
|
|
18540
|
-
*
|
|
18859
|
+
* Empty sends nothing, and the server logs that — silence must be
|
|
18860
|
+
* attributable.
|
|
18861
|
+
*/
|
|
18862
|
+
announceTargets: array(string().min(1)).max(16),
|
|
18863
|
+
/**
|
|
18864
|
+
* Per arm mode, the covered devices whose being OPEN does not refuse the
|
|
18865
|
+
* arm (a window left ajar for the cat under `home`, say). At most one entry
|
|
18866
|
+
* per mode is meaningful. Defaulted to `[]` so a blob stored before D630
|
|
18867
|
+
* parses as "every opening blocks" — the behaviour it was written under.
|
|
18541
18868
|
*/
|
|
18542
|
-
|
|
18869
|
+
nonBlocking: NcAlarmNonBlockingListSchema.default([])
|
|
18543
18870
|
});
|
|
18544
|
-
/**
|
|
18545
|
-
|
|
18871
|
+
/**
|
|
18872
|
+
* Every field optional — a tab edits one control at a time.
|
|
18873
|
+
*
|
|
18874
|
+
* `nonBlocking` is re-declared WITHOUT its default: `.partial()` does not
|
|
18875
|
+
* remove an inner `.default()`, so a patch that never named the field would
|
|
18876
|
+
* parse to `nonBlocking: []` and wipe every mode's list on an unrelated edit
|
|
18877
|
+
* (the same trap {@link NcRulePatchSchema} documents).
|
|
18878
|
+
*/
|
|
18879
|
+
var NcAlarmSettingsPatchSchema = NcAlarmSettingsSchema.extend({ nonBlocking: NcAlarmNonBlockingListSchema }).partial();
|
|
18546
18880
|
/**
|
|
18547
18881
|
* What one arm mode actually arms, DERIVED from the enabled rules gated on it.
|
|
18548
18882
|
* Never authored, never stored — see `alarm-mode-coverage.ts` for why a stored
|
|
@@ -18587,6 +18921,36 @@ var NcAlarmModeCoverageSchema = object({
|
|
|
18587
18921
|
*/
|
|
18588
18922
|
skippedDevices: array(NcAlarmSkippedDeviceSchema).default([])
|
|
18589
18923
|
});
|
|
18924
|
+
/** One covered device that reads open, and whether that refuses the arm. */
|
|
18925
|
+
var NcAlarmOpeningSchema = object({
|
|
18926
|
+
deviceId: number().int(),
|
|
18927
|
+
name: string(),
|
|
18928
|
+
/** The device's state word as read (`open`, `unlocked`, …). */
|
|
18929
|
+
state: string(),
|
|
18930
|
+
/** False when the mode's non-blocking list names this device. */
|
|
18931
|
+
blocking: boolean()
|
|
18932
|
+
});
|
|
18933
|
+
/** A device the panel is currently ignoring, and which side of it it is on. */
|
|
18934
|
+
var NcAlarmExclusionViewSchema = object({
|
|
18935
|
+
deviceId: number().int(),
|
|
18936
|
+
name: string(),
|
|
18937
|
+
phase: _enum(["open", "closed"])
|
|
18938
|
+
});
|
|
18939
|
+
/** One mode's openings: devices read open, plus those whose state is unknown. */
|
|
18940
|
+
var NcAlarmModeOpeningsSchema = object({
|
|
18941
|
+
mode: AlarmArmModeSchema,
|
|
18942
|
+
devices: array(NcAlarmOpeningSchema),
|
|
18943
|
+
/** Covered devices whose state could not be read (D49): neither open nor closed. */
|
|
18944
|
+
unknown: array(number().int())
|
|
18945
|
+
});
|
|
18946
|
+
var NcAlarmLiveSchema = object({
|
|
18947
|
+
state: AlarmStateSchema,
|
|
18948
|
+
/** The mode being armed into (exit delay) or held; null when disarmed. */
|
|
18949
|
+
targetMode: AlarmArmModeSchema.nullable(),
|
|
18950
|
+
availableModes: array(AlarmArmModeSchema),
|
|
18951
|
+
openings: array(NcAlarmModeOpeningsSchema),
|
|
18952
|
+
exclusions: array(NcAlarmExclusionViewSchema)
|
|
18953
|
+
});
|
|
18590
18954
|
var NcAlarmConfigSchema = object({
|
|
18591
18955
|
/**
|
|
18592
18956
|
* The panel's device id, or null when this install has no panel (the ensure
|
|
@@ -18596,7 +18960,13 @@ var NcAlarmConfigSchema = object({
|
|
|
18596
18960
|
*/
|
|
18597
18961
|
deviceId: number().int().nullable(),
|
|
18598
18962
|
settings: NcAlarmSettingsSchema,
|
|
18599
|
-
coverage: array(NcAlarmModeCoverageSchema)
|
|
18963
|
+
coverage: array(NcAlarmModeCoverageSchema),
|
|
18964
|
+
/**
|
|
18965
|
+
* The panel as it is NOW — state, what each mode would find open, what is
|
|
18966
|
+
* excluded. Optional: a panel-less hub answers without it, and a client
|
|
18967
|
+
* must not read its absence as "nothing is open".
|
|
18968
|
+
*/
|
|
18969
|
+
live: NcAlarmLiveSchema.optional()
|
|
18600
18970
|
});
|
|
18601
18971
|
/**
|
|
18602
18972
|
* ONE rule's demand on ONE camera's clip ring.
|
|
@@ -18644,10 +19014,7 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
18644
19014
|
kind: "mutation",
|
|
18645
19015
|
auth: "admin",
|
|
18646
19016
|
caller: "required"
|
|
18647
|
-
}), method(object({
|
|
18648
|
-
ruleId: string(),
|
|
18649
|
-
patch: NcRulePatchSchema
|
|
18650
|
-
}), object({ rule: NcRuleSchema }), {
|
|
19017
|
+
}), method(NcRuleUpdateInputSchema, object({ rule: NcRuleSchema }), {
|
|
18651
19018
|
kind: "mutation",
|
|
18652
19019
|
auth: "admin",
|
|
18653
19020
|
caller: "required"
|
|
@@ -18675,7 +19042,10 @@ method(object({}), object({ rules: array(NcRuleSchema) }), { auth: "admin" }), m
|
|
|
18675
19042
|
}), method(object({}), object({
|
|
18676
19043
|
catalog: array(NcConditionDescriptorSchema),
|
|
18677
19044
|
taxonomy: NcTaxonomySchema.optional()
|
|
18678
|
-
})), method(object({
|
|
19045
|
+
})), method(object({}), object({ vars: array(NcTemplateVarDescriptorSchema) })), method(NcTemplatePreviewInputSchema, NcTemplatePreviewSchema, {
|
|
19046
|
+
kind: "mutation",
|
|
19047
|
+
access: "view"
|
|
19048
|
+
}), method(object({ filter: NcHistoryFilterSchema.default({ limit: 100 }) }), object({ entries: array(NcHistoryEntrySchema) }), { auth: "admin" }), method(object({ artifactId: string().min(1) }), object({ url: string().nullable() }), { auth: "admin" }), method(object({}), object({ snoozes: array(NcSnoozeSchema) }), { caller: "required" }), method(object({ snooze: NcSnoozeInputSchema }), object({ snooze: NcSnoozeSchema }), {
|
|
18679
19049
|
kind: "mutation",
|
|
18680
19050
|
caller: "required"
|
|
18681
19051
|
}), method(object({ snoozeId: string() }), object({ success: literal(true) }), {
|
|
@@ -23148,6 +23518,7 @@ var CAP_TO_KIND = {
|
|
|
23148
23518
|
"enum-sensor": "enum-sensor",
|
|
23149
23519
|
"event-emitter": "device-event",
|
|
23150
23520
|
"lock-control": "lock",
|
|
23521
|
+
cover: "cover",
|
|
23151
23522
|
switch: "switch",
|
|
23152
23523
|
button: "button",
|
|
23153
23524
|
doorbell: "doorbell"
|
|
@@ -23812,41 +24183,65 @@ var ProviderListEntrySchema = discriminatedUnion("shouldSaveDiskSpace", [object(
|
|
|
23812
24183
|
method(object({
|
|
23813
24184
|
location: StorageLocationRefSchema,
|
|
23814
24185
|
relativePath: string()
|
|
23815
|
-
}), string()), method(object({
|
|
24186
|
+
}), string(), { auth: "admin" }), method(object({
|
|
23816
24187
|
location: StorageLocationRefSchema,
|
|
23817
24188
|
relativePath: string(),
|
|
23818
24189
|
data: _instanceof(Uint8Array)
|
|
23819
|
-
}), _void(), {
|
|
24190
|
+
}), _void(), {
|
|
24191
|
+
kind: "mutation",
|
|
24192
|
+
auth: "admin"
|
|
24193
|
+
}), method(object({
|
|
23820
24194
|
location: StorageLocationRefSchema,
|
|
23821
24195
|
relativePath: string()
|
|
23822
|
-
}), _instanceof(Uint8Array)), method(object({
|
|
24196
|
+
}), _instanceof(Uint8Array), { auth: "admin" }), method(object({
|
|
23823
24197
|
location: StorageLocationRefSchema,
|
|
23824
24198
|
relativePath: string()
|
|
23825
|
-
}), boolean()), method(object({
|
|
24199
|
+
}), boolean(), { auth: "admin" }), method(object({
|
|
23826
24200
|
location: StorageLocationRefSchema,
|
|
23827
24201
|
prefix: string().optional()
|
|
23828
|
-
}), array(string()).readonly()), method(object({
|
|
24202
|
+
}), array(string()).readonly(), { auth: "admin" }), method(object({
|
|
23829
24203
|
location: StorageLocationRefSchema,
|
|
23830
24204
|
relativePath: string()
|
|
23831
|
-
}), _void(), {
|
|
24205
|
+
}), _void(), {
|
|
24206
|
+
kind: "mutation",
|
|
24207
|
+
auth: "admin"
|
|
24208
|
+
}), method(object({ location: StorageLocationRefSchema }), number().nullable()), method(object({
|
|
23832
24209
|
location: StorageLocationRefSchema,
|
|
23833
24210
|
relativePath: string(),
|
|
23834
24211
|
sizeBytes: number().optional()
|
|
23835
|
-
}), object({ uploadId: string() }), {
|
|
24212
|
+
}), object({ uploadId: string() }), {
|
|
24213
|
+
kind: "mutation",
|
|
24214
|
+
auth: "admin"
|
|
24215
|
+
}), method(object({
|
|
23836
24216
|
uploadId: string(),
|
|
23837
24217
|
offset: number(),
|
|
23838
24218
|
data: _instanceof(Uint8Array)
|
|
23839
|
-
}), _void(), {
|
|
24219
|
+
}), _void(), {
|
|
24220
|
+
kind: "mutation",
|
|
24221
|
+
auth: "admin"
|
|
24222
|
+
}), method(object({ uploadId: string() }), _void(), {
|
|
24223
|
+
kind: "mutation",
|
|
24224
|
+
auth: "admin"
|
|
24225
|
+
}), method(object({ uploadId: string() }), _void(), {
|
|
24226
|
+
kind: "mutation",
|
|
24227
|
+
auth: "admin"
|
|
24228
|
+
}), method(object({
|
|
23840
24229
|
location: StorageLocationRefSchema,
|
|
23841
24230
|
relativePath: string()
|
|
23842
24231
|
}), object({
|
|
23843
24232
|
downloadId: string(),
|
|
23844
24233
|
sizeBytes: number()
|
|
23845
|
-
}), {
|
|
24234
|
+
}), {
|
|
24235
|
+
kind: "mutation",
|
|
24236
|
+
auth: "admin"
|
|
24237
|
+
}), method(object({
|
|
23846
24238
|
downloadId: string(),
|
|
23847
24239
|
offset: number(),
|
|
23848
24240
|
length: number()
|
|
23849
|
-
}), _instanceof(Uint8Array)
|
|
24241
|
+
}), _instanceof(Uint8Array), { auth: "admin" }), method(object({ downloadId: string() }), _void(), {
|
|
24242
|
+
kind: "mutation",
|
|
24243
|
+
auth: "admin"
|
|
24244
|
+
}), method(object({ type: StorageLocationTypeSchema.optional() }), array(StorageLocationSchema).readonly()), method(_void(), array(StorageLocationDeclarationSchema).readonly()), method(StorageLocationSchema.omit({
|
|
23850
24245
|
createdAt: true,
|
|
23851
24246
|
updatedAt: true
|
|
23852
24247
|
}), StorageLocationSchema, {
|
|
@@ -25860,7 +26255,67 @@ var WebrtcLadderDecisionSchema = object({
|
|
|
25860
26255
|
serverSending: boolean().nullable(),
|
|
25861
26256
|
/** Epoch ms. The overlay prints its age; a decision from 40 minutes ago
|
|
25862
26257
|
* must not read like one from this second. */
|
|
25863
|
-
at: number()
|
|
26258
|
+
at: number(),
|
|
26259
|
+
/** The broker's own cause when it is finer than `cause` can carry, e.g.
|
|
26260
|
+
* `trial-revert` (reported as `stall`/`loss` for older consumers). Optional:
|
|
26261
|
+
* an older server strips it instead of rejecting the answer. */
|
|
26262
|
+
causeDetail: string().optional()
|
|
26263
|
+
});
|
|
26264
|
+
var WebrtcClimbHoldSchema = object({
|
|
26265
|
+
/** `stall-building` — the client receives ≪ what is sent; `delivery` — the
|
|
26266
|
+
* current rung is not landing whole; `no-evidence` — no receiver feedback;
|
|
26267
|
+
* `warming` — healthy, accumulating the streak; `cooldown` — too soon after
|
|
26268
|
+
* a change; `capacity` — a MEASURED probe below the next rung's bar;
|
|
26269
|
+
* `try-unclean` — no capacity evidence, and the path is not clean enough to
|
|
26270
|
+
* try; `try-backoff` — a try reverted, waiting out its backoff; `trial` —
|
|
26271
|
+
* this rung IS a try, reverting if it does not land; `suspended` —
|
|
26272
|
+
* recorded playback on the track. */
|
|
26273
|
+
code: string(),
|
|
26274
|
+
/** The rung a climb would go to. */
|
|
26275
|
+
nextTier: CamProfileSchema,
|
|
26276
|
+
/** Bytes received ÷ bytes sent over the delivery window (≥ one GOP). */
|
|
26277
|
+
deliveryRatio: number().nullable(),
|
|
26278
|
+
deliveryWindowMs: number().nullable(),
|
|
26279
|
+
/** The ratio a climb requires. */
|
|
26280
|
+
climbDeliveryRatio: number(),
|
|
26281
|
+
/** The receiver's bandwidth estimate (REMB), kbps. Capped by what is sent. */
|
|
26282
|
+
capacityKbps: number().nullable(),
|
|
26283
|
+
/** The viewer's downlink probe for this session, kbps. */
|
|
26284
|
+
probeKbps: number().nullable(),
|
|
26285
|
+
/** The next rung's measured bitrate, kbps. */
|
|
26286
|
+
nextTierKbps: number().nullable(),
|
|
26287
|
+
capacityMargin: number(),
|
|
26288
|
+
/** The capacity a climb to `nextTier` needs, kbps (bitrate × margin, or the
|
|
26289
|
+
* client ladder threshold when the rung has measured nothing). */
|
|
26290
|
+
neededKbps: number().nullable(),
|
|
26291
|
+
/** For `try-unclean`: which clean-path condition failed (a `WebrtcTryBlocker`,
|
|
26292
|
+
* carried as a string for the same reason as `code`). */
|
|
26293
|
+
tryBlockedBy: string().nullable(),
|
|
26294
|
+
/** The delivery ratio and the loss ratio a try requires / must stay under.
|
|
26295
|
+
* Optional, like every field added to this schema after it first shipped:
|
|
26296
|
+
* the router validates output, so a REQUIRED new field makes a newer server
|
|
26297
|
+
* reject an older broker's whole answer (review m1). */
|
|
26298
|
+
tryDeliveryRatio: number().optional(),
|
|
26299
|
+
tryMaxLossRatio: number().optional(),
|
|
26300
|
+
/** For `try-backoff`: time until the next try, ms. */
|
|
26301
|
+
retryInMs: number().nullable(),
|
|
26302
|
+
/** The last try that reverted in this viewer session. */
|
|
26303
|
+
lastRevert: object({
|
|
26304
|
+
tier: CamProfileSchema,
|
|
26305
|
+
deliveryRatio: number().nullable(),
|
|
26306
|
+
stalled: boolean(),
|
|
26307
|
+
at: number()
|
|
26308
|
+
}).nullable(),
|
|
26309
|
+
/** For `trial`: how long this rung has been on trial, and for how long it must hold. */
|
|
26310
|
+
trialForMs: number().nullable(),
|
|
26311
|
+
trialMs: number().nullable(),
|
|
26312
|
+
healthyForMs: number().nullable(),
|
|
26313
|
+
requiredHealthyMs: number().nullable(),
|
|
26314
|
+
/** Epoch ms this hold's code began. */
|
|
26315
|
+
since: number(),
|
|
26316
|
+
/** The same one-line account the broker logs, e.g.
|
|
26317
|
+
* `held: delivery 0.97 over 4.0s ok, REMB 480 kbps < mid 1800 kbps × 1.2`. */
|
|
26318
|
+
summary: string()
|
|
25864
26319
|
});
|
|
25865
26320
|
/** Per-tier count of MEASURED downgrades this viewer session has suffered. */
|
|
25866
26321
|
var WebrtcTierFailureSchema = object({
|
|
@@ -25924,7 +26379,12 @@ var WebrtcSessionDebugSchema = object({
|
|
|
25924
26379
|
tierFailures: array(WebrtcTierFailureSchema),
|
|
25925
26380
|
/** Successful promotions in this viewer session (drives the progressive
|
|
25926
26381
|
* healthy timer). Null when no ladder is running. */
|
|
25927
|
-
upgradeCount: number().nullable()
|
|
26382
|
+
upgradeCount: number().nullable(),
|
|
26383
|
+
/** Why the ladder is not climbing right now. Null when no ladder runs, it
|
|
26384
|
+
* is at the top rung, or it just moved. Optional because this output is
|
|
26385
|
+
* validated by the router: a server carrying this schema must still accept
|
|
26386
|
+
* the answer of a stream-broker deployed before the field existed. */
|
|
26387
|
+
climbHold: WebrtcClimbHoldSchema.nullable().optional()
|
|
25928
26388
|
});
|
|
25929
26389
|
DeviceType.Camera, method(object({ deviceId: number().int().nonnegative() }), array(WebrtcStreamChoiceSchema)), method(object({
|
|
25930
26390
|
deviceId: number().int().nonnegative(),
|
|
@@ -31145,6 +31605,11 @@ var ClientNetworkStatsSchema = object({
|
|
|
31145
31605
|
jitterMs: number(),
|
|
31146
31606
|
estimatedBandwidthKbps: number(),
|
|
31147
31607
|
packetLossPercent: number().min(0).max(100),
|
|
31608
|
+
/** The viewer's latest `/adaptive-probe` reading, Mbps — whatever it read
|
|
31609
|
+
* (D635 I4). Absent when it never probed or the probe failed (unknown). */
|
|
31610
|
+
downlinkMbps: number().optional(),
|
|
31611
|
+
/** Age of that reading when this report was sent, ms. */
|
|
31612
|
+
downlinkAgeMs: number().optional(),
|
|
31148
31613
|
lastUpdated: number()
|
|
31149
31614
|
});
|
|
31150
31615
|
var DeviceNetworkStatsSchema = object({
|
|
@@ -31157,7 +31622,11 @@ method(object({ deviceId: number() }), DeviceNetworkStatsSchema.nullable()), met
|
|
|
31157
31622
|
rttMs: number().min(0).max(6e4),
|
|
31158
31623
|
jitterMs: number().min(0).max(1e4),
|
|
31159
31624
|
estimatedBandwidthKbps: number().min(0).max(1e6),
|
|
31160
|
-
packetLossPercent: number().min(0).max(100)
|
|
31625
|
+
packetLossPercent: number().min(0).max(100),
|
|
31626
|
+
/** Latest downlink probe, Mbps, whatever it read; absent = never / failed. */
|
|
31627
|
+
downlinkMbps: number().min(0).max(1e5).optional(),
|
|
31628
|
+
/** Age of that probe when this report is sent, ms. */
|
|
31629
|
+
downlinkAgeMs: number().min(0).max(864e5).optional()
|
|
31161
31630
|
}), _void(), { kind: "mutation" });
|
|
31162
31631
|
/**
|
|
31163
31632
|
* nodes — system-scoped singleton capability for cluster topology
|
|
@@ -41971,6 +42440,12 @@ Object.freeze({
|
|
|
41971
42440
|
addonId: null,
|
|
41972
42441
|
access: "view"
|
|
41973
42442
|
},
|
|
42443
|
+
"deviceManager.listDeviceIdsByCaps": {
|
|
42444
|
+
capName: "device-manager",
|
|
42445
|
+
capScope: "system",
|
|
42446
|
+
addonId: null,
|
|
42447
|
+
access: "view"
|
|
42448
|
+
},
|
|
41974
42449
|
"deviceManager.listLocationDescriptions": {
|
|
41975
42450
|
capName: "device-manager",
|
|
41976
42451
|
capScope: "system",
|
|
@@ -42931,12 +43406,30 @@ Object.freeze({
|
|
|
42931
43406
|
addonId: null,
|
|
42932
43407
|
access: "create"
|
|
42933
43408
|
},
|
|
43409
|
+
"llmRuntime.appleAvailability": {
|
|
43410
|
+
capName: "llm-runtime",
|
|
43411
|
+
capScope: "system",
|
|
43412
|
+
addonId: null,
|
|
43413
|
+
access: "view"
|
|
43414
|
+
},
|
|
43415
|
+
"llmRuntime.cancel": {
|
|
43416
|
+
capName: "llm-runtime",
|
|
43417
|
+
capScope: "system",
|
|
43418
|
+
addonId: null,
|
|
43419
|
+
access: "create"
|
|
43420
|
+
},
|
|
42934
43421
|
"llmRuntime.complete": {
|
|
42935
43422
|
capName: "llm-runtime",
|
|
42936
43423
|
capScope: "system",
|
|
42937
43424
|
addonId: null,
|
|
42938
43425
|
access: "create"
|
|
42939
43426
|
},
|
|
43427
|
+
"llmRuntime.completeApple": {
|
|
43428
|
+
capName: "llm-runtime",
|
|
43429
|
+
capScope: "system",
|
|
43430
|
+
addonId: null,
|
|
43431
|
+
access: "create"
|
|
43432
|
+
},
|
|
42940
43433
|
"llmRuntime.deleteModel": {
|
|
42941
43434
|
capName: "llm-runtime",
|
|
42942
43435
|
capScope: "system",
|
|
@@ -43777,6 +44270,12 @@ Object.freeze({
|
|
|
43777
44270
|
addonId: null,
|
|
43778
44271
|
access: "view"
|
|
43779
44272
|
},
|
|
44273
|
+
"notificationRules.getTemplateCatalog": {
|
|
44274
|
+
capName: "notification-rules",
|
|
44275
|
+
capScope: "system",
|
|
44276
|
+
addonId: null,
|
|
44277
|
+
access: "view"
|
|
44278
|
+
},
|
|
43780
44279
|
"notificationRules.listDeviceMutes": {
|
|
43781
44280
|
capName: "notification-rules",
|
|
43782
44281
|
capScope: "system",
|
|
@@ -43795,6 +44294,12 @@ Object.freeze({
|
|
|
43795
44294
|
addonId: null,
|
|
43796
44295
|
access: "view"
|
|
43797
44296
|
},
|
|
44297
|
+
"notificationRules.previewTemplate": {
|
|
44298
|
+
capName: "notification-rules",
|
|
44299
|
+
capScope: "system",
|
|
44300
|
+
addonId: null,
|
|
44301
|
+
access: "view"
|
|
44302
|
+
},
|
|
43798
44303
|
"notificationRules.resolveArtifactUrl": {
|
|
43799
44304
|
capName: "notification-rules",
|
|
43800
44305
|
capScope: "system",
|
|
@@ -49362,6 +49867,41 @@ var NC_AUDIO_DEFAULTS = {
|
|
|
49362
49867
|
};
|
|
49363
49868
|
new Set(["devices", "classes"]);
|
|
49364
49869
|
NC_AUDIO_DEFAULTS.hitPercent, NC_AUDIO_DEFAULTS.samplingSeconds;
|
|
49870
|
+
var RULE_BODY_ONLY = ["body"];
|
|
49871
|
+
var RULE_TEXT = ["title", "body"];
|
|
49872
|
+
var BATTERY = ["device-battery-low", "device-battery-normal"];
|
|
49873
|
+
var CONSUMABLE = ["device-consumable-low", "device-consumable-normal"];
|
|
49874
|
+
var UPDATES = [
|
|
49875
|
+
"addon-update-available",
|
|
49876
|
+
"server-update-available",
|
|
49877
|
+
"wrapper-update-available"
|
|
49878
|
+
];
|
|
49879
|
+
var PACKAGE_UPDATES = ["addon-update-available", "server-update-available"];
|
|
49880
|
+
var ALARM = [
|
|
49881
|
+
"alarm-triggered",
|
|
49882
|
+
"alarm-armed",
|
|
49883
|
+
"alarm-disarmed",
|
|
49884
|
+
"alarm-arming",
|
|
49885
|
+
"alarm-arm-refused",
|
|
49886
|
+
"alarm-pending",
|
|
49887
|
+
"alarm-rearmed",
|
|
49888
|
+
"alarm-sensor-bypass",
|
|
49889
|
+
"alarm-check-failed"
|
|
49890
|
+
];
|
|
49891
|
+
var COMBINED_ALARM = ["alarm-triggered"];
|
|
49892
|
+
var DEVICE_EVENTS = [
|
|
49893
|
+
"device-online",
|
|
49894
|
+
"device-offline",
|
|
49895
|
+
"device-disabled",
|
|
49896
|
+
"device-enabled",
|
|
49897
|
+
...BATTERY,
|
|
49898
|
+
...CONSUMABLE,
|
|
49899
|
+
"stream-online",
|
|
49900
|
+
"stream-offline",
|
|
49901
|
+
"detection-blind",
|
|
49902
|
+
...ALARM
|
|
49903
|
+
];
|
|
49904
|
+
[...DEVICE_EVENTS], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...COMBINED_ALARM], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_BODY_ONLY], [...RULE_TEXT], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...DEVICE_EVENTS], [...RULE_TEXT], [...BATTERY, ...CONSUMABLE], [...RULE_TEXT], [...CONSUMABLE], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...UPDATES], [...RULE_TEXT], [...PACKAGE_UPDATES], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT], [...RULE_TEXT];
|
|
49365
49905
|
/**
|
|
49366
49906
|
* TimelapseRule — the STANDALONE scheduled timelapse producer's rule model.
|
|
49367
49907
|
*
|
|
@@ -54760,11 +55300,22 @@ function createClipThumbHandler(deps) {
|
|
|
54760
55300
|
writeJpeg(res, cached, req.method === "HEAD");
|
|
54761
55301
|
return;
|
|
54762
55302
|
}
|
|
55303
|
+
const gone = new AbortController();
|
|
55304
|
+
const onClose = () => {
|
|
55305
|
+
if (!res.writableEnded) gone.abort();
|
|
55306
|
+
};
|
|
55307
|
+
res.on("close", onClose);
|
|
54763
55308
|
let minted;
|
|
54764
55309
|
try {
|
|
54765
|
-
minted = await deps.mint(deviceId, clipKey);
|
|
55310
|
+
minted = await deps.mint(deviceId, clipKey, gone.signal);
|
|
54766
55311
|
} catch (err) {
|
|
54767
55312
|
minted = refuseStill("camera-refused", err instanceof Error ? err.message : String(err));
|
|
55313
|
+
} finally {
|
|
55314
|
+
res.off("close", onClose);
|
|
55315
|
+
}
|
|
55316
|
+
if (!minted.ok && "abandoned" in minted) {
|
|
55317
|
+
if (!res.writableEnded) res.end();
|
|
55318
|
+
return;
|
|
54768
55319
|
}
|
|
54769
55320
|
if (!minted.ok) {
|
|
54770
55321
|
writeStillRefusal(res, minted, deviceId, deps.logger, clipKey);
|
|
@@ -55643,6 +56194,11 @@ function createClipStreamProducer(deps) {
|
|
|
55643
56194
|
code: "replay-busy",
|
|
55644
56195
|
detail: lease.detail
|
|
55645
56196
|
};
|
|
56197
|
+
if (lease.kind === "abandoned") return {
|
|
56198
|
+
kind: "refused",
|
|
56199
|
+
code: "replay-busy",
|
|
56200
|
+
detail: "the playback was abandoned before this camera’s replay came free"
|
|
56201
|
+
};
|
|
55646
56202
|
const args = buildClipStreamMuxArgs({
|
|
55647
56203
|
url: request.url,
|
|
55648
56204
|
audio: request.audio
|
|
@@ -55897,7 +56453,7 @@ async function cutClipStill(input, deps) {
|
|
|
55897
56453
|
child.stderr?.on("data", (chunk) => {
|
|
55898
56454
|
stderr = `${stderr}${chunk.toString("utf8")}`.slice(-4e3);
|
|
55899
56455
|
});
|
|
55900
|
-
const timeoutMs = deps.timeoutMs ??
|
|
56456
|
+
const timeoutMs = deps.timeoutMs ?? 5e3;
|
|
55901
56457
|
let yielded = false;
|
|
55902
56458
|
const exit = await new Promise((resolve) => {
|
|
55903
56459
|
const onAbort = () => {
|
|
@@ -55952,10 +56508,73 @@ async function cutClipStill(input, deps) {
|
|
|
55952
56508
|
detail: `the replay ran for ${String(slotMs)} ms and produced no decodable frame`
|
|
55953
56509
|
};
|
|
55954
56510
|
}
|
|
56511
|
+
function memoryKey(deviceId, clipKey) {
|
|
56512
|
+
return `${String(deviceId)}/${clipKey}`;
|
|
56513
|
+
}
|
|
56514
|
+
function createClipStillGuard(deps) {
|
|
56515
|
+
const now = deps.now ?? (() => Date.now());
|
|
56516
|
+
const memoryMs = deps.memoryMs ?? 6e5;
|
|
56517
|
+
const pauseMs = deps.pauseMs ?? 3e5;
|
|
56518
|
+
const failuresToPause = deps.failuresToPause ?? 2;
|
|
56519
|
+
const failures = /* @__PURE__ */ new Map();
|
|
56520
|
+
const consecutive = /* @__PURE__ */ new Map();
|
|
56521
|
+
const pausedUntil = /* @__PURE__ */ new Map();
|
|
56522
|
+
const forgetLapsed = (at) => {
|
|
56523
|
+
for (const [key, entry] of failures) if (entry.untilMs <= at) failures.delete(key);
|
|
56524
|
+
};
|
|
56525
|
+
return {
|
|
56526
|
+
remembered: (deviceId, clipKey) => {
|
|
56527
|
+
const key = memoryKey(deviceId, clipKey);
|
|
56528
|
+
const entry = failures.get(key);
|
|
56529
|
+
if (entry === void 0) return null;
|
|
56530
|
+
if (entry.untilMs <= now()) {
|
|
56531
|
+
failures.delete(key);
|
|
56532
|
+
return null;
|
|
56533
|
+
}
|
|
56534
|
+
return entry.refusal;
|
|
56535
|
+
},
|
|
56536
|
+
pausedForMs: (deviceId) => {
|
|
56537
|
+
const until = pausedUntil.get(deviceId);
|
|
56538
|
+
if (until === void 0) return null;
|
|
56539
|
+
const left = until - now();
|
|
56540
|
+
if (left > 0) return left;
|
|
56541
|
+
pausedUntil.delete(deviceId);
|
|
56542
|
+
return null;
|
|
56543
|
+
},
|
|
56544
|
+
recordFailure: (deviceId, clipKey, refusal) => {
|
|
56545
|
+
const at = now();
|
|
56546
|
+
forgetLapsed(at);
|
|
56547
|
+
failures.set(memoryKey(deviceId, clipKey), {
|
|
56548
|
+
refusal,
|
|
56549
|
+
untilMs: at + memoryMs
|
|
56550
|
+
});
|
|
56551
|
+
const count = (consecutive.get(deviceId) ?? 0) + 1;
|
|
56552
|
+
if (count < failuresToPause) {
|
|
56553
|
+
consecutive.set(deviceId, count);
|
|
56554
|
+
return;
|
|
56555
|
+
}
|
|
56556
|
+
consecutive.delete(deviceId);
|
|
56557
|
+
pausedUntil.set(deviceId, at + pauseMs);
|
|
56558
|
+
deps.logger.warn("videoclips: pausing clip stills for this camera — its replays keep failing", {
|
|
56559
|
+
tags: { deviceId },
|
|
56560
|
+
meta: {
|
|
56561
|
+
branch: "still-pause",
|
|
56562
|
+
consecutiveFailures: count,
|
|
56563
|
+
pauseMs,
|
|
56564
|
+
lastClipKey: clipKey,
|
|
56565
|
+
lastReason: refusal.reason
|
|
56566
|
+
}
|
|
56567
|
+
});
|
|
56568
|
+
},
|
|
56569
|
+
recordSuccess: (deviceId) => {
|
|
56570
|
+
consecutive.delete(deviceId);
|
|
56571
|
+
}
|
|
56572
|
+
};
|
|
56573
|
+
}
|
|
55955
56574
|
//#endregion
|
|
55956
56575
|
//#region src/clips/replay-lane.ts
|
|
55957
56576
|
/**
|
|
55958
|
-
* How long
|
|
56577
|
+
* How long a PLAYBACK may wait for a slot that is being released.
|
|
55959
56578
|
*
|
|
55960
56579
|
* A holder shutting down kills its ffmpeg and the camera's socket dies with
|
|
55961
56580
|
* it; there is no teardown handshake to flush. Two seconds is an order of
|
|
@@ -55974,15 +56593,29 @@ var REPLAY_LANE_WAIT_MS = 2e3;
|
|
|
55974
56593
|
function replayPreempts(asked, holder) {
|
|
55975
56594
|
return asked === "stream" && holder === "still";
|
|
55976
56595
|
}
|
|
56596
|
+
/** Where a new waiter goes: a playback ahead of every still, else the tail. */
|
|
56597
|
+
function queuePosition(queue, purpose) {
|
|
56598
|
+
if (purpose === "still") return queue.length;
|
|
56599
|
+
const firstStill = queue.findIndex((w) => w.purpose === "still");
|
|
56600
|
+
return firstStill === -1 ? queue.length : firstStill;
|
|
56601
|
+
}
|
|
55977
56602
|
function createReplayLane(deps) {
|
|
55978
56603
|
const now = deps.now ?? (() => Date.now());
|
|
55979
56604
|
const waitMs = deps.waitMs ?? 2e3;
|
|
56605
|
+
const maxPending = deps.maxPending ?? 8;
|
|
55980
56606
|
const holding = /* @__PURE__ */ new Map();
|
|
56607
|
+
const queues = /* @__PURE__ */ new Map();
|
|
56608
|
+
const leave = (deviceId, waiter) => {
|
|
56609
|
+
const queue = queues.get(deviceId);
|
|
56610
|
+
if (queue === void 0) return;
|
|
56611
|
+
const index = queue.indexOf(waiter);
|
|
56612
|
+
if (index !== -1) queue.splice(index, 1);
|
|
56613
|
+
if (queue.length === 0) queues.delete(deviceId);
|
|
56614
|
+
};
|
|
55981
56615
|
const take = (deviceId, purpose, onYield) => {
|
|
55982
56616
|
const entry = {
|
|
55983
56617
|
purpose,
|
|
55984
56618
|
sinceMs: now(),
|
|
55985
|
-
waiters: [],
|
|
55986
56619
|
...onYield === void 0 ? {} : { onYield }
|
|
55987
56620
|
};
|
|
55988
56621
|
holding.set(deviceId, entry);
|
|
@@ -55994,16 +56627,82 @@ function createReplayLane(deps) {
|
|
|
55994
56627
|
released = true;
|
|
55995
56628
|
if (holding.get(deviceId) !== entry) return;
|
|
55996
56629
|
holding.delete(deviceId);
|
|
55997
|
-
|
|
56630
|
+
const next = queues.get(deviceId)?.[0];
|
|
56631
|
+
if (next === void 0) return;
|
|
56632
|
+
leave(deviceId, next);
|
|
56633
|
+
next.grant();
|
|
55998
56634
|
}
|
|
55999
56635
|
};
|
|
56000
56636
|
};
|
|
56637
|
+
const refuse = (deviceId, purpose, current, waitedMs, detail, branch) => {
|
|
56638
|
+
const heldForMs = now() - current.sinceMs;
|
|
56639
|
+
const extras = {
|
|
56640
|
+
tags: { deviceId },
|
|
56641
|
+
meta: {
|
|
56642
|
+
branch,
|
|
56643
|
+
asked: purpose,
|
|
56644
|
+
holder: current.purpose,
|
|
56645
|
+
heldForMs,
|
|
56646
|
+
waitedMs
|
|
56647
|
+
}
|
|
56648
|
+
};
|
|
56649
|
+
if (purpose === "stream") deps.logger.warn("videoclips: a replay was refused — this node already holds one", extras);
|
|
56650
|
+
else deps.logger.debug("videoclips: a still was refused a place in the replay queue", extras);
|
|
56651
|
+
return {
|
|
56652
|
+
kind: "busy",
|
|
56653
|
+
holder: current.purpose,
|
|
56654
|
+
heldForMs,
|
|
56655
|
+
waitedMs,
|
|
56656
|
+
detail
|
|
56657
|
+
};
|
|
56658
|
+
};
|
|
56001
56659
|
return {
|
|
56002
56660
|
held: (deviceId) => holding.has(deviceId),
|
|
56003
|
-
acquire: async (deviceId, purpose, onYield) => {
|
|
56661
|
+
acquire: async (deviceId, purpose, onYield, signal) => {
|
|
56004
56662
|
const askedAt = now();
|
|
56663
|
+
if (signal?.aborted === true) return {
|
|
56664
|
+
kind: "abandoned",
|
|
56665
|
+
waitedMs: 0
|
|
56666
|
+
};
|
|
56005
56667
|
const current = holding.get(deviceId);
|
|
56006
56668
|
if (current === void 0) return take(deviceId, purpose, onYield);
|
|
56669
|
+
const queue = queues.get(deviceId) ?? [];
|
|
56670
|
+
if (purpose === "still" && queue.length >= maxPending) return refuse(deviceId, purpose, current, 0, `queue-full: ${String(queue.length)} replays are already waiting for this camera (at most ${String(maxPending)}) — nothing about this clip; ask again once the queue drains`, "replay-queue-full");
|
|
56671
|
+
let settle = () => void 0;
|
|
56672
|
+
const settled = new Promise((resolve) => {
|
|
56673
|
+
settle = resolve;
|
|
56674
|
+
});
|
|
56675
|
+
let timer = null;
|
|
56676
|
+
let done = false;
|
|
56677
|
+
const finish = (value) => {
|
|
56678
|
+
if (done) return;
|
|
56679
|
+
done = true;
|
|
56680
|
+
if (timer !== null) clearTimeout(timer);
|
|
56681
|
+
timer = null;
|
|
56682
|
+
signal?.removeEventListener("abort", onAbort);
|
|
56683
|
+
settle(value);
|
|
56684
|
+
};
|
|
56685
|
+
const waiter = {
|
|
56686
|
+
purpose,
|
|
56687
|
+
grant: () => {
|
|
56688
|
+
finish({
|
|
56689
|
+
kind: "granted",
|
|
56690
|
+
lease: take(deviceId, purpose, onYield)
|
|
56691
|
+
});
|
|
56692
|
+
}
|
|
56693
|
+
};
|
|
56694
|
+
const onAbort = () => {
|
|
56695
|
+
leave(deviceId, waiter);
|
|
56696
|
+
finish({ kind: "abandoned" });
|
|
56697
|
+
};
|
|
56698
|
+
queue.splice(queuePosition(queue, purpose), 0, waiter);
|
|
56699
|
+
queues.set(deviceId, queue);
|
|
56700
|
+
signal?.addEventListener("abort", onAbort, { once: true });
|
|
56701
|
+
if (purpose === "stream") timer = setTimeout(() => {
|
|
56702
|
+
timer = null;
|
|
56703
|
+
leave(deviceId, waiter);
|
|
56704
|
+
finish({ kind: "timeout" });
|
|
56705
|
+
}, waitMs);
|
|
56007
56706
|
if (replayPreempts(purpose, current.purpose) && current.onYield !== void 0) {
|
|
56008
56707
|
deps.logger.debug("videoclips: a speculative replay was asked to give the slot back", {
|
|
56009
56708
|
tags: { deviceId },
|
|
@@ -56015,43 +56714,15 @@ function createReplayLane(deps) {
|
|
|
56015
56714
|
});
|
|
56016
56715
|
current.onYield();
|
|
56017
56716
|
}
|
|
56018
|
-
|
|
56019
|
-
let timer = null;
|
|
56020
|
-
const wake = () => {
|
|
56021
|
-
if (timer !== null) clearTimeout(timer);
|
|
56022
|
-
timer = null;
|
|
56023
|
-
resolve();
|
|
56024
|
-
};
|
|
56025
|
-
timer = setTimeout(() => {
|
|
56026
|
-
timer = null;
|
|
56027
|
-
const index = current.waiters.indexOf(wake);
|
|
56028
|
-
if (index !== -1) current.waiters.splice(index, 1);
|
|
56029
|
-
resolve();
|
|
56030
|
-
}, waitMs);
|
|
56031
|
-
current.waiters.push(wake);
|
|
56032
|
-
});
|
|
56033
|
-
const after = holding.get(deviceId);
|
|
56034
|
-
if (after === void 0) return take(deviceId, purpose, onYield);
|
|
56035
|
-
const heldForMs = now() - after.sinceMs;
|
|
56717
|
+
const outcome = await settled;
|
|
56036
56718
|
const waitedMs = now() - askedAt;
|
|
56037
|
-
|
|
56038
|
-
|
|
56039
|
-
|
|
56040
|
-
|
|
56041
|
-
branch: "replay-busy",
|
|
56042
|
-
asked: purpose,
|
|
56043
|
-
holder: after.purpose,
|
|
56044
|
-
heldForMs,
|
|
56045
|
-
waitedMs
|
|
56046
|
-
}
|
|
56047
|
-
});
|
|
56048
|
-
return {
|
|
56049
|
-
kind: "busy",
|
|
56050
|
-
holder: after.purpose,
|
|
56051
|
-
heldForMs,
|
|
56052
|
-
waitedMs,
|
|
56053
|
-
detail
|
|
56719
|
+
if (outcome.kind === "granted") return outcome.lease;
|
|
56720
|
+
if (outcome.kind === "abandoned") return {
|
|
56721
|
+
kind: "abandoned",
|
|
56722
|
+
waitedMs
|
|
56054
56723
|
};
|
|
56724
|
+
const after = holding.get(deviceId) ?? current;
|
|
56725
|
+
return refuse(deviceId, purpose, after, waitedMs, `replay-busy: this camera is already serving a clip ${after.purpose} for this node, held for ${String(now() - after.sinceMs)} ms — nothing about this clip, and nothing a retry against the camera would change`, "replay-busy");
|
|
56055
56726
|
}
|
|
56056
56727
|
};
|
|
56057
56728
|
}
|
|
@@ -56063,7 +56734,7 @@ function createReplayLane(deps) {
|
|
|
56063
56734
|
* The caller is the route, which has already answered a cached still from disk
|
|
56064
56735
|
* — this is only ever reached for a clip whose picture does not exist yet.
|
|
56065
56736
|
*/
|
|
56066
|
-
async function mintClipStill(deviceId, clipKey, deps) {
|
|
56737
|
+
async function mintClipStill(deviceId, clipKey, deps, signal) {
|
|
56067
56738
|
const tags = { deviceId };
|
|
56068
56739
|
const meta = { clipKey };
|
|
56069
56740
|
if (!await deps.hasCamera(deviceId)) {
|
|
@@ -56100,12 +56771,41 @@ async function mintClipStill(deviceId, clipKey, deps) {
|
|
|
56100
56771
|
});
|
|
56101
56772
|
return refuseStill("no-keyframe", "this clip has no window to take a midpoint of — its catalog row spans nothing, and a midpoint of nothing is a guess rather than a picture");
|
|
56102
56773
|
}
|
|
56774
|
+
const remembered = deps.guard.remembered(deviceId, clipKey);
|
|
56775
|
+
if (remembered !== null) {
|
|
56776
|
+
deps.logger.debug("videoclips: no still — this clip already failed recently", {
|
|
56777
|
+
tags,
|
|
56778
|
+
meta: {
|
|
56779
|
+
...meta,
|
|
56780
|
+
branch: "remembered-failure",
|
|
56781
|
+
code: remembered.code
|
|
56782
|
+
}
|
|
56783
|
+
});
|
|
56784
|
+
return remembered;
|
|
56785
|
+
}
|
|
56786
|
+
const paused = pausedRefusal(deviceId, clipKey, deps);
|
|
56787
|
+
if (paused !== null) return paused;
|
|
56103
56788
|
const evict = new AbortController();
|
|
56104
56789
|
const lease = await deps.lane.acquire(deviceId, "still", () => {
|
|
56105
56790
|
evict.abort();
|
|
56106
|
-
});
|
|
56791
|
+
}, signal);
|
|
56792
|
+
if (lease.kind === "abandoned") {
|
|
56793
|
+
deps.logger.debug("videoclips: no still — its caller went away while it queued", {
|
|
56794
|
+
tags,
|
|
56795
|
+
meta: {
|
|
56796
|
+
...meta,
|
|
56797
|
+
branch: "abandoned",
|
|
56798
|
+
waitedMs: lease.waitedMs
|
|
56799
|
+
}
|
|
56800
|
+
});
|
|
56801
|
+
return {
|
|
56802
|
+
ok: false,
|
|
56803
|
+
abandoned: true,
|
|
56804
|
+
waitedMs: lease.waitedMs
|
|
56805
|
+
};
|
|
56806
|
+
}
|
|
56107
56807
|
if (lease.kind === "busy") {
|
|
56108
|
-
deps.logger.debug("videoclips: no still yet — this camera’s replay is
|
|
56808
|
+
deps.logger.debug("videoclips: no still yet — this camera’s replay queue is full", {
|
|
56109
56809
|
tags,
|
|
56110
56810
|
meta: {
|
|
56111
56811
|
...meta,
|
|
@@ -56116,6 +56816,26 @@ async function mintClipStill(deviceId, clipKey, deps) {
|
|
|
56116
56816
|
});
|
|
56117
56817
|
return deferStill("queue-full", lease.detail, REPLAY_LANE_WAIT_MS);
|
|
56118
56818
|
}
|
|
56819
|
+
const pausedWhileQueued = pausedRefusal(deviceId, clipKey, deps);
|
|
56820
|
+
if (pausedWhileQueued !== null) {
|
|
56821
|
+
lease.release();
|
|
56822
|
+
return pausedWhileQueued;
|
|
56823
|
+
}
|
|
56824
|
+
const mintedWhileQueued = await readCachedSafely(deviceId, clipKey, deps);
|
|
56825
|
+
if (mintedWhileQueued !== null) {
|
|
56826
|
+
lease.release();
|
|
56827
|
+
deps.logger.debug("videoclips: still minted while this ask queued — served from disk", {
|
|
56828
|
+
tags,
|
|
56829
|
+
meta: {
|
|
56830
|
+
...meta,
|
|
56831
|
+
branch: "cached-while-queued"
|
|
56832
|
+
}
|
|
56833
|
+
});
|
|
56834
|
+
return {
|
|
56835
|
+
ok: true,
|
|
56836
|
+
bytes: mintedWhileQueued
|
|
56837
|
+
};
|
|
56838
|
+
}
|
|
56119
56839
|
let cut;
|
|
56120
56840
|
try {
|
|
56121
56841
|
const replay = await deps.replayUrlFor(deviceId, row, window);
|
|
@@ -56128,7 +56848,9 @@ async function mintClipStill(deviceId, clipKey, deps) {
|
|
|
56128
56848
|
reason: replay.detail
|
|
56129
56849
|
}
|
|
56130
56850
|
});
|
|
56131
|
-
|
|
56851
|
+
const refusal = refuseStill("camera-refused", replay.detail);
|
|
56852
|
+
deps.guard.recordFailure(deviceId, clipKey, refusal);
|
|
56853
|
+
return refusal;
|
|
56132
56854
|
}
|
|
56133
56855
|
cut = await deps.cut({
|
|
56134
56856
|
url: replay.url,
|
|
@@ -56157,8 +56879,11 @@ async function mintClipStill(deviceId, clipKey, deps) {
|
|
|
56157
56879
|
reason: cut.detail
|
|
56158
56880
|
}
|
|
56159
56881
|
});
|
|
56160
|
-
|
|
56882
|
+
const refusal = refuseStill(cut.code, cut.detail);
|
|
56883
|
+
deps.guard.recordFailure(deviceId, clipKey, refusal);
|
|
56884
|
+
return refusal;
|
|
56161
56885
|
}
|
|
56886
|
+
deps.guard.recordSuccess(deviceId);
|
|
56162
56887
|
await deps.putStill(deviceId, clipKey, cut.bytes);
|
|
56163
56888
|
deps.logger.debug("videoclips: minted a clip still from the camera’s replay", {
|
|
56164
56889
|
tags,
|
|
@@ -56176,6 +56901,35 @@ async function mintClipStill(deviceId, clipKey, deps) {
|
|
|
56176
56901
|
bytes: cut.bytes
|
|
56177
56902
|
};
|
|
56178
56903
|
}
|
|
56904
|
+
/** `camera-backoff` while this camera's stills are paused, else `null`. */
|
|
56905
|
+
function pausedRefusal(deviceId, clipKey, deps) {
|
|
56906
|
+
const leftMs = deps.guard.pausedForMs(deviceId);
|
|
56907
|
+
if (leftMs === null) return null;
|
|
56908
|
+
deps.logger.debug("videoclips: no still — this camera’s stills are paused", {
|
|
56909
|
+
tags: { deviceId },
|
|
56910
|
+
meta: {
|
|
56911
|
+
clipKey,
|
|
56912
|
+
branch: "camera-backoff",
|
|
56913
|
+
leftMs
|
|
56914
|
+
}
|
|
56915
|
+
});
|
|
56916
|
+
return deferStill("camera-backoff", `camera-backoff: this camera failed its last clip replays in a row, so clip stills are paused for another ${String(Math.ceil(leftMs / 1e3))} s — nothing about this clip`, leftMs);
|
|
56917
|
+
}
|
|
56918
|
+
/** A cache read that fails is not a still: answered as "not there". */
|
|
56919
|
+
async function readCachedSafely(deviceId, clipKey, deps) {
|
|
56920
|
+
try {
|
|
56921
|
+
return await deps.readCached(deviceId, clipKey);
|
|
56922
|
+
} catch (err) {
|
|
56923
|
+
deps.logger.debug("videoclips: the still cache could not be read after the queue", {
|
|
56924
|
+
tags: { deviceId },
|
|
56925
|
+
meta: {
|
|
56926
|
+
clipKey,
|
|
56927
|
+
error: err instanceof Error ? err.message : String(err)
|
|
56928
|
+
}
|
|
56929
|
+
});
|
|
56930
|
+
return null;
|
|
56931
|
+
}
|
|
56932
|
+
}
|
|
56179
56933
|
//#endregion
|
|
56180
56934
|
//#region src/clips/clip-twins.ts
|
|
56181
56935
|
/**
|
|
@@ -56827,9 +57581,15 @@ function createClipService(deps) {
|
|
|
56827
57581
|
* DEFERS — lives in `clip-still-mint.ts`; this is only the wiring that gives
|
|
56828
57582
|
* it a camera, a clock and a place to put the bytes.
|
|
56829
57583
|
*/
|
|
56830
|
-
const
|
|
57584
|
+
const stillGuard = createClipStillGuard({
|
|
57585
|
+
logger,
|
|
57586
|
+
now
|
|
57587
|
+
});
|
|
57588
|
+
const mintStill = async (deviceId, clipKey, signal) => await mintClipStill(deviceId, clipKey, {
|
|
56831
57589
|
logger,
|
|
56832
57590
|
lane,
|
|
57591
|
+
guard: stillGuard,
|
|
57592
|
+
readCached: async (id, key) => await thumbs.read(id, key),
|
|
56833
57593
|
hasCamera: async () => await deps.resolveCamera(deviceId) !== null,
|
|
56834
57594
|
rowFor: (id, key) => rowsByKey.get(rowKey(id, key)),
|
|
56835
57595
|
replayUrlFor: async (id, row, window) => {
|
|
@@ -56864,7 +57624,7 @@ function createClipService(deps) {
|
|
|
56864
57624
|
putStill: async (id, key, bytes) => {
|
|
56865
57625
|
await thumbs.put(id, key, bytes);
|
|
56866
57626
|
}
|
|
56867
|
-
});
|
|
57627
|
+
}, signal);
|
|
56868
57628
|
return {
|
|
56869
57629
|
provider: createAmcrestVideoclipsProvider({
|
|
56870
57630
|
now,
|