@camstack/addon-provider-rtsp 1.2.41 → 1.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
|
@@ -8095,18 +8095,61 @@ var RelocateFootageInputSchema = object({
|
|
|
8095
8095
|
* `RecordingConfig.enabled` or camera wrapper bindings. */
|
|
8096
8096
|
var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
|
|
8097
8097
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
|
|
8098
|
+
/**
|
|
8099
|
+
* What a `relocateMedia` pass DOES. One engine, three passes — never a second
|
|
8100
|
+
* mover (the engine already walks both collections with a timestamp cursor and
|
|
8101
|
+
* already has a stamp-without-copy path).
|
|
8102
|
+
*
|
|
8103
|
+
* - `move` — the default and the historical behaviour: event-media and
|
|
8104
|
+
* retrain blobs move to `toLocationId` and their rows are
|
|
8105
|
+
* stamped. The enrolled gallery is skipped (D197).
|
|
8106
|
+
* - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
|
|
8107
|
+
* stamped with `toLocationId`. `toLocationId` here is the id the
|
|
8108
|
+
* bytes ALREADY sit on — today's `eventMedia` default — because
|
|
8109
|
+
* a NULL row means "wherever `eventMedia` points *now*", and the
|
|
8110
|
+
* instant a repoint moves that pointer the row reads from the
|
|
8111
|
+
* new disk while its bytes are on the old one.
|
|
8112
|
+
* - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
|
|
8113
|
+
* (enrolled-gallery) rows, which `move` deliberately skips.
|
|
8114
|
+
* `galleryMedia` is `cardinality: 'single'`, so this pass can
|
|
8115
|
+
* never run beside a live second location: it is stop-the-world
|
|
8116
|
+
* by construction, which is acceptable only because the gallery
|
|
8117
|
+
* is a few KB per enrolled sample.
|
|
8118
|
+
*/
|
|
8119
|
+
var MediaRelocateModeSchema = _enum([
|
|
8120
|
+
"move",
|
|
8121
|
+
"seal",
|
|
8122
|
+
"gallery"
|
|
8123
|
+
]);
|
|
8098
8124
|
var RelocateMediaInputSchema = object({
|
|
8099
8125
|
toLocationId: string(),
|
|
8100
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8126
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8127
|
+
/** Omitted = `move`, the pre-existing behaviour. */
|
|
8128
|
+
mode: MediaRelocateModeSchema.optional()
|
|
8129
|
+
});
|
|
8130
|
+
/** How many rows still carry NO `locationId` — the population a repoint would
|
|
8131
|
+
* silently re-aim at a disk that does not hold their bytes. Zero is the only
|
|
8132
|
+
* value that permits a non-blocking `eventMedia` cutover. */
|
|
8133
|
+
var UnstampedEventMediaCountSchema = object({
|
|
8134
|
+
media: number().int().nonnegative(),
|
|
8135
|
+
retrainFrames: number().int().nonnegative(),
|
|
8136
|
+
total: number().int().nonnegative()
|
|
8101
8137
|
});
|
|
8102
8138
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
|
|
8103
|
-
/** The independently selectable logical storage classes
|
|
8104
|
-
*
|
|
8105
|
-
*
|
|
8139
|
+
/** The independently selectable logical storage classes — every class
|
|
8140
|
+
* `storage.listLocationDeclarations` reports, so an operator never meets a
|
|
8141
|
+
* Zod enum error where they should meet an explanation.
|
|
8142
|
+
*
|
|
8143
|
+
* `recordings` encompasses the high and mid segment profiles; `recordingsLow`
|
|
8144
|
+
* is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
|
|
8145
|
+
* enrolled gallery; `backups` is the system backup archive. The last two have
|
|
8146
|
+
* their own rules — see {@link StorageMigrationFindingCodeSchema}. */
|
|
8106
8147
|
var StorageMigrationClassSchema = _enum([
|
|
8107
8148
|
"recordings",
|
|
8108
8149
|
"recordingsLow",
|
|
8109
|
-
"eventMedia"
|
|
8150
|
+
"eventMedia",
|
|
8151
|
+
"backups",
|
|
8152
|
+
"galleryMedia"
|
|
8110
8153
|
]);
|
|
8111
8154
|
/** A destination is always an existing, fully-qualified location id. The
|
|
8112
8155
|
* migration API intentionally never changes a source location's `basePath`:
|
|
@@ -8114,20 +8157,56 @@ var StorageMigrationClassSchema = _enum([
|
|
|
8114
8157
|
var StorageMigrationDestinationsSchema = object({
|
|
8115
8158
|
recordings: string().min(1).optional(),
|
|
8116
8159
|
recordingsLow: string().min(1).optional(),
|
|
8117
|
-
eventMedia: string().min(1).optional()
|
|
8160
|
+
eventMedia: string().min(1).optional(),
|
|
8161
|
+
backups: string().min(1).optional(),
|
|
8162
|
+
galleryMedia: string().min(1).optional()
|
|
8118
8163
|
}).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
|
|
8164
|
+
/**
|
|
8165
|
+
* How a migration sequences the cutover against the byte move.
|
|
8166
|
+
*
|
|
8167
|
+
* - `blocking` — the historical order: pause, move every byte, repoint,
|
|
8168
|
+
* resume. Recording is stopped for the whole move. Right
|
|
8169
|
+
* for a small or a cold class, and the only legal mode for
|
|
8170
|
+
* a `cardinality: 'single'` class.
|
|
8171
|
+
* - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
|
|
8172
|
+
* refresh, resume, then move the past with everything
|
|
8173
|
+
* running. The pause is three bounded instants (a detach +
|
|
8174
|
+
* attach round, a write-gate drain, a lease) instead of one
|
|
8175
|
+
* bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
|
|
8176
|
+
* stopped recording under `blocking`; the same move is
|
|
8177
|
+
* seconds of stopped recording under `nonBlocking`.
|
|
8178
|
+
*
|
|
8179
|
+
* The mode is on the JOB, not only on the input, because `status` is where an
|
|
8180
|
+
* operator finds out which one is running.
|
|
8181
|
+
*/
|
|
8182
|
+
var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
|
|
8119
8183
|
/** Shared input for planning and starting an orchestrated storage migration. */
|
|
8120
8184
|
var StorageMigrationInputSchema = object({
|
|
8121
8185
|
destinations: StorageMigrationDestinationsSchema,
|
|
8122
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8186
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8187
|
+
/** Omitted = `blocking`, which stays the default. */
|
|
8188
|
+
mode: StorageMigrationModeSchema.optional()
|
|
8123
8189
|
});
|
|
8124
|
-
/**
|
|
8125
|
-
*
|
|
8126
|
-
*
|
|
8190
|
+
/**
|
|
8191
|
+
* The durable coordinator state machine.
|
|
8192
|
+
*
|
|
8193
|
+
* `blocking`:
|
|
8194
|
+
* planning → pausing → moving → verifying → repointing → refreshing → resuming → done
|
|
8195
|
+
*
|
|
8196
|
+
* `nonBlocking`:
|
|
8197
|
+
* planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
|
|
8198
|
+
*
|
|
8199
|
+
* Same phases, different order plus two new ones — not a second mover.
|
|
8200
|
+
* `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
|
|
8201
|
+
* `draining` runs the same movers UNLEASED, after every writer is back up.
|
|
8202
|
+
* `repointing` is still the only phase that changes a default location.
|
|
8203
|
+
*/
|
|
8127
8204
|
var StorageMigrationPhaseSchema = _enum([
|
|
8128
8205
|
"planning",
|
|
8206
|
+
"sealing",
|
|
8129
8207
|
"pausing",
|
|
8130
8208
|
"moving",
|
|
8209
|
+
"draining",
|
|
8131
8210
|
"verifying",
|
|
8132
8211
|
"repointing",
|
|
8133
8212
|
"refreshing",
|
|
@@ -8152,6 +8231,9 @@ var StorageMigrationMoveSchema = object({
|
|
|
8152
8231
|
var StorageMigrationJobSchema = object({
|
|
8153
8232
|
jobId: string(),
|
|
8154
8233
|
phase: StorageMigrationPhaseSchema,
|
|
8234
|
+
/** Which order this job is running. `status` is the only place an operator
|
|
8235
|
+
* can tell a seconds-long cutover from a thirty-hour one. */
|
|
8236
|
+
mode: StorageMigrationModeSchema,
|
|
8155
8237
|
destinations: StorageMigrationDestinationsSchema,
|
|
8156
8238
|
throttleMbps: number(),
|
|
8157
8239
|
moves: array(StorageMigrationMoveSchema),
|
|
@@ -8164,13 +8246,30 @@ var StorageMigrationJobSchema = object({
|
|
|
8164
8246
|
finishedAt: number().nullable(),
|
|
8165
8247
|
error: string().nullable()
|
|
8166
8248
|
});
|
|
8249
|
+
var StorageMigrationFindingSchema = object({
|
|
8250
|
+
code: _enum([
|
|
8251
|
+
"sharesDeviceWithSource",
|
|
8252
|
+
"deviceIdentityUnknown",
|
|
8253
|
+
"unstampedEventMediaRows",
|
|
8254
|
+
"blockingOnly",
|
|
8255
|
+
"noMover"
|
|
8256
|
+
]),
|
|
8257
|
+
storageClass: StorageMigrationClassSchema,
|
|
8258
|
+
/** Human-readable, already carrying the ids and counts. */
|
|
8259
|
+
message: string()
|
|
8260
|
+
});
|
|
8167
8261
|
var StorageMigrationPlanSchema = object({
|
|
8168
8262
|
destinations: StorageMigrationDestinationsSchema,
|
|
8263
|
+
/** The mode this plan was built for. A plan is only valid for its mode: the
|
|
8264
|
+
* `eventMedia` seal gate and the single-cardinality refusal both depend on
|
|
8265
|
+
* it. */
|
|
8266
|
+
mode: StorageMigrationModeSchema,
|
|
8169
8267
|
moves: array(object({
|
|
8170
8268
|
storageClass: StorageMigrationClassSchema,
|
|
8171
8269
|
fromLocationId: string(),
|
|
8172
8270
|
toLocationId: string()
|
|
8173
|
-
}))
|
|
8271
|
+
})),
|
|
8272
|
+
findings: array(StorageMigrationFindingSchema)
|
|
8174
8273
|
});
|
|
8175
8274
|
/**
|
|
8176
8275
|
* `StorageLocationType` — an addon-declared id that identifies the *kind* of
|
|
@@ -19023,7 +19122,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19023
19122
|
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
19024
19123
|
kind: "mutation",
|
|
19025
19124
|
auth: "admin"
|
|
19026
|
-
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
19125
|
+
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
19027
19126
|
kind: "query",
|
|
19028
19127
|
auth: "admin"
|
|
19029
19128
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
@@ -21034,7 +21133,10 @@ method(object({
|
|
|
21034
21133
|
}), StorageLocationSchema, {
|
|
21035
21134
|
kind: "mutation",
|
|
21036
21135
|
auth: "admin"
|
|
21037
|
-
}), method(object({
|
|
21136
|
+
}), method(object({
|
|
21137
|
+
id: string(),
|
|
21138
|
+
force: boolean().optional()
|
|
21139
|
+
}), _void(), {
|
|
21038
21140
|
kind: "mutation",
|
|
21039
21141
|
auth: "admin"
|
|
21040
21142
|
}), method(object({ id: string() }), object({
|
|
@@ -25110,6 +25212,33 @@ var intercomCapability = {
|
|
|
25110
25212
|
deviceNative: true,
|
|
25111
25213
|
mode: "singleton",
|
|
25112
25214
|
deviceTypes: [DeviceType.Camera],
|
|
25215
|
+
/**
|
|
25216
|
+
* **Auth tier: `protected` on every method — deliberate, and load-bearing.**
|
|
25217
|
+
*
|
|
25218
|
+
* Talking through a camera is an OPERATE action, not a CONFIGURE one. This
|
|
25219
|
+
* cap has no configuration surface at all: all six methods open, feed and
|
|
25220
|
+
* close one live audio session against one `deviceId`. That is the same
|
|
25221
|
+
* authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
|
|
25222
|
+
* the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
|
|
25223
|
+
* `admin` because they change what the device IS.
|
|
25224
|
+
*
|
|
25225
|
+
* `protected` does not mean ungated: `protectedProcedure` runs the
|
|
25226
|
+
* `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
|
|
25227
|
+
* with `access: 'create'` and a `deviceId` in its input. So a caller needs a
|
|
25228
|
+
* grant that covers THAT camera at `create` — a `camera-viewer` (`view`
|
|
25229
|
+
* only) still cannot talk, and a grant on camera 5 cannot talk through
|
|
25230
|
+
* camera 7.
|
|
25231
|
+
*
|
|
25232
|
+
* Every method was `auth: 'admin'` from the initial commit, which made the
|
|
25233
|
+
* cap unreachable by every non-admin principal — `adminProcedure` throws
|
|
25234
|
+
* `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
|
|
25235
|
+
* machinery generated for this cap (`METHOD_ACCESS_MAP`,
|
|
25236
|
+
* `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
|
|
25237
|
+
* `camera-operator` scope preset has promised "PTZ control, intercom,
|
|
25238
|
+
* snapshots" since that same commit; the promise could not be kept. Recorded
|
|
25239
|
+
* as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
|
|
25240
|
+
* preset promises a cap no row of that preset can reach.
|
|
25241
|
+
*/
|
|
25113
25242
|
methods: {
|
|
25114
25243
|
/**
|
|
25115
25244
|
* Open a server-side WebRTC audio-only session. Returns an SDP
|
|
@@ -25122,7 +25251,7 @@ var intercomCapability = {
|
|
|
25122
25251
|
sdpOffer: string()
|
|
25123
25252
|
}), {
|
|
25124
25253
|
kind: "mutation",
|
|
25125
|
-
auth: "
|
|
25254
|
+
auth: "protected"
|
|
25126
25255
|
}),
|
|
25127
25256
|
handleAnswer: method(object({
|
|
25128
25257
|
deviceId: number(),
|
|
@@ -25130,7 +25259,7 @@ var intercomCapability = {
|
|
|
25130
25259
|
sdpAnswer: string()
|
|
25131
25260
|
}), _void(), {
|
|
25132
25261
|
kind: "mutation",
|
|
25133
|
-
auth: "
|
|
25262
|
+
auth: "protected"
|
|
25134
25263
|
}),
|
|
25135
25264
|
/** Close explicitly. Server also auto-closes on 30s idle. */
|
|
25136
25265
|
stopSession: method(object({
|
|
@@ -25138,7 +25267,7 @@ var intercomCapability = {
|
|
|
25138
25267
|
sessionId: string()
|
|
25139
25268
|
}), _void(), {
|
|
25140
25269
|
kind: "mutation",
|
|
25141
|
-
auth: "
|
|
25270
|
+
auth: "protected"
|
|
25142
25271
|
}),
|
|
25143
25272
|
/**
|
|
25144
25273
|
* Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
|
|
@@ -25151,7 +25280,7 @@ var intercomCapability = {
|
|
|
25151
25280
|
*/
|
|
25152
25281
|
startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
|
|
25153
25282
|
kind: "mutation",
|
|
25154
|
-
auth: "
|
|
25283
|
+
auth: "protected"
|
|
25155
25284
|
}),
|
|
25156
25285
|
/**
|
|
25157
25286
|
* Push one chunk of talk-back audio onto the active talk session.
|
|
@@ -25186,12 +25315,12 @@ var intercomCapability = {
|
|
|
25186
25315
|
sequenceNumber: number().int()
|
|
25187
25316
|
}), object({ accepted: boolean() }), {
|
|
25188
25317
|
kind: "mutation",
|
|
25189
|
-
auth: "
|
|
25318
|
+
auth: "protected"
|
|
25190
25319
|
}),
|
|
25191
25320
|
/** Close the raw-PCM talk session. Idempotent. */
|
|
25192
25321
|
endTalkSession: method(object({ deviceId: number() }), _void(), {
|
|
25193
25322
|
kind: "mutation",
|
|
25194
|
-
auth: "
|
|
25323
|
+
auth: "protected"
|
|
25195
25324
|
})
|
|
25196
25325
|
},
|
|
25197
25326
|
events: { onStatusChanged: { data: object({
|
|
@@ -34938,6 +35067,12 @@ Object.freeze({
|
|
|
34938
35067
|
addonId: null,
|
|
34939
35068
|
access: "create"
|
|
34940
35069
|
},
|
|
35070
|
+
"pipelineAnalytics.countUnstampedEventMedia": {
|
|
35071
|
+
capName: "pipeline-analytics",
|
|
35072
|
+
capScope: "device",
|
|
35073
|
+
addonId: null,
|
|
35074
|
+
access: "view"
|
|
35075
|
+
},
|
|
34941
35076
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
34942
35077
|
capName: "pipeline-analytics",
|
|
34943
35078
|
capScope: "device",
|
package/dist/addon.mjs
CHANGED
|
@@ -8071,18 +8071,61 @@ var RelocateFootageInputSchema = object({
|
|
|
8071
8071
|
* `RecordingConfig.enabled` or camera wrapper bindings. */
|
|
8072
8072
|
var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
|
|
8073
8073
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
|
|
8074
|
+
/**
|
|
8075
|
+
* What a `relocateMedia` pass DOES. One engine, three passes — never a second
|
|
8076
|
+
* mover (the engine already walks both collections with a timestamp cursor and
|
|
8077
|
+
* already has a stamp-without-copy path).
|
|
8078
|
+
*
|
|
8079
|
+
* - `move` — the default and the historical behaviour: event-media and
|
|
8080
|
+
* retrain blobs move to `toLocationId` and their rows are
|
|
8081
|
+
* stamped. The enrolled gallery is skipped (D197).
|
|
8082
|
+
* - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
|
|
8083
|
+
* stamped with `toLocationId`. `toLocationId` here is the id the
|
|
8084
|
+
* bytes ALREADY sit on — today's `eventMedia` default — because
|
|
8085
|
+
* a NULL row means "wherever `eventMedia` points *now*", and the
|
|
8086
|
+
* instant a repoint moves that pointer the row reads from the
|
|
8087
|
+
* new disk while its bytes are on the old one.
|
|
8088
|
+
* - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
|
|
8089
|
+
* (enrolled-gallery) rows, which `move` deliberately skips.
|
|
8090
|
+
* `galleryMedia` is `cardinality: 'single'`, so this pass can
|
|
8091
|
+
* never run beside a live second location: it is stop-the-world
|
|
8092
|
+
* by construction, which is acceptable only because the gallery
|
|
8093
|
+
* is a few KB per enrolled sample.
|
|
8094
|
+
*/
|
|
8095
|
+
var MediaRelocateModeSchema = _enum([
|
|
8096
|
+
"move",
|
|
8097
|
+
"seal",
|
|
8098
|
+
"gallery"
|
|
8099
|
+
]);
|
|
8074
8100
|
var RelocateMediaInputSchema = object({
|
|
8075
8101
|
toLocationId: string(),
|
|
8076
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8102
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8103
|
+
/** Omitted = `move`, the pre-existing behaviour. */
|
|
8104
|
+
mode: MediaRelocateModeSchema.optional()
|
|
8105
|
+
});
|
|
8106
|
+
/** How many rows still carry NO `locationId` — the population a repoint would
|
|
8107
|
+
* silently re-aim at a disk that does not hold their bytes. Zero is the only
|
|
8108
|
+
* value that permits a non-blocking `eventMedia` cutover. */
|
|
8109
|
+
var UnstampedEventMediaCountSchema = object({
|
|
8110
|
+
media: number().int().nonnegative(),
|
|
8111
|
+
retrainFrames: number().int().nonnegative(),
|
|
8112
|
+
total: number().int().nonnegative()
|
|
8077
8113
|
});
|
|
8078
8114
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
|
|
8079
|
-
/** The independently selectable logical storage classes
|
|
8080
|
-
*
|
|
8081
|
-
*
|
|
8115
|
+
/** The independently selectable logical storage classes — every class
|
|
8116
|
+
* `storage.listLocationDeclarations` reports, so an operator never meets a
|
|
8117
|
+
* Zod enum error where they should meet an explanation.
|
|
8118
|
+
*
|
|
8119
|
+
* `recordings` encompasses the high and mid segment profiles; `recordingsLow`
|
|
8120
|
+
* is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
|
|
8121
|
+
* enrolled gallery; `backups` is the system backup archive. The last two have
|
|
8122
|
+
* their own rules — see {@link StorageMigrationFindingCodeSchema}. */
|
|
8082
8123
|
var StorageMigrationClassSchema = _enum([
|
|
8083
8124
|
"recordings",
|
|
8084
8125
|
"recordingsLow",
|
|
8085
|
-
"eventMedia"
|
|
8126
|
+
"eventMedia",
|
|
8127
|
+
"backups",
|
|
8128
|
+
"galleryMedia"
|
|
8086
8129
|
]);
|
|
8087
8130
|
/** A destination is always an existing, fully-qualified location id. The
|
|
8088
8131
|
* migration API intentionally never changes a source location's `basePath`:
|
|
@@ -8090,20 +8133,56 @@ var StorageMigrationClassSchema = _enum([
|
|
|
8090
8133
|
var StorageMigrationDestinationsSchema = object({
|
|
8091
8134
|
recordings: string().min(1).optional(),
|
|
8092
8135
|
recordingsLow: string().min(1).optional(),
|
|
8093
|
-
eventMedia: string().min(1).optional()
|
|
8136
|
+
eventMedia: string().min(1).optional(),
|
|
8137
|
+
backups: string().min(1).optional(),
|
|
8138
|
+
galleryMedia: string().min(1).optional()
|
|
8094
8139
|
}).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
|
|
8140
|
+
/**
|
|
8141
|
+
* How a migration sequences the cutover against the byte move.
|
|
8142
|
+
*
|
|
8143
|
+
* - `blocking` — the historical order: pause, move every byte, repoint,
|
|
8144
|
+
* resume. Recording is stopped for the whole move. Right
|
|
8145
|
+
* for a small or a cold class, and the only legal mode for
|
|
8146
|
+
* a `cardinality: 'single'` class.
|
|
8147
|
+
* - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
|
|
8148
|
+
* refresh, resume, then move the past with everything
|
|
8149
|
+
* running. The pause is three bounded instants (a detach +
|
|
8150
|
+
* attach round, a write-gate drain, a lease) instead of one
|
|
8151
|
+
* bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
|
|
8152
|
+
* stopped recording under `blocking`; the same move is
|
|
8153
|
+
* seconds of stopped recording under `nonBlocking`.
|
|
8154
|
+
*
|
|
8155
|
+
* The mode is on the JOB, not only on the input, because `status` is where an
|
|
8156
|
+
* operator finds out which one is running.
|
|
8157
|
+
*/
|
|
8158
|
+
var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
|
|
8095
8159
|
/** Shared input for planning and starting an orchestrated storage migration. */
|
|
8096
8160
|
var StorageMigrationInputSchema = object({
|
|
8097
8161
|
destinations: StorageMigrationDestinationsSchema,
|
|
8098
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8162
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8163
|
+
/** Omitted = `blocking`, which stays the default. */
|
|
8164
|
+
mode: StorageMigrationModeSchema.optional()
|
|
8099
8165
|
});
|
|
8100
|
-
/**
|
|
8101
|
-
*
|
|
8102
|
-
*
|
|
8166
|
+
/**
|
|
8167
|
+
* The durable coordinator state machine.
|
|
8168
|
+
*
|
|
8169
|
+
* `blocking`:
|
|
8170
|
+
* planning → pausing → moving → verifying → repointing → refreshing → resuming → done
|
|
8171
|
+
*
|
|
8172
|
+
* `nonBlocking`:
|
|
8173
|
+
* planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
|
|
8174
|
+
*
|
|
8175
|
+
* Same phases, different order plus two new ones — not a second mover.
|
|
8176
|
+
* `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
|
|
8177
|
+
* `draining` runs the same movers UNLEASED, after every writer is back up.
|
|
8178
|
+
* `repointing` is still the only phase that changes a default location.
|
|
8179
|
+
*/
|
|
8103
8180
|
var StorageMigrationPhaseSchema = _enum([
|
|
8104
8181
|
"planning",
|
|
8182
|
+
"sealing",
|
|
8105
8183
|
"pausing",
|
|
8106
8184
|
"moving",
|
|
8185
|
+
"draining",
|
|
8107
8186
|
"verifying",
|
|
8108
8187
|
"repointing",
|
|
8109
8188
|
"refreshing",
|
|
@@ -8128,6 +8207,9 @@ var StorageMigrationMoveSchema = object({
|
|
|
8128
8207
|
var StorageMigrationJobSchema = object({
|
|
8129
8208
|
jobId: string(),
|
|
8130
8209
|
phase: StorageMigrationPhaseSchema,
|
|
8210
|
+
/** Which order this job is running. `status` is the only place an operator
|
|
8211
|
+
* can tell a seconds-long cutover from a thirty-hour one. */
|
|
8212
|
+
mode: StorageMigrationModeSchema,
|
|
8131
8213
|
destinations: StorageMigrationDestinationsSchema,
|
|
8132
8214
|
throttleMbps: number(),
|
|
8133
8215
|
moves: array(StorageMigrationMoveSchema),
|
|
@@ -8140,13 +8222,30 @@ var StorageMigrationJobSchema = object({
|
|
|
8140
8222
|
finishedAt: number().nullable(),
|
|
8141
8223
|
error: string().nullable()
|
|
8142
8224
|
});
|
|
8225
|
+
var StorageMigrationFindingSchema = object({
|
|
8226
|
+
code: _enum([
|
|
8227
|
+
"sharesDeviceWithSource",
|
|
8228
|
+
"deviceIdentityUnknown",
|
|
8229
|
+
"unstampedEventMediaRows",
|
|
8230
|
+
"blockingOnly",
|
|
8231
|
+
"noMover"
|
|
8232
|
+
]),
|
|
8233
|
+
storageClass: StorageMigrationClassSchema,
|
|
8234
|
+
/** Human-readable, already carrying the ids and counts. */
|
|
8235
|
+
message: string()
|
|
8236
|
+
});
|
|
8143
8237
|
var StorageMigrationPlanSchema = object({
|
|
8144
8238
|
destinations: StorageMigrationDestinationsSchema,
|
|
8239
|
+
/** The mode this plan was built for. A plan is only valid for its mode: the
|
|
8240
|
+
* `eventMedia` seal gate and the single-cardinality refusal both depend on
|
|
8241
|
+
* it. */
|
|
8242
|
+
mode: StorageMigrationModeSchema,
|
|
8145
8243
|
moves: array(object({
|
|
8146
8244
|
storageClass: StorageMigrationClassSchema,
|
|
8147
8245
|
fromLocationId: string(),
|
|
8148
8246
|
toLocationId: string()
|
|
8149
|
-
}))
|
|
8247
|
+
})),
|
|
8248
|
+
findings: array(StorageMigrationFindingSchema)
|
|
8150
8249
|
});
|
|
8151
8250
|
/**
|
|
8152
8251
|
* `StorageLocationType` — an addon-declared id that identifies the *kind* of
|
|
@@ -18999,7 +19098,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18999
19098
|
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
19000
19099
|
kind: "mutation",
|
|
19001
19100
|
auth: "admin"
|
|
19002
|
-
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
19101
|
+
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
19003
19102
|
kind: "query",
|
|
19004
19103
|
auth: "admin"
|
|
19005
19104
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
@@ -21010,7 +21109,10 @@ method(object({
|
|
|
21010
21109
|
}), StorageLocationSchema, {
|
|
21011
21110
|
kind: "mutation",
|
|
21012
21111
|
auth: "admin"
|
|
21013
|
-
}), method(object({
|
|
21112
|
+
}), method(object({
|
|
21113
|
+
id: string(),
|
|
21114
|
+
force: boolean().optional()
|
|
21115
|
+
}), _void(), {
|
|
21014
21116
|
kind: "mutation",
|
|
21015
21117
|
auth: "admin"
|
|
21016
21118
|
}), method(object({ id: string() }), object({
|
|
@@ -25086,6 +25188,33 @@ var intercomCapability = {
|
|
|
25086
25188
|
deviceNative: true,
|
|
25087
25189
|
mode: "singleton",
|
|
25088
25190
|
deviceTypes: [DeviceType.Camera],
|
|
25191
|
+
/**
|
|
25192
|
+
* **Auth tier: `protected` on every method — deliberate, and load-bearing.**
|
|
25193
|
+
*
|
|
25194
|
+
* Talking through a camera is an OPERATE action, not a CONFIGURE one. This
|
|
25195
|
+
* cap has no configuration surface at all: all six methods open, feed and
|
|
25196
|
+
* close one live audio session against one `deviceId`. That is the same
|
|
25197
|
+
* authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
|
|
25198
|
+
* the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
|
|
25199
|
+
* `admin` because they change what the device IS.
|
|
25200
|
+
*
|
|
25201
|
+
* `protected` does not mean ungated: `protectedProcedure` runs the
|
|
25202
|
+
* `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
|
|
25203
|
+
* with `access: 'create'` and a `deviceId` in its input. So a caller needs a
|
|
25204
|
+
* grant that covers THAT camera at `create` — a `camera-viewer` (`view`
|
|
25205
|
+
* only) still cannot talk, and a grant on camera 5 cannot talk through
|
|
25206
|
+
* camera 7.
|
|
25207
|
+
*
|
|
25208
|
+
* Every method was `auth: 'admin'` from the initial commit, which made the
|
|
25209
|
+
* cap unreachable by every non-admin principal — `adminProcedure` throws
|
|
25210
|
+
* `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
|
|
25211
|
+
* machinery generated for this cap (`METHOD_ACCESS_MAP`,
|
|
25212
|
+
* `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
|
|
25213
|
+
* `camera-operator` scope preset has promised "PTZ control, intercom,
|
|
25214
|
+
* snapshots" since that same commit; the promise could not be kept. Recorded
|
|
25215
|
+
* as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
|
|
25216
|
+
* preset promises a cap no row of that preset can reach.
|
|
25217
|
+
*/
|
|
25089
25218
|
methods: {
|
|
25090
25219
|
/**
|
|
25091
25220
|
* Open a server-side WebRTC audio-only session. Returns an SDP
|
|
@@ -25098,7 +25227,7 @@ var intercomCapability = {
|
|
|
25098
25227
|
sdpOffer: string()
|
|
25099
25228
|
}), {
|
|
25100
25229
|
kind: "mutation",
|
|
25101
|
-
auth: "
|
|
25230
|
+
auth: "protected"
|
|
25102
25231
|
}),
|
|
25103
25232
|
handleAnswer: method(object({
|
|
25104
25233
|
deviceId: number(),
|
|
@@ -25106,7 +25235,7 @@ var intercomCapability = {
|
|
|
25106
25235
|
sdpAnswer: string()
|
|
25107
25236
|
}), _void(), {
|
|
25108
25237
|
kind: "mutation",
|
|
25109
|
-
auth: "
|
|
25238
|
+
auth: "protected"
|
|
25110
25239
|
}),
|
|
25111
25240
|
/** Close explicitly. Server also auto-closes on 30s idle. */
|
|
25112
25241
|
stopSession: method(object({
|
|
@@ -25114,7 +25243,7 @@ var intercomCapability = {
|
|
|
25114
25243
|
sessionId: string()
|
|
25115
25244
|
}), _void(), {
|
|
25116
25245
|
kind: "mutation",
|
|
25117
|
-
auth: "
|
|
25246
|
+
auth: "protected"
|
|
25118
25247
|
}),
|
|
25119
25248
|
/**
|
|
25120
25249
|
* Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
|
|
@@ -25127,7 +25256,7 @@ var intercomCapability = {
|
|
|
25127
25256
|
*/
|
|
25128
25257
|
startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
|
|
25129
25258
|
kind: "mutation",
|
|
25130
|
-
auth: "
|
|
25259
|
+
auth: "protected"
|
|
25131
25260
|
}),
|
|
25132
25261
|
/**
|
|
25133
25262
|
* Push one chunk of talk-back audio onto the active talk session.
|
|
@@ -25162,12 +25291,12 @@ var intercomCapability = {
|
|
|
25162
25291
|
sequenceNumber: number().int()
|
|
25163
25292
|
}), object({ accepted: boolean() }), {
|
|
25164
25293
|
kind: "mutation",
|
|
25165
|
-
auth: "
|
|
25294
|
+
auth: "protected"
|
|
25166
25295
|
}),
|
|
25167
25296
|
/** Close the raw-PCM talk session. Idempotent. */
|
|
25168
25297
|
endTalkSession: method(object({ deviceId: number() }), _void(), {
|
|
25169
25298
|
kind: "mutation",
|
|
25170
|
-
auth: "
|
|
25299
|
+
auth: "protected"
|
|
25171
25300
|
})
|
|
25172
25301
|
},
|
|
25173
25302
|
events: { onStatusChanged: { data: object({
|
|
@@ -34914,6 +35043,12 @@ Object.freeze({
|
|
|
34914
35043
|
addonId: null,
|
|
34915
35044
|
access: "create"
|
|
34916
35045
|
},
|
|
35046
|
+
"pipelineAnalytics.countUnstampedEventMedia": {
|
|
35047
|
+
capName: "pipeline-analytics",
|
|
35048
|
+
capScope: "device",
|
|
35049
|
+
addonId: null,
|
|
35050
|
+
access: "view"
|
|
35051
|
+
},
|
|
34917
35052
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
34918
35053
|
capName: "pipeline-analytics",
|
|
34919
35054
|
capScope: "device",
|