@camstack/addon-provider-velux 0.2.41 → 0.2.43
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 +154 -19
- package/dist/addon.mjs +154 -19
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -8039,18 +8039,61 @@ var RelocateFootageInputSchema = object({
|
|
|
8039
8039
|
* `RecordingConfig.enabled` or camera wrapper bindings. */
|
|
8040
8040
|
var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
|
|
8041
8041
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
|
|
8042
|
+
/**
|
|
8043
|
+
* What a `relocateMedia` pass DOES. One engine, three passes — never a second
|
|
8044
|
+
* mover (the engine already walks both collections with a timestamp cursor and
|
|
8045
|
+
* already has a stamp-without-copy path).
|
|
8046
|
+
*
|
|
8047
|
+
* - `move` — the default and the historical behaviour: event-media and
|
|
8048
|
+
* retrain blobs move to `toLocationId` and their rows are
|
|
8049
|
+
* stamped. The enrolled gallery is skipped (D197).
|
|
8050
|
+
* - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
|
|
8051
|
+
* stamped with `toLocationId`. `toLocationId` here is the id the
|
|
8052
|
+
* bytes ALREADY sit on — today's `eventMedia` default — because
|
|
8053
|
+
* a NULL row means "wherever `eventMedia` points *now*", and the
|
|
8054
|
+
* instant a repoint moves that pointer the row reads from the
|
|
8055
|
+
* new disk while its bytes are on the old one.
|
|
8056
|
+
* - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
|
|
8057
|
+
* (enrolled-gallery) rows, which `move` deliberately skips.
|
|
8058
|
+
* `galleryMedia` is `cardinality: 'single'`, so this pass can
|
|
8059
|
+
* never run beside a live second location: it is stop-the-world
|
|
8060
|
+
* by construction, which is acceptable only because the gallery
|
|
8061
|
+
* is a few KB per enrolled sample.
|
|
8062
|
+
*/
|
|
8063
|
+
var MediaRelocateModeSchema = _enum([
|
|
8064
|
+
"move",
|
|
8065
|
+
"seal",
|
|
8066
|
+
"gallery"
|
|
8067
|
+
]);
|
|
8042
8068
|
var RelocateMediaInputSchema = object({
|
|
8043
8069
|
toLocationId: string(),
|
|
8044
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8070
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8071
|
+
/** Omitted = `move`, the pre-existing behaviour. */
|
|
8072
|
+
mode: MediaRelocateModeSchema.optional()
|
|
8073
|
+
});
|
|
8074
|
+
/** How many rows still carry NO `locationId` — the population a repoint would
|
|
8075
|
+
* silently re-aim at a disk that does not hold their bytes. Zero is the only
|
|
8076
|
+
* value that permits a non-blocking `eventMedia` cutover. */
|
|
8077
|
+
var UnstampedEventMediaCountSchema = object({
|
|
8078
|
+
media: number().int().nonnegative(),
|
|
8079
|
+
retrainFrames: number().int().nonnegative(),
|
|
8080
|
+
total: number().int().nonnegative()
|
|
8045
8081
|
});
|
|
8046
8082
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
|
|
8047
|
-
/** The independently selectable logical storage classes
|
|
8048
|
-
*
|
|
8049
|
-
*
|
|
8083
|
+
/** The independently selectable logical storage classes — every class
|
|
8084
|
+
* `storage.listLocationDeclarations` reports, so an operator never meets a
|
|
8085
|
+
* Zod enum error where they should meet an explanation.
|
|
8086
|
+
*
|
|
8087
|
+
* `recordings` encompasses the high and mid segment profiles; `recordingsLow`
|
|
8088
|
+
* is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
|
|
8089
|
+
* enrolled gallery; `backups` is the system backup archive. The last two have
|
|
8090
|
+
* their own rules — see {@link StorageMigrationFindingCodeSchema}. */
|
|
8050
8091
|
var StorageMigrationClassSchema = _enum([
|
|
8051
8092
|
"recordings",
|
|
8052
8093
|
"recordingsLow",
|
|
8053
|
-
"eventMedia"
|
|
8094
|
+
"eventMedia",
|
|
8095
|
+
"backups",
|
|
8096
|
+
"galleryMedia"
|
|
8054
8097
|
]);
|
|
8055
8098
|
/** A destination is always an existing, fully-qualified location id. The
|
|
8056
8099
|
* migration API intentionally never changes a source location's `basePath`:
|
|
@@ -8058,20 +8101,56 @@ var StorageMigrationClassSchema = _enum([
|
|
|
8058
8101
|
var StorageMigrationDestinationsSchema = object({
|
|
8059
8102
|
recordings: string().min(1).optional(),
|
|
8060
8103
|
recordingsLow: string().min(1).optional(),
|
|
8061
|
-
eventMedia: string().min(1).optional()
|
|
8104
|
+
eventMedia: string().min(1).optional(),
|
|
8105
|
+
backups: string().min(1).optional(),
|
|
8106
|
+
galleryMedia: string().min(1).optional()
|
|
8062
8107
|
}).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
|
|
8108
|
+
/**
|
|
8109
|
+
* How a migration sequences the cutover against the byte move.
|
|
8110
|
+
*
|
|
8111
|
+
* - `blocking` — the historical order: pause, move every byte, repoint,
|
|
8112
|
+
* resume. Recording is stopped for the whole move. Right
|
|
8113
|
+
* for a small or a cold class, and the only legal mode for
|
|
8114
|
+
* a `cardinality: 'single'` class.
|
|
8115
|
+
* - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
|
|
8116
|
+
* refresh, resume, then move the past with everything
|
|
8117
|
+
* running. The pause is three bounded instants (a detach +
|
|
8118
|
+
* attach round, a write-gate drain, a lease) instead of one
|
|
8119
|
+
* bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
|
|
8120
|
+
* stopped recording under `blocking`; the same move is
|
|
8121
|
+
* seconds of stopped recording under `nonBlocking`.
|
|
8122
|
+
*
|
|
8123
|
+
* The mode is on the JOB, not only on the input, because `status` is where an
|
|
8124
|
+
* operator finds out which one is running.
|
|
8125
|
+
*/
|
|
8126
|
+
var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
|
|
8063
8127
|
/** Shared input for planning and starting an orchestrated storage migration. */
|
|
8064
8128
|
var StorageMigrationInputSchema = object({
|
|
8065
8129
|
destinations: StorageMigrationDestinationsSchema,
|
|
8066
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8130
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8131
|
+
/** Omitted = `blocking`, which stays the default. */
|
|
8132
|
+
mode: StorageMigrationModeSchema.optional()
|
|
8067
8133
|
});
|
|
8068
|
-
/**
|
|
8069
|
-
*
|
|
8070
|
-
*
|
|
8134
|
+
/**
|
|
8135
|
+
* The durable coordinator state machine.
|
|
8136
|
+
*
|
|
8137
|
+
* `blocking`:
|
|
8138
|
+
* planning → pausing → moving → verifying → repointing → refreshing → resuming → done
|
|
8139
|
+
*
|
|
8140
|
+
* `nonBlocking`:
|
|
8141
|
+
* planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
|
|
8142
|
+
*
|
|
8143
|
+
* Same phases, different order plus two new ones — not a second mover.
|
|
8144
|
+
* `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
|
|
8145
|
+
* `draining` runs the same movers UNLEASED, after every writer is back up.
|
|
8146
|
+
* `repointing` is still the only phase that changes a default location.
|
|
8147
|
+
*/
|
|
8071
8148
|
var StorageMigrationPhaseSchema = _enum([
|
|
8072
8149
|
"planning",
|
|
8150
|
+
"sealing",
|
|
8073
8151
|
"pausing",
|
|
8074
8152
|
"moving",
|
|
8153
|
+
"draining",
|
|
8075
8154
|
"verifying",
|
|
8076
8155
|
"repointing",
|
|
8077
8156
|
"refreshing",
|
|
@@ -8096,6 +8175,9 @@ var StorageMigrationMoveSchema = object({
|
|
|
8096
8175
|
var StorageMigrationJobSchema = object({
|
|
8097
8176
|
jobId: string(),
|
|
8098
8177
|
phase: StorageMigrationPhaseSchema,
|
|
8178
|
+
/** Which order this job is running. `status` is the only place an operator
|
|
8179
|
+
* can tell a seconds-long cutover from a thirty-hour one. */
|
|
8180
|
+
mode: StorageMigrationModeSchema,
|
|
8099
8181
|
destinations: StorageMigrationDestinationsSchema,
|
|
8100
8182
|
throttleMbps: number(),
|
|
8101
8183
|
moves: array(StorageMigrationMoveSchema),
|
|
@@ -8108,13 +8190,30 @@ var StorageMigrationJobSchema = object({
|
|
|
8108
8190
|
finishedAt: number().nullable(),
|
|
8109
8191
|
error: string().nullable()
|
|
8110
8192
|
});
|
|
8193
|
+
var StorageMigrationFindingSchema = object({
|
|
8194
|
+
code: _enum([
|
|
8195
|
+
"sharesDeviceWithSource",
|
|
8196
|
+
"deviceIdentityUnknown",
|
|
8197
|
+
"unstampedEventMediaRows",
|
|
8198
|
+
"blockingOnly",
|
|
8199
|
+
"noMover"
|
|
8200
|
+
]),
|
|
8201
|
+
storageClass: StorageMigrationClassSchema,
|
|
8202
|
+
/** Human-readable, already carrying the ids and counts. */
|
|
8203
|
+
message: string()
|
|
8204
|
+
});
|
|
8111
8205
|
var StorageMigrationPlanSchema = object({
|
|
8112
8206
|
destinations: StorageMigrationDestinationsSchema,
|
|
8207
|
+
/** The mode this plan was built for. A plan is only valid for its mode: the
|
|
8208
|
+
* `eventMedia` seal gate and the single-cardinality refusal both depend on
|
|
8209
|
+
* it. */
|
|
8210
|
+
mode: StorageMigrationModeSchema,
|
|
8113
8211
|
moves: array(object({
|
|
8114
8212
|
storageClass: StorageMigrationClassSchema,
|
|
8115
8213
|
fromLocationId: string(),
|
|
8116
8214
|
toLocationId: string()
|
|
8117
|
-
}))
|
|
8215
|
+
})),
|
|
8216
|
+
findings: array(StorageMigrationFindingSchema)
|
|
8118
8217
|
});
|
|
8119
8218
|
/**
|
|
8120
8219
|
* `StorageLocationType` — an addon-declared id that identifies the *kind* of
|
|
@@ -18967,7 +19066,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18967
19066
|
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
18968
19067
|
kind: "mutation",
|
|
18969
19068
|
auth: "admin"
|
|
18970
|
-
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
19069
|
+
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
18971
19070
|
kind: "query",
|
|
18972
19071
|
auth: "admin"
|
|
18973
19072
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
@@ -20874,7 +20973,10 @@ method(object({
|
|
|
20874
20973
|
}), StorageLocationSchema, {
|
|
20875
20974
|
kind: "mutation",
|
|
20876
20975
|
auth: "admin"
|
|
20877
|
-
}), method(object({
|
|
20976
|
+
}), method(object({
|
|
20977
|
+
id: string(),
|
|
20978
|
+
force: boolean().optional()
|
|
20979
|
+
}), _void(), {
|
|
20878
20980
|
kind: "mutation",
|
|
20879
20981
|
auth: "admin"
|
|
20880
20982
|
}), method(object({ id: string() }), object({
|
|
@@ -24942,6 +25044,33 @@ var intercomCapability = {
|
|
|
24942
25044
|
deviceNative: true,
|
|
24943
25045
|
mode: "singleton",
|
|
24944
25046
|
deviceTypes: [DeviceType.Camera],
|
|
25047
|
+
/**
|
|
25048
|
+
* **Auth tier: `protected` on every method — deliberate, and load-bearing.**
|
|
25049
|
+
*
|
|
25050
|
+
* Talking through a camera is an OPERATE action, not a CONFIGURE one. This
|
|
25051
|
+
* cap has no configuration surface at all: all six methods open, feed and
|
|
25052
|
+
* close one live audio session against one `deviceId`. That is the same
|
|
25053
|
+
* authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
|
|
25054
|
+
* the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
|
|
25055
|
+
* `admin` because they change what the device IS.
|
|
25056
|
+
*
|
|
25057
|
+
* `protected` does not mean ungated: `protectedProcedure` runs the
|
|
25058
|
+
* `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
|
|
25059
|
+
* with `access: 'create'` and a `deviceId` in its input. So a caller needs a
|
|
25060
|
+
* grant that covers THAT camera at `create` — a `camera-viewer` (`view`
|
|
25061
|
+
* only) still cannot talk, and a grant on camera 5 cannot talk through
|
|
25062
|
+
* camera 7.
|
|
25063
|
+
*
|
|
25064
|
+
* Every method was `auth: 'admin'` from the initial commit, which made the
|
|
25065
|
+
* cap unreachable by every non-admin principal — `adminProcedure` throws
|
|
25066
|
+
* `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
|
|
25067
|
+
* machinery generated for this cap (`METHOD_ACCESS_MAP`,
|
|
25068
|
+
* `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
|
|
25069
|
+
* `camera-operator` scope preset has promised "PTZ control, intercom,
|
|
25070
|
+
* snapshots" since that same commit; the promise could not be kept. Recorded
|
|
25071
|
+
* as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
|
|
25072
|
+
* preset promises a cap no row of that preset can reach.
|
|
25073
|
+
*/
|
|
24945
25074
|
methods: {
|
|
24946
25075
|
/**
|
|
24947
25076
|
* Open a server-side WebRTC audio-only session. Returns an SDP
|
|
@@ -24954,7 +25083,7 @@ var intercomCapability = {
|
|
|
24954
25083
|
sdpOffer: string()
|
|
24955
25084
|
}), {
|
|
24956
25085
|
kind: "mutation",
|
|
24957
|
-
auth: "
|
|
25086
|
+
auth: "protected"
|
|
24958
25087
|
}),
|
|
24959
25088
|
handleAnswer: method(object({
|
|
24960
25089
|
deviceId: number(),
|
|
@@ -24962,7 +25091,7 @@ var intercomCapability = {
|
|
|
24962
25091
|
sdpAnswer: string()
|
|
24963
25092
|
}), _void(), {
|
|
24964
25093
|
kind: "mutation",
|
|
24965
|
-
auth: "
|
|
25094
|
+
auth: "protected"
|
|
24966
25095
|
}),
|
|
24967
25096
|
/** Close explicitly. Server also auto-closes on 30s idle. */
|
|
24968
25097
|
stopSession: method(object({
|
|
@@ -24970,7 +25099,7 @@ var intercomCapability = {
|
|
|
24970
25099
|
sessionId: string()
|
|
24971
25100
|
}), _void(), {
|
|
24972
25101
|
kind: "mutation",
|
|
24973
|
-
auth: "
|
|
25102
|
+
auth: "protected"
|
|
24974
25103
|
}),
|
|
24975
25104
|
/**
|
|
24976
25105
|
* Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
|
|
@@ -24983,7 +25112,7 @@ var intercomCapability = {
|
|
|
24983
25112
|
*/
|
|
24984
25113
|
startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
|
|
24985
25114
|
kind: "mutation",
|
|
24986
|
-
auth: "
|
|
25115
|
+
auth: "protected"
|
|
24987
25116
|
}),
|
|
24988
25117
|
/**
|
|
24989
25118
|
* Push one chunk of talk-back audio onto the active talk session.
|
|
@@ -25018,12 +25147,12 @@ var intercomCapability = {
|
|
|
25018
25147
|
sequenceNumber: number().int()
|
|
25019
25148
|
}), object({ accepted: boolean() }), {
|
|
25020
25149
|
kind: "mutation",
|
|
25021
|
-
auth: "
|
|
25150
|
+
auth: "protected"
|
|
25022
25151
|
}),
|
|
25023
25152
|
/** Close the raw-PCM talk session. Idempotent. */
|
|
25024
25153
|
endTalkSession: method(object({ deviceId: number() }), _void(), {
|
|
25025
25154
|
kind: "mutation",
|
|
25026
|
-
auth: "
|
|
25155
|
+
auth: "protected"
|
|
25027
25156
|
})
|
|
25028
25157
|
},
|
|
25029
25158
|
events: { onStatusChanged: { data: object({
|
|
@@ -34704,6 +34833,12 @@ Object.freeze({
|
|
|
34704
34833
|
addonId: null,
|
|
34705
34834
|
access: "create"
|
|
34706
34835
|
},
|
|
34836
|
+
"pipelineAnalytics.countUnstampedEventMedia": {
|
|
34837
|
+
capName: "pipeline-analytics",
|
|
34838
|
+
capScope: "device",
|
|
34839
|
+
addonId: null,
|
|
34840
|
+
access: "view"
|
|
34841
|
+
},
|
|
34707
34842
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
34708
34843
|
capName: "pipeline-analytics",
|
|
34709
34844
|
capScope: "device",
|
package/dist/addon.mjs
CHANGED
|
@@ -8038,18 +8038,61 @@ var RelocateFootageInputSchema = object({
|
|
|
8038
8038
|
* `RecordingConfig.enabled` or camera wrapper bindings. */
|
|
8039
8039
|
var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
|
|
8040
8040
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
|
|
8041
|
+
/**
|
|
8042
|
+
* What a `relocateMedia` pass DOES. One engine, three passes — never a second
|
|
8043
|
+
* mover (the engine already walks both collections with a timestamp cursor and
|
|
8044
|
+
* already has a stamp-without-copy path).
|
|
8045
|
+
*
|
|
8046
|
+
* - `move` — the default and the historical behaviour: event-media and
|
|
8047
|
+
* retrain blobs move to `toLocationId` and their rows are
|
|
8048
|
+
* stamped. The enrolled gallery is skipped (D197).
|
|
8049
|
+
* - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
|
|
8050
|
+
* stamped with `toLocationId`. `toLocationId` here is the id the
|
|
8051
|
+
* bytes ALREADY sit on — today's `eventMedia` default — because
|
|
8052
|
+
* a NULL row means "wherever `eventMedia` points *now*", and the
|
|
8053
|
+
* instant a repoint moves that pointer the row reads from the
|
|
8054
|
+
* new disk while its bytes are on the old one.
|
|
8055
|
+
* - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
|
|
8056
|
+
* (enrolled-gallery) rows, which `move` deliberately skips.
|
|
8057
|
+
* `galleryMedia` is `cardinality: 'single'`, so this pass can
|
|
8058
|
+
* never run beside a live second location: it is stop-the-world
|
|
8059
|
+
* by construction, which is acceptable only because the gallery
|
|
8060
|
+
* is a few KB per enrolled sample.
|
|
8061
|
+
*/
|
|
8062
|
+
var MediaRelocateModeSchema = _enum([
|
|
8063
|
+
"move",
|
|
8064
|
+
"seal",
|
|
8065
|
+
"gallery"
|
|
8066
|
+
]);
|
|
8041
8067
|
var RelocateMediaInputSchema = object({
|
|
8042
8068
|
toLocationId: string(),
|
|
8043
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8069
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8070
|
+
/** Omitted = `move`, the pre-existing behaviour. */
|
|
8071
|
+
mode: MediaRelocateModeSchema.optional()
|
|
8072
|
+
});
|
|
8073
|
+
/** How many rows still carry NO `locationId` — the population a repoint would
|
|
8074
|
+
* silently re-aim at a disk that does not hold their bytes. Zero is the only
|
|
8075
|
+
* value that permits a non-blocking `eventMedia` cutover. */
|
|
8076
|
+
var UnstampedEventMediaCountSchema = object({
|
|
8077
|
+
media: number().int().nonnegative(),
|
|
8078
|
+
retrainFrames: number().int().nonnegative(),
|
|
8079
|
+
total: number().int().nonnegative()
|
|
8044
8080
|
});
|
|
8045
8081
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
|
|
8046
|
-
/** The independently selectable logical storage classes
|
|
8047
|
-
*
|
|
8048
|
-
*
|
|
8082
|
+
/** The independently selectable logical storage classes — every class
|
|
8083
|
+
* `storage.listLocationDeclarations` reports, so an operator never meets a
|
|
8084
|
+
* Zod enum error where they should meet an explanation.
|
|
8085
|
+
*
|
|
8086
|
+
* `recordings` encompasses the high and mid segment profiles; `recordingsLow`
|
|
8087
|
+
* is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
|
|
8088
|
+
* enrolled gallery; `backups` is the system backup archive. The last two have
|
|
8089
|
+
* their own rules — see {@link StorageMigrationFindingCodeSchema}. */
|
|
8049
8090
|
var StorageMigrationClassSchema = _enum([
|
|
8050
8091
|
"recordings",
|
|
8051
8092
|
"recordingsLow",
|
|
8052
|
-
"eventMedia"
|
|
8093
|
+
"eventMedia",
|
|
8094
|
+
"backups",
|
|
8095
|
+
"galleryMedia"
|
|
8053
8096
|
]);
|
|
8054
8097
|
/** A destination is always an existing, fully-qualified location id. The
|
|
8055
8098
|
* migration API intentionally never changes a source location's `basePath`:
|
|
@@ -8057,20 +8100,56 @@ var StorageMigrationClassSchema = _enum([
|
|
|
8057
8100
|
var StorageMigrationDestinationsSchema = object({
|
|
8058
8101
|
recordings: string().min(1).optional(),
|
|
8059
8102
|
recordingsLow: string().min(1).optional(),
|
|
8060
|
-
eventMedia: string().min(1).optional()
|
|
8103
|
+
eventMedia: string().min(1).optional(),
|
|
8104
|
+
backups: string().min(1).optional(),
|
|
8105
|
+
galleryMedia: string().min(1).optional()
|
|
8061
8106
|
}).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
|
|
8107
|
+
/**
|
|
8108
|
+
* How a migration sequences the cutover against the byte move.
|
|
8109
|
+
*
|
|
8110
|
+
* - `blocking` — the historical order: pause, move every byte, repoint,
|
|
8111
|
+
* resume. Recording is stopped for the whole move. Right
|
|
8112
|
+
* for a small or a cold class, and the only legal mode for
|
|
8113
|
+
* a `cardinality: 'single'` class.
|
|
8114
|
+
* - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
|
|
8115
|
+
* refresh, resume, then move the past with everything
|
|
8116
|
+
* running. The pause is three bounded instants (a detach +
|
|
8117
|
+
* attach round, a write-gate drain, a lease) instead of one
|
|
8118
|
+
* bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
|
|
8119
|
+
* stopped recording under `blocking`; the same move is
|
|
8120
|
+
* seconds of stopped recording under `nonBlocking`.
|
|
8121
|
+
*
|
|
8122
|
+
* The mode is on the JOB, not only on the input, because `status` is where an
|
|
8123
|
+
* operator finds out which one is running.
|
|
8124
|
+
*/
|
|
8125
|
+
var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
|
|
8062
8126
|
/** Shared input for planning and starting an orchestrated storage migration. */
|
|
8063
8127
|
var StorageMigrationInputSchema = object({
|
|
8064
8128
|
destinations: StorageMigrationDestinationsSchema,
|
|
8065
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8129
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8130
|
+
/** Omitted = `blocking`, which stays the default. */
|
|
8131
|
+
mode: StorageMigrationModeSchema.optional()
|
|
8066
8132
|
});
|
|
8067
|
-
/**
|
|
8068
|
-
*
|
|
8069
|
-
*
|
|
8133
|
+
/**
|
|
8134
|
+
* The durable coordinator state machine.
|
|
8135
|
+
*
|
|
8136
|
+
* `blocking`:
|
|
8137
|
+
* planning → pausing → moving → verifying → repointing → refreshing → resuming → done
|
|
8138
|
+
*
|
|
8139
|
+
* `nonBlocking`:
|
|
8140
|
+
* planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
|
|
8141
|
+
*
|
|
8142
|
+
* Same phases, different order plus two new ones — not a second mover.
|
|
8143
|
+
* `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
|
|
8144
|
+
* `draining` runs the same movers UNLEASED, after every writer is back up.
|
|
8145
|
+
* `repointing` is still the only phase that changes a default location.
|
|
8146
|
+
*/
|
|
8070
8147
|
var StorageMigrationPhaseSchema = _enum([
|
|
8071
8148
|
"planning",
|
|
8149
|
+
"sealing",
|
|
8072
8150
|
"pausing",
|
|
8073
8151
|
"moving",
|
|
8152
|
+
"draining",
|
|
8074
8153
|
"verifying",
|
|
8075
8154
|
"repointing",
|
|
8076
8155
|
"refreshing",
|
|
@@ -8095,6 +8174,9 @@ var StorageMigrationMoveSchema = object({
|
|
|
8095
8174
|
var StorageMigrationJobSchema = object({
|
|
8096
8175
|
jobId: string(),
|
|
8097
8176
|
phase: StorageMigrationPhaseSchema,
|
|
8177
|
+
/** Which order this job is running. `status` is the only place an operator
|
|
8178
|
+
* can tell a seconds-long cutover from a thirty-hour one. */
|
|
8179
|
+
mode: StorageMigrationModeSchema,
|
|
8098
8180
|
destinations: StorageMigrationDestinationsSchema,
|
|
8099
8181
|
throttleMbps: number(),
|
|
8100
8182
|
moves: array(StorageMigrationMoveSchema),
|
|
@@ -8107,13 +8189,30 @@ var StorageMigrationJobSchema = object({
|
|
|
8107
8189
|
finishedAt: number().nullable(),
|
|
8108
8190
|
error: string().nullable()
|
|
8109
8191
|
});
|
|
8192
|
+
var StorageMigrationFindingSchema = object({
|
|
8193
|
+
code: _enum([
|
|
8194
|
+
"sharesDeviceWithSource",
|
|
8195
|
+
"deviceIdentityUnknown",
|
|
8196
|
+
"unstampedEventMediaRows",
|
|
8197
|
+
"blockingOnly",
|
|
8198
|
+
"noMover"
|
|
8199
|
+
]),
|
|
8200
|
+
storageClass: StorageMigrationClassSchema,
|
|
8201
|
+
/** Human-readable, already carrying the ids and counts. */
|
|
8202
|
+
message: string()
|
|
8203
|
+
});
|
|
8110
8204
|
var StorageMigrationPlanSchema = object({
|
|
8111
8205
|
destinations: StorageMigrationDestinationsSchema,
|
|
8206
|
+
/** The mode this plan was built for. A plan is only valid for its mode: the
|
|
8207
|
+
* `eventMedia` seal gate and the single-cardinality refusal both depend on
|
|
8208
|
+
* it. */
|
|
8209
|
+
mode: StorageMigrationModeSchema,
|
|
8112
8210
|
moves: array(object({
|
|
8113
8211
|
storageClass: StorageMigrationClassSchema,
|
|
8114
8212
|
fromLocationId: string(),
|
|
8115
8213
|
toLocationId: string()
|
|
8116
|
-
}))
|
|
8214
|
+
})),
|
|
8215
|
+
findings: array(StorageMigrationFindingSchema)
|
|
8117
8216
|
});
|
|
8118
8217
|
/**
|
|
8119
8218
|
* `StorageLocationType` — an addon-declared id that identifies the *kind* of
|
|
@@ -18966,7 +19065,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18966
19065
|
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
18967
19066
|
kind: "mutation",
|
|
18968
19067
|
auth: "admin"
|
|
18969
|
-
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
19068
|
+
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
18970
19069
|
kind: "query",
|
|
18971
19070
|
auth: "admin"
|
|
18972
19071
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
@@ -20873,7 +20972,10 @@ method(object({
|
|
|
20873
20972
|
}), StorageLocationSchema, {
|
|
20874
20973
|
kind: "mutation",
|
|
20875
20974
|
auth: "admin"
|
|
20876
|
-
}), method(object({
|
|
20975
|
+
}), method(object({
|
|
20976
|
+
id: string(),
|
|
20977
|
+
force: boolean().optional()
|
|
20978
|
+
}), _void(), {
|
|
20877
20979
|
kind: "mutation",
|
|
20878
20980
|
auth: "admin"
|
|
20879
20981
|
}), method(object({ id: string() }), object({
|
|
@@ -24941,6 +25043,33 @@ var intercomCapability = {
|
|
|
24941
25043
|
deviceNative: true,
|
|
24942
25044
|
mode: "singleton",
|
|
24943
25045
|
deviceTypes: [DeviceType.Camera],
|
|
25046
|
+
/**
|
|
25047
|
+
* **Auth tier: `protected` on every method — deliberate, and load-bearing.**
|
|
25048
|
+
*
|
|
25049
|
+
* Talking through a camera is an OPERATE action, not a CONFIGURE one. This
|
|
25050
|
+
* cap has no configuration surface at all: all six methods open, feed and
|
|
25051
|
+
* close one live audio session against one `deviceId`. That is the same
|
|
25052
|
+
* authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
|
|
25053
|
+
* the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
|
|
25054
|
+
* `admin` because they change what the device IS.
|
|
25055
|
+
*
|
|
25056
|
+
* `protected` does not mean ungated: `protectedProcedure` runs the
|
|
25057
|
+
* `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
|
|
25058
|
+
* with `access: 'create'` and a `deviceId` in its input. So a caller needs a
|
|
25059
|
+
* grant that covers THAT camera at `create` — a `camera-viewer` (`view`
|
|
25060
|
+
* only) still cannot talk, and a grant on camera 5 cannot talk through
|
|
25061
|
+
* camera 7.
|
|
25062
|
+
*
|
|
25063
|
+
* Every method was `auth: 'admin'` from the initial commit, which made the
|
|
25064
|
+
* cap unreachable by every non-admin principal — `adminProcedure` throws
|
|
25065
|
+
* `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
|
|
25066
|
+
* machinery generated for this cap (`METHOD_ACCESS_MAP`,
|
|
25067
|
+
* `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
|
|
25068
|
+
* `camera-operator` scope preset has promised "PTZ control, intercom,
|
|
25069
|
+
* snapshots" since that same commit; the promise could not be kept. Recorded
|
|
25070
|
+
* as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
|
|
25071
|
+
* preset promises a cap no row of that preset can reach.
|
|
25072
|
+
*/
|
|
24944
25073
|
methods: {
|
|
24945
25074
|
/**
|
|
24946
25075
|
* Open a server-side WebRTC audio-only session. Returns an SDP
|
|
@@ -24953,7 +25082,7 @@ var intercomCapability = {
|
|
|
24953
25082
|
sdpOffer: string()
|
|
24954
25083
|
}), {
|
|
24955
25084
|
kind: "mutation",
|
|
24956
|
-
auth: "
|
|
25085
|
+
auth: "protected"
|
|
24957
25086
|
}),
|
|
24958
25087
|
handleAnswer: method(object({
|
|
24959
25088
|
deviceId: number(),
|
|
@@ -24961,7 +25090,7 @@ var intercomCapability = {
|
|
|
24961
25090
|
sdpAnswer: string()
|
|
24962
25091
|
}), _void(), {
|
|
24963
25092
|
kind: "mutation",
|
|
24964
|
-
auth: "
|
|
25093
|
+
auth: "protected"
|
|
24965
25094
|
}),
|
|
24966
25095
|
/** Close explicitly. Server also auto-closes on 30s idle. */
|
|
24967
25096
|
stopSession: method(object({
|
|
@@ -24969,7 +25098,7 @@ var intercomCapability = {
|
|
|
24969
25098
|
sessionId: string()
|
|
24970
25099
|
}), _void(), {
|
|
24971
25100
|
kind: "mutation",
|
|
24972
|
-
auth: "
|
|
25101
|
+
auth: "protected"
|
|
24973
25102
|
}),
|
|
24974
25103
|
/**
|
|
24975
25104
|
* Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
|
|
@@ -24982,7 +25111,7 @@ var intercomCapability = {
|
|
|
24982
25111
|
*/
|
|
24983
25112
|
startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
|
|
24984
25113
|
kind: "mutation",
|
|
24985
|
-
auth: "
|
|
25114
|
+
auth: "protected"
|
|
24986
25115
|
}),
|
|
24987
25116
|
/**
|
|
24988
25117
|
* Push one chunk of talk-back audio onto the active talk session.
|
|
@@ -25017,12 +25146,12 @@ var intercomCapability = {
|
|
|
25017
25146
|
sequenceNumber: number().int()
|
|
25018
25147
|
}), object({ accepted: boolean() }), {
|
|
25019
25148
|
kind: "mutation",
|
|
25020
|
-
auth: "
|
|
25149
|
+
auth: "protected"
|
|
25021
25150
|
}),
|
|
25022
25151
|
/** Close the raw-PCM talk session. Idempotent. */
|
|
25023
25152
|
endTalkSession: method(object({ deviceId: number() }), _void(), {
|
|
25024
25153
|
kind: "mutation",
|
|
25025
|
-
auth: "
|
|
25154
|
+
auth: "protected"
|
|
25026
25155
|
})
|
|
25027
25156
|
},
|
|
25028
25157
|
events: { onStatusChanged: { data: object({
|
|
@@ -34703,6 +34832,12 @@ Object.freeze({
|
|
|
34703
34832
|
addonId: null,
|
|
34704
34833
|
access: "create"
|
|
34705
34834
|
},
|
|
34835
|
+
"pipelineAnalytics.countUnstampedEventMedia": {
|
|
34836
|
+
capName: "pipeline-analytics",
|
|
34837
|
+
capScope: "device",
|
|
34838
|
+
addonId: null,
|
|
34839
|
+
access: "view"
|
|
34840
|
+
},
|
|
34706
34841
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
34707
34842
|
capName: "pipeline-analytics",
|
|
34708
34843
|
capScope: "device",
|
package/package.json
CHANGED