@camstack/addon-terminal 0.1.46 → 0.1.48
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
|
@@ -8138,18 +8138,61 @@ var RelocateFootageInputSchema = object({
|
|
|
8138
8138
|
* `RecordingConfig.enabled` or camera wrapper bindings. */
|
|
8139
8139
|
var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
|
|
8140
8140
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
|
|
8141
|
+
/**
|
|
8142
|
+
* What a `relocateMedia` pass DOES. One engine, three passes — never a second
|
|
8143
|
+
* mover (the engine already walks both collections with a timestamp cursor and
|
|
8144
|
+
* already has a stamp-without-copy path).
|
|
8145
|
+
*
|
|
8146
|
+
* - `move` — the default and the historical behaviour: event-media and
|
|
8147
|
+
* retrain blobs move to `toLocationId` and their rows are
|
|
8148
|
+
* stamped. The enrolled gallery is skipped (D197).
|
|
8149
|
+
* - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
|
|
8150
|
+
* stamped with `toLocationId`. `toLocationId` here is the id the
|
|
8151
|
+
* bytes ALREADY sit on — today's `eventMedia` default — because
|
|
8152
|
+
* a NULL row means "wherever `eventMedia` points *now*", and the
|
|
8153
|
+
* instant a repoint moves that pointer the row reads from the
|
|
8154
|
+
* new disk while its bytes are on the old one.
|
|
8155
|
+
* - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
|
|
8156
|
+
* (enrolled-gallery) rows, which `move` deliberately skips.
|
|
8157
|
+
* `galleryMedia` is `cardinality: 'single'`, so this pass can
|
|
8158
|
+
* never run beside a live second location: it is stop-the-world
|
|
8159
|
+
* by construction, which is acceptable only because the gallery
|
|
8160
|
+
* is a few KB per enrolled sample.
|
|
8161
|
+
*/
|
|
8162
|
+
var MediaRelocateModeSchema = _enum([
|
|
8163
|
+
"move",
|
|
8164
|
+
"seal",
|
|
8165
|
+
"gallery"
|
|
8166
|
+
]);
|
|
8141
8167
|
var RelocateMediaInputSchema = object({
|
|
8142
8168
|
toLocationId: string(),
|
|
8143
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8169
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8170
|
+
/** Omitted = `move`, the pre-existing behaviour. */
|
|
8171
|
+
mode: MediaRelocateModeSchema.optional()
|
|
8172
|
+
});
|
|
8173
|
+
/** How many rows still carry NO `locationId` — the population a repoint would
|
|
8174
|
+
* silently re-aim at a disk that does not hold their bytes. Zero is the only
|
|
8175
|
+
* value that permits a non-blocking `eventMedia` cutover. */
|
|
8176
|
+
var UnstampedEventMediaCountSchema = object({
|
|
8177
|
+
media: number().int().nonnegative(),
|
|
8178
|
+
retrainFrames: number().int().nonnegative(),
|
|
8179
|
+
total: number().int().nonnegative()
|
|
8144
8180
|
});
|
|
8145
8181
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
|
|
8146
|
-
/** The independently selectable logical storage classes
|
|
8147
|
-
*
|
|
8148
|
-
*
|
|
8182
|
+
/** The independently selectable logical storage classes — every class
|
|
8183
|
+
* `storage.listLocationDeclarations` reports, so an operator never meets a
|
|
8184
|
+
* Zod enum error where they should meet an explanation.
|
|
8185
|
+
*
|
|
8186
|
+
* `recordings` encompasses the high and mid segment profiles; `recordingsLow`
|
|
8187
|
+
* is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
|
|
8188
|
+
* enrolled gallery; `backups` is the system backup archive. The last two have
|
|
8189
|
+
* their own rules — see {@link StorageMigrationFindingCodeSchema}. */
|
|
8149
8190
|
var StorageMigrationClassSchema = _enum([
|
|
8150
8191
|
"recordings",
|
|
8151
8192
|
"recordingsLow",
|
|
8152
|
-
"eventMedia"
|
|
8193
|
+
"eventMedia",
|
|
8194
|
+
"backups",
|
|
8195
|
+
"galleryMedia"
|
|
8153
8196
|
]);
|
|
8154
8197
|
/** A destination is always an existing, fully-qualified location id. The
|
|
8155
8198
|
* migration API intentionally never changes a source location's `basePath`:
|
|
@@ -8157,20 +8200,56 @@ var StorageMigrationClassSchema = _enum([
|
|
|
8157
8200
|
var StorageMigrationDestinationsSchema = object({
|
|
8158
8201
|
recordings: string().min(1).optional(),
|
|
8159
8202
|
recordingsLow: string().min(1).optional(),
|
|
8160
|
-
eventMedia: string().min(1).optional()
|
|
8203
|
+
eventMedia: string().min(1).optional(),
|
|
8204
|
+
backups: string().min(1).optional(),
|
|
8205
|
+
galleryMedia: string().min(1).optional()
|
|
8161
8206
|
}).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
|
|
8207
|
+
/**
|
|
8208
|
+
* How a migration sequences the cutover against the byte move.
|
|
8209
|
+
*
|
|
8210
|
+
* - `blocking` — the historical order: pause, move every byte, repoint,
|
|
8211
|
+
* resume. Recording is stopped for the whole move. Right
|
|
8212
|
+
* for a small or a cold class, and the only legal mode for
|
|
8213
|
+
* a `cardinality: 'single'` class.
|
|
8214
|
+
* - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
|
|
8215
|
+
* refresh, resume, then move the past with everything
|
|
8216
|
+
* running. The pause is three bounded instants (a detach +
|
|
8217
|
+
* attach round, a write-gate drain, a lease) instead of one
|
|
8218
|
+
* bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
|
|
8219
|
+
* stopped recording under `blocking`; the same move is
|
|
8220
|
+
* seconds of stopped recording under `nonBlocking`.
|
|
8221
|
+
*
|
|
8222
|
+
* The mode is on the JOB, not only on the input, because `status` is where an
|
|
8223
|
+
* operator finds out which one is running.
|
|
8224
|
+
*/
|
|
8225
|
+
var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
|
|
8162
8226
|
/** Shared input for planning and starting an orchestrated storage migration. */
|
|
8163
8227
|
var StorageMigrationInputSchema = object({
|
|
8164
8228
|
destinations: StorageMigrationDestinationsSchema,
|
|
8165
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8229
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8230
|
+
/** Omitted = `blocking`, which stays the default. */
|
|
8231
|
+
mode: StorageMigrationModeSchema.optional()
|
|
8166
8232
|
});
|
|
8167
|
-
/**
|
|
8168
|
-
*
|
|
8169
|
-
*
|
|
8233
|
+
/**
|
|
8234
|
+
* The durable coordinator state machine.
|
|
8235
|
+
*
|
|
8236
|
+
* `blocking`:
|
|
8237
|
+
* planning → pausing → moving → verifying → repointing → refreshing → resuming → done
|
|
8238
|
+
*
|
|
8239
|
+
* `nonBlocking`:
|
|
8240
|
+
* planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
|
|
8241
|
+
*
|
|
8242
|
+
* Same phases, different order plus two new ones — not a second mover.
|
|
8243
|
+
* `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
|
|
8244
|
+
* `draining` runs the same movers UNLEASED, after every writer is back up.
|
|
8245
|
+
* `repointing` is still the only phase that changes a default location.
|
|
8246
|
+
*/
|
|
8170
8247
|
var StorageMigrationPhaseSchema = _enum([
|
|
8171
8248
|
"planning",
|
|
8249
|
+
"sealing",
|
|
8172
8250
|
"pausing",
|
|
8173
8251
|
"moving",
|
|
8252
|
+
"draining",
|
|
8174
8253
|
"verifying",
|
|
8175
8254
|
"repointing",
|
|
8176
8255
|
"refreshing",
|
|
@@ -8195,6 +8274,9 @@ var StorageMigrationMoveSchema = object({
|
|
|
8195
8274
|
var StorageMigrationJobSchema = object({
|
|
8196
8275
|
jobId: string(),
|
|
8197
8276
|
phase: StorageMigrationPhaseSchema,
|
|
8277
|
+
/** Which order this job is running. `status` is the only place an operator
|
|
8278
|
+
* can tell a seconds-long cutover from a thirty-hour one. */
|
|
8279
|
+
mode: StorageMigrationModeSchema,
|
|
8198
8280
|
destinations: StorageMigrationDestinationsSchema,
|
|
8199
8281
|
throttleMbps: number(),
|
|
8200
8282
|
moves: array(StorageMigrationMoveSchema),
|
|
@@ -8207,13 +8289,30 @@ var StorageMigrationJobSchema = object({
|
|
|
8207
8289
|
finishedAt: number().nullable(),
|
|
8208
8290
|
error: string().nullable()
|
|
8209
8291
|
});
|
|
8292
|
+
var StorageMigrationFindingSchema = object({
|
|
8293
|
+
code: _enum([
|
|
8294
|
+
"sharesDeviceWithSource",
|
|
8295
|
+
"deviceIdentityUnknown",
|
|
8296
|
+
"unstampedEventMediaRows",
|
|
8297
|
+
"blockingOnly",
|
|
8298
|
+
"noMover"
|
|
8299
|
+
]),
|
|
8300
|
+
storageClass: StorageMigrationClassSchema,
|
|
8301
|
+
/** Human-readable, already carrying the ids and counts. */
|
|
8302
|
+
message: string()
|
|
8303
|
+
});
|
|
8210
8304
|
var StorageMigrationPlanSchema = object({
|
|
8211
8305
|
destinations: StorageMigrationDestinationsSchema,
|
|
8306
|
+
/** The mode this plan was built for. A plan is only valid for its mode: the
|
|
8307
|
+
* `eventMedia` seal gate and the single-cardinality refusal both depend on
|
|
8308
|
+
* it. */
|
|
8309
|
+
mode: StorageMigrationModeSchema,
|
|
8212
8310
|
moves: array(object({
|
|
8213
8311
|
storageClass: StorageMigrationClassSchema,
|
|
8214
8312
|
fromLocationId: string(),
|
|
8215
8313
|
toLocationId: string()
|
|
8216
|
-
}))
|
|
8314
|
+
})),
|
|
8315
|
+
findings: array(StorageMigrationFindingSchema)
|
|
8217
8316
|
});
|
|
8218
8317
|
/**
|
|
8219
8318
|
* `StorageLocationType` — an addon-declared id that identifies the *kind* of
|
|
@@ -19011,7 +19110,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
19011
19110
|
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
19012
19111
|
kind: "mutation",
|
|
19013
19112
|
auth: "admin"
|
|
19014
|
-
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
19113
|
+
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
19015
19114
|
kind: "query",
|
|
19016
19115
|
auth: "admin"
|
|
19017
19116
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
@@ -21022,7 +21121,10 @@ method(object({
|
|
|
21022
21121
|
}), StorageLocationSchema, {
|
|
21023
21122
|
kind: "mutation",
|
|
21024
21123
|
auth: "admin"
|
|
21025
|
-
}), method(object({
|
|
21124
|
+
}), method(object({
|
|
21125
|
+
id: string(),
|
|
21126
|
+
force: boolean().optional()
|
|
21127
|
+
}), _void(), {
|
|
21026
21128
|
kind: "mutation",
|
|
21027
21129
|
auth: "admin"
|
|
21028
21130
|
}), method(object({ id: string() }), object({
|
|
@@ -25134,6 +25236,33 @@ var intercomCapability = {
|
|
|
25134
25236
|
deviceNative: true,
|
|
25135
25237
|
mode: "singleton",
|
|
25136
25238
|
deviceTypes: [DeviceType.Camera],
|
|
25239
|
+
/**
|
|
25240
|
+
* **Auth tier: `protected` on every method — deliberate, and load-bearing.**
|
|
25241
|
+
*
|
|
25242
|
+
* Talking through a camera is an OPERATE action, not a CONFIGURE one. This
|
|
25243
|
+
* cap has no configuration surface at all: all six methods open, feed and
|
|
25244
|
+
* close one live audio session against one `deviceId`. That is the same
|
|
25245
|
+
* authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
|
|
25246
|
+
* the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
|
|
25247
|
+
* `admin` because they change what the device IS.
|
|
25248
|
+
*
|
|
25249
|
+
* `protected` does not mean ungated: `protectedProcedure` runs the
|
|
25250
|
+
* `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
|
|
25251
|
+
* with `access: 'create'` and a `deviceId` in its input. So a caller needs a
|
|
25252
|
+
* grant that covers THAT camera at `create` — a `camera-viewer` (`view`
|
|
25253
|
+
* only) still cannot talk, and a grant on camera 5 cannot talk through
|
|
25254
|
+
* camera 7.
|
|
25255
|
+
*
|
|
25256
|
+
* Every method was `auth: 'admin'` from the initial commit, which made the
|
|
25257
|
+
* cap unreachable by every non-admin principal — `adminProcedure` throws
|
|
25258
|
+
* `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
|
|
25259
|
+
* machinery generated for this cap (`METHOD_ACCESS_MAP`,
|
|
25260
|
+
* `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
|
|
25261
|
+
* `camera-operator` scope preset has promised "PTZ control, intercom,
|
|
25262
|
+
* snapshots" since that same commit; the promise could not be kept. Recorded
|
|
25263
|
+
* as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
|
|
25264
|
+
* preset promises a cap no row of that preset can reach.
|
|
25265
|
+
*/
|
|
25137
25266
|
methods: {
|
|
25138
25267
|
/**
|
|
25139
25268
|
* Open a server-side WebRTC audio-only session. Returns an SDP
|
|
@@ -25146,7 +25275,7 @@ var intercomCapability = {
|
|
|
25146
25275
|
sdpOffer: string()
|
|
25147
25276
|
}), {
|
|
25148
25277
|
kind: "mutation",
|
|
25149
|
-
auth: "
|
|
25278
|
+
auth: "protected"
|
|
25150
25279
|
}),
|
|
25151
25280
|
handleAnswer: method(object({
|
|
25152
25281
|
deviceId: number(),
|
|
@@ -25154,7 +25283,7 @@ var intercomCapability = {
|
|
|
25154
25283
|
sdpAnswer: string()
|
|
25155
25284
|
}), _void(), {
|
|
25156
25285
|
kind: "mutation",
|
|
25157
|
-
auth: "
|
|
25286
|
+
auth: "protected"
|
|
25158
25287
|
}),
|
|
25159
25288
|
/** Close explicitly. Server also auto-closes on 30s idle. */
|
|
25160
25289
|
stopSession: method(object({
|
|
@@ -25162,7 +25291,7 @@ var intercomCapability = {
|
|
|
25162
25291
|
sessionId: string()
|
|
25163
25292
|
}), _void(), {
|
|
25164
25293
|
kind: "mutation",
|
|
25165
|
-
auth: "
|
|
25294
|
+
auth: "protected"
|
|
25166
25295
|
}),
|
|
25167
25296
|
/**
|
|
25168
25297
|
* Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
|
|
@@ -25175,7 +25304,7 @@ var intercomCapability = {
|
|
|
25175
25304
|
*/
|
|
25176
25305
|
startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
|
|
25177
25306
|
kind: "mutation",
|
|
25178
|
-
auth: "
|
|
25307
|
+
auth: "protected"
|
|
25179
25308
|
}),
|
|
25180
25309
|
/**
|
|
25181
25310
|
* Push one chunk of talk-back audio onto the active talk session.
|
|
@@ -25210,12 +25339,12 @@ var intercomCapability = {
|
|
|
25210
25339
|
sequenceNumber: number().int()
|
|
25211
25340
|
}), object({ accepted: boolean() }), {
|
|
25212
25341
|
kind: "mutation",
|
|
25213
|
-
auth: "
|
|
25342
|
+
auth: "protected"
|
|
25214
25343
|
}),
|
|
25215
25344
|
/** Close the raw-PCM talk session. Idempotent. */
|
|
25216
25345
|
endTalkSession: method(object({ deviceId: number() }), _void(), {
|
|
25217
25346
|
kind: "mutation",
|
|
25218
|
-
auth: "
|
|
25347
|
+
auth: "protected"
|
|
25219
25348
|
})
|
|
25220
25349
|
},
|
|
25221
25350
|
events: { onStatusChanged: { data: object({
|
|
@@ -34894,6 +35023,12 @@ Object.freeze({
|
|
|
34894
35023
|
addonId: null,
|
|
34895
35024
|
access: "create"
|
|
34896
35025
|
},
|
|
35026
|
+
"pipelineAnalytics.countUnstampedEventMedia": {
|
|
35027
|
+
capName: "pipeline-analytics",
|
|
35028
|
+
capScope: "device",
|
|
35029
|
+
addonId: null,
|
|
35030
|
+
access: "view"
|
|
35031
|
+
},
|
|
34897
35032
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
34898
35033
|
capName: "pipeline-analytics",
|
|
34899
35034
|
capScope: "device",
|
package/dist/addon.mjs
CHANGED
|
@@ -8115,18 +8115,61 @@ var RelocateFootageInputSchema = object({
|
|
|
8115
8115
|
* `RecordingConfig.enabled` or camera wrapper bindings. */
|
|
8116
8116
|
var StorageMigrationLeaseInputSchema = object({ leaseId: string().min(1) });
|
|
8117
8117
|
var StorageMigrationFootageMoveInputSchema = RelocateFootageInputSchema.extend({ leaseId: string().min(1) });
|
|
8118
|
+
/**
|
|
8119
|
+
* What a `relocateMedia` pass DOES. One engine, three passes — never a second
|
|
8120
|
+
* mover (the engine already walks both collections with a timestamp cursor and
|
|
8121
|
+
* already has a stamp-without-copy path).
|
|
8122
|
+
*
|
|
8123
|
+
* - `move` — the default and the historical behaviour: event-media and
|
|
8124
|
+
* retrain blobs move to `toLocationId` and their rows are
|
|
8125
|
+
* stamped. The enrolled gallery is skipped (D197).
|
|
8126
|
+
* - `seal` — ROWS ONLY, no bytes. Every row whose `locationId` is NULL is
|
|
8127
|
+
* stamped with `toLocationId`. `toLocationId` here is the id the
|
|
8128
|
+
* bytes ALREADY sit on — today's `eventMedia` default — because
|
|
8129
|
+
* a NULL row means "wherever `eventMedia` points *now*", and the
|
|
8130
|
+
* instant a repoint moves that pointer the row reads from the
|
|
8131
|
+
* new disk while its bytes are on the old one.
|
|
8132
|
+
* - `gallery` — the inverse selection of `move`: ONLY the retention-exempt
|
|
8133
|
+
* (enrolled-gallery) rows, which `move` deliberately skips.
|
|
8134
|
+
* `galleryMedia` is `cardinality: 'single'`, so this pass can
|
|
8135
|
+
* never run beside a live second location: it is stop-the-world
|
|
8136
|
+
* by construction, which is acceptable only because the gallery
|
|
8137
|
+
* is a few KB per enrolled sample.
|
|
8138
|
+
*/
|
|
8139
|
+
var MediaRelocateModeSchema = _enum([
|
|
8140
|
+
"move",
|
|
8141
|
+
"seal",
|
|
8142
|
+
"gallery"
|
|
8143
|
+
]);
|
|
8118
8144
|
var RelocateMediaInputSchema = object({
|
|
8119
8145
|
toLocationId: string(),
|
|
8120
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8146
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8147
|
+
/** Omitted = `move`, the pre-existing behaviour. */
|
|
8148
|
+
mode: MediaRelocateModeSchema.optional()
|
|
8149
|
+
});
|
|
8150
|
+
/** How many rows still carry NO `locationId` — the population a repoint would
|
|
8151
|
+
* silently re-aim at a disk that does not hold their bytes. Zero is the only
|
|
8152
|
+
* value that permits a non-blocking `eventMedia` cutover. */
|
|
8153
|
+
var UnstampedEventMediaCountSchema = object({
|
|
8154
|
+
media: number().int().nonnegative(),
|
|
8155
|
+
retrainFrames: number().int().nonnegative(),
|
|
8156
|
+
total: number().int().nonnegative()
|
|
8121
8157
|
});
|
|
8122
8158
|
var StorageMigrationMediaMoveInputSchema = RelocateMediaInputSchema.extend({ leaseId: string().min(1) });
|
|
8123
|
-
/** The independently selectable logical storage classes
|
|
8124
|
-
*
|
|
8125
|
-
*
|
|
8159
|
+
/** The independently selectable logical storage classes — every class
|
|
8160
|
+
* `storage.listLocationDeclarations` reports, so an operator never meets a
|
|
8161
|
+
* Zod enum error where they should meet an explanation.
|
|
8162
|
+
*
|
|
8163
|
+
* `recordings` encompasses the high and mid segment profiles; `recordingsLow`
|
|
8164
|
+
* is low segments; `eventMedia` is post-analysis blobs; `galleryMedia` is the
|
|
8165
|
+
* enrolled gallery; `backups` is the system backup archive. The last two have
|
|
8166
|
+
* their own rules — see {@link StorageMigrationFindingCodeSchema}. */
|
|
8126
8167
|
var StorageMigrationClassSchema = _enum([
|
|
8127
8168
|
"recordings",
|
|
8128
8169
|
"recordingsLow",
|
|
8129
|
-
"eventMedia"
|
|
8170
|
+
"eventMedia",
|
|
8171
|
+
"backups",
|
|
8172
|
+
"galleryMedia"
|
|
8130
8173
|
]);
|
|
8131
8174
|
/** A destination is always an existing, fully-qualified location id. The
|
|
8132
8175
|
* migration API intentionally never changes a source location's `basePath`:
|
|
@@ -8134,20 +8177,56 @@ var StorageMigrationClassSchema = _enum([
|
|
|
8134
8177
|
var StorageMigrationDestinationsSchema = object({
|
|
8135
8178
|
recordings: string().min(1).optional(),
|
|
8136
8179
|
recordingsLow: string().min(1).optional(),
|
|
8137
|
-
eventMedia: string().min(1).optional()
|
|
8180
|
+
eventMedia: string().min(1).optional(),
|
|
8181
|
+
backups: string().min(1).optional(),
|
|
8182
|
+
galleryMedia: string().min(1).optional()
|
|
8138
8183
|
}).refine((value) => Object.keys(value).length > 0, { message: "select at least one storage class" });
|
|
8184
|
+
/**
|
|
8185
|
+
* How a migration sequences the cutover against the byte move.
|
|
8186
|
+
*
|
|
8187
|
+
* - `blocking` — the historical order: pause, move every byte, repoint,
|
|
8188
|
+
* resume. Recording is stopped for the whole move. Right
|
|
8189
|
+
* for a small or a cold class, and the only legal mode for
|
|
8190
|
+
* a `cardinality: 'single'` class.
|
|
8191
|
+
* - `nonBlocking` — repoint FIRST, drain behind: seal, pause, repoint,
|
|
8192
|
+
* refresh, resume, then move the past with everything
|
|
8193
|
+
* running. The pause is three bounded instants (a detach +
|
|
8194
|
+
* attach round, a write-gate drain, a lease) instead of one
|
|
8195
|
+
* bounded by bytes. 1.09 TB at 7–14 MB/s is thirty hours of
|
|
8196
|
+
* stopped recording under `blocking`; the same move is
|
|
8197
|
+
* seconds of stopped recording under `nonBlocking`.
|
|
8198
|
+
*
|
|
8199
|
+
* The mode is on the JOB, not only on the input, because `status` is where an
|
|
8200
|
+
* operator finds out which one is running.
|
|
8201
|
+
*/
|
|
8202
|
+
var StorageMigrationModeSchema = _enum(["blocking", "nonBlocking"]);
|
|
8139
8203
|
/** Shared input for planning and starting an orchestrated storage migration. */
|
|
8140
8204
|
var StorageMigrationInputSchema = object({
|
|
8141
8205
|
destinations: StorageMigrationDestinationsSchema,
|
|
8142
|
-
throttleMbps: number().min(1).max(1e3).optional()
|
|
8206
|
+
throttleMbps: number().min(1).max(1e3).optional(),
|
|
8207
|
+
/** Omitted = `blocking`, which stays the default. */
|
|
8208
|
+
mode: StorageMigrationModeSchema.optional()
|
|
8143
8209
|
});
|
|
8144
|
-
/**
|
|
8145
|
-
*
|
|
8146
|
-
*
|
|
8210
|
+
/**
|
|
8211
|
+
* The durable coordinator state machine.
|
|
8212
|
+
*
|
|
8213
|
+
* `blocking`:
|
|
8214
|
+
* planning → pausing → moving → verifying → repointing → refreshing → resuming → done
|
|
8215
|
+
*
|
|
8216
|
+
* `nonBlocking`:
|
|
8217
|
+
* planning → sealing → pausing → repointing → refreshing → resuming → draining → verifying → done
|
|
8218
|
+
*
|
|
8219
|
+
* Same phases, different order plus two new ones — not a second mover.
|
|
8220
|
+
* `sealing` closes the `eventMedia` NULL-row hole BEFORE anything is paused;
|
|
8221
|
+
* `draining` runs the same movers UNLEASED, after every writer is back up.
|
|
8222
|
+
* `repointing` is still the only phase that changes a default location.
|
|
8223
|
+
*/
|
|
8147
8224
|
var StorageMigrationPhaseSchema = _enum([
|
|
8148
8225
|
"planning",
|
|
8226
|
+
"sealing",
|
|
8149
8227
|
"pausing",
|
|
8150
8228
|
"moving",
|
|
8229
|
+
"draining",
|
|
8151
8230
|
"verifying",
|
|
8152
8231
|
"repointing",
|
|
8153
8232
|
"refreshing",
|
|
@@ -8172,6 +8251,9 @@ var StorageMigrationMoveSchema = object({
|
|
|
8172
8251
|
var StorageMigrationJobSchema = object({
|
|
8173
8252
|
jobId: string(),
|
|
8174
8253
|
phase: StorageMigrationPhaseSchema,
|
|
8254
|
+
/** Which order this job is running. `status` is the only place an operator
|
|
8255
|
+
* can tell a seconds-long cutover from a thirty-hour one. */
|
|
8256
|
+
mode: StorageMigrationModeSchema,
|
|
8175
8257
|
destinations: StorageMigrationDestinationsSchema,
|
|
8176
8258
|
throttleMbps: number(),
|
|
8177
8259
|
moves: array(StorageMigrationMoveSchema),
|
|
@@ -8184,13 +8266,30 @@ var StorageMigrationJobSchema = object({
|
|
|
8184
8266
|
finishedAt: number().nullable(),
|
|
8185
8267
|
error: string().nullable()
|
|
8186
8268
|
});
|
|
8269
|
+
var StorageMigrationFindingSchema = object({
|
|
8270
|
+
code: _enum([
|
|
8271
|
+
"sharesDeviceWithSource",
|
|
8272
|
+
"deviceIdentityUnknown",
|
|
8273
|
+
"unstampedEventMediaRows",
|
|
8274
|
+
"blockingOnly",
|
|
8275
|
+
"noMover"
|
|
8276
|
+
]),
|
|
8277
|
+
storageClass: StorageMigrationClassSchema,
|
|
8278
|
+
/** Human-readable, already carrying the ids and counts. */
|
|
8279
|
+
message: string()
|
|
8280
|
+
});
|
|
8187
8281
|
var StorageMigrationPlanSchema = object({
|
|
8188
8282
|
destinations: StorageMigrationDestinationsSchema,
|
|
8283
|
+
/** The mode this plan was built for. A plan is only valid for its mode: the
|
|
8284
|
+
* `eventMedia` seal gate and the single-cardinality refusal both depend on
|
|
8285
|
+
* it. */
|
|
8286
|
+
mode: StorageMigrationModeSchema,
|
|
8189
8287
|
moves: array(object({
|
|
8190
8288
|
storageClass: StorageMigrationClassSchema,
|
|
8191
8289
|
fromLocationId: string(),
|
|
8192
8290
|
toLocationId: string()
|
|
8193
|
-
}))
|
|
8291
|
+
})),
|
|
8292
|
+
findings: array(StorageMigrationFindingSchema)
|
|
8194
8293
|
});
|
|
8195
8294
|
/**
|
|
8196
8295
|
* `StorageLocationType` — an addon-declared id that identifies the *kind* of
|
|
@@ -18988,7 +19087,7 @@ DeviceType.Camera, method(object({ deviceId: number() }), array(TrackSchema).rea
|
|
|
18988
19087
|
}), method(RelocateMediaInputSchema, object({ jobId: string() }), {
|
|
18989
19088
|
kind: "mutation",
|
|
18990
19089
|
auth: "admin"
|
|
18991
|
-
}), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
19090
|
+
}), method(object({}), UnstampedEventMediaCountSchema, { auth: "admin" }), method(object({}), array(RelocateJobSchema).readonly(), {
|
|
18992
19091
|
kind: "query",
|
|
18993
19092
|
auth: "admin"
|
|
18994
19093
|
}), method(object({ jobId: string() }), object({ cancelled: boolean() }), {
|
|
@@ -20999,7 +21098,10 @@ method(object({
|
|
|
20999
21098
|
}), StorageLocationSchema, {
|
|
21000
21099
|
kind: "mutation",
|
|
21001
21100
|
auth: "admin"
|
|
21002
|
-
}), method(object({
|
|
21101
|
+
}), method(object({
|
|
21102
|
+
id: string(),
|
|
21103
|
+
force: boolean().optional()
|
|
21104
|
+
}), _void(), {
|
|
21003
21105
|
kind: "mutation",
|
|
21004
21106
|
auth: "admin"
|
|
21005
21107
|
}), method(object({ id: string() }), object({
|
|
@@ -25111,6 +25213,33 @@ var intercomCapability = {
|
|
|
25111
25213
|
deviceNative: true,
|
|
25112
25214
|
mode: "singleton",
|
|
25113
25215
|
deviceTypes: [DeviceType.Camera],
|
|
25216
|
+
/**
|
|
25217
|
+
* **Auth tier: `protected` on every method — deliberate, and load-bearing.**
|
|
25218
|
+
*
|
|
25219
|
+
* Talking through a camera is an OPERATE action, not a CONFIGURE one. This
|
|
25220
|
+
* cap has no configuration surface at all: all six methods open, feed and
|
|
25221
|
+
* close one live audio session against one `deviceId`. That is the same
|
|
25222
|
+
* authority as `ptz.move` or `snapshot.getSnapshot`, both `protected` — and
|
|
25223
|
+
* the opposite of `ptz.savePreset` / `snapshot.invalidateCache`, which are
|
|
25224
|
+
* `admin` because they change what the device IS.
|
|
25225
|
+
*
|
|
25226
|
+
* `protected` does not mean ungated: `protectedProcedure` runs the
|
|
25227
|
+
* `METHOD_ACCESS_MAP` scope check, and every method here is `scope: 'device'`
|
|
25228
|
+
* with `access: 'create'` and a `deviceId` in its input. So a caller needs a
|
|
25229
|
+
* grant that covers THAT camera at `create` — a `camera-viewer` (`view`
|
|
25230
|
+
* only) still cannot talk, and a grant on camera 5 cannot talk through
|
|
25231
|
+
* camera 7.
|
|
25232
|
+
*
|
|
25233
|
+
* Every method was `auth: 'admin'` from the initial commit, which made the
|
|
25234
|
+
* cap unreachable by every non-admin principal — `adminProcedure` throws
|
|
25235
|
+
* `FORBIDDEN: Admin required` BEFORE the scope check runs, so the scope
|
|
25236
|
+
* machinery generated for this cap (`METHOD_ACCESS_MAP`,
|
|
25237
|
+
* `DEVICE_SCOPED_CAPS`, `METHOD_DEVICE_SELECTORS`) was complete and dead. The
|
|
25238
|
+
* `camera-operator` scope preset has promised "PTZ control, intercom,
|
|
25239
|
+
* snapshots" since that same commit; the promise could not be kept. Recorded
|
|
25240
|
+
* as D289; `scripts/check-scope-preset-promises.ts` now fails the build if a
|
|
25241
|
+
* preset promises a cap no row of that preset can reach.
|
|
25242
|
+
*/
|
|
25114
25243
|
methods: {
|
|
25115
25244
|
/**
|
|
25116
25245
|
* Open a server-side WebRTC audio-only session. Returns an SDP
|
|
@@ -25123,7 +25252,7 @@ var intercomCapability = {
|
|
|
25123
25252
|
sdpOffer: string()
|
|
25124
25253
|
}), {
|
|
25125
25254
|
kind: "mutation",
|
|
25126
|
-
auth: "
|
|
25255
|
+
auth: "protected"
|
|
25127
25256
|
}),
|
|
25128
25257
|
handleAnswer: method(object({
|
|
25129
25258
|
deviceId: number(),
|
|
@@ -25131,7 +25260,7 @@ var intercomCapability = {
|
|
|
25131
25260
|
sdpAnswer: string()
|
|
25132
25261
|
}), _void(), {
|
|
25133
25262
|
kind: "mutation",
|
|
25134
|
-
auth: "
|
|
25263
|
+
auth: "protected"
|
|
25135
25264
|
}),
|
|
25136
25265
|
/** Close explicitly. Server also auto-closes on 30s idle. */
|
|
25137
25266
|
stopSession: method(object({
|
|
@@ -25139,7 +25268,7 @@ var intercomCapability = {
|
|
|
25139
25268
|
sessionId: string()
|
|
25140
25269
|
}), _void(), {
|
|
25141
25270
|
kind: "mutation",
|
|
25142
|
-
auth: "
|
|
25271
|
+
auth: "protected"
|
|
25143
25272
|
}),
|
|
25144
25273
|
/**
|
|
25145
25274
|
* Open a raw-PCM talk session (no WebRTC SDP plumbing). Used by
|
|
@@ -25152,7 +25281,7 @@ var intercomCapability = {
|
|
|
25152
25281
|
*/
|
|
25153
25282
|
startTalkSession: method(object({ deviceId: number() }), object({ sessionId: string() }), {
|
|
25154
25283
|
kind: "mutation",
|
|
25155
|
-
auth: "
|
|
25284
|
+
auth: "protected"
|
|
25156
25285
|
}),
|
|
25157
25286
|
/**
|
|
25158
25287
|
* Push one chunk of talk-back audio onto the active talk session.
|
|
@@ -25187,12 +25316,12 @@ var intercomCapability = {
|
|
|
25187
25316
|
sequenceNumber: number().int()
|
|
25188
25317
|
}), object({ accepted: boolean() }), {
|
|
25189
25318
|
kind: "mutation",
|
|
25190
|
-
auth: "
|
|
25319
|
+
auth: "protected"
|
|
25191
25320
|
}),
|
|
25192
25321
|
/** Close the raw-PCM talk session. Idempotent. */
|
|
25193
25322
|
endTalkSession: method(object({ deviceId: number() }), _void(), {
|
|
25194
25323
|
kind: "mutation",
|
|
25195
|
-
auth: "
|
|
25324
|
+
auth: "protected"
|
|
25196
25325
|
})
|
|
25197
25326
|
},
|
|
25198
25327
|
events: { onStatusChanged: { data: object({
|
|
@@ -34871,6 +35000,12 @@ Object.freeze({
|
|
|
34871
35000
|
addonId: null,
|
|
34872
35001
|
access: "create"
|
|
34873
35002
|
},
|
|
35003
|
+
"pipelineAnalytics.countUnstampedEventMedia": {
|
|
35004
|
+
capName: "pipeline-analytics",
|
|
35005
|
+
capScope: "device",
|
|
35006
|
+
addonId: null,
|
|
35007
|
+
access: "view"
|
|
35008
|
+
},
|
|
34874
35009
|
"pipelineAnalytics.deleteDeviceEvents": {
|
|
34875
35010
|
capName: "pipeline-analytics",
|
|
34876
35011
|
capScope: "device",
|