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