@camstack/types 1.2.124 → 1.2.127
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 +6 -3
- package/dist/addon.mjs +6 -3
- package/dist/capabilities/device-manager.cap.d.ts +9 -0
- package/dist/capabilities/pipeline-analytics.cap.d.ts +46 -5
- package/dist/capabilities/recording.cap.d.ts +22 -0
- package/dist/capabilities/storage-migration.cap.d.ts +85 -0
- package/dist/capabilities/storage.cap.d.ts +4 -0
- package/dist/capabilities/vector-store.cap.d.ts +113 -6
- package/dist/device/system-mirror.d.ts +7 -0
- package/dist/generated/addon-api.d.ts +49 -0
- package/dist/generated/cap-input-defaults.d.ts +1 -1
- package/dist/generated/method-access-map.d.ts +1 -1
- package/dist/generated/system-proxy.d.ts +2 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +557 -19
- package/dist/index.mjs +542 -20
- package/dist/interfaces/addon.d.ts +67 -0
- package/dist/interfaces/config-ui-secrets.d.ts +48 -0
- package/dist/interfaces/config-ui.d.ts +17 -0
- package/dist/interfaces/relocate.d.ts +226 -7
- package/dist/interfaces/storage-location-declaration.d.ts +40 -0
- package/dist/{sleep-CJrvRDlD.js → sleep-CWWLTM6W.js} +1 -0
- package/dist/{sleep-L5-bG_a_.mjs → sleep-bm0x6zZF.mjs} +1 -0
- package/package.json +1 -1
|
@@ -1025,6 +1025,36 @@ export interface AddonDeclaration {
|
|
|
1025
1025
|
* `ctx.deps.installPythonRequirements(absolutePath)`.
|
|
1026
1026
|
*/
|
|
1027
1027
|
readonly python?: AddonPythonRequirements;
|
|
1028
|
+
/**
|
|
1029
|
+
* Host binaries this addon needs on PATH. Same declaration site, same
|
|
1030
|
+
* lifecycle position and same idempotence contract as {@link python} —
|
|
1031
|
+
* the runner satisfies them BEFORE `onInitialize()` runs, so a capability
|
|
1032
|
+
* never advertises itself while the binary it wraps is missing.
|
|
1033
|
+
*
|
|
1034
|
+
* The declaration names WHAT is needed (a binary) plus the package name
|
|
1035
|
+
* each host package manager knows it by — a translation table, not a
|
|
1036
|
+
* procedure. HOW to install (`apt-get update && apt-get install -y …`,
|
|
1037
|
+
* `brew install …`) belongs to the framework, so the next addon that needs
|
|
1038
|
+
* a binary does not re-answer the question.
|
|
1039
|
+
*
|
|
1040
|
+
* Example:
|
|
1041
|
+
* ```jsonc
|
|
1042
|
+
* "camstack": {
|
|
1043
|
+
* "addons": [{
|
|
1044
|
+
* "id": "smb-storage",
|
|
1045
|
+
* "systemDependencies": [
|
|
1046
|
+
* { "binary": "smbclient", "packages": { "apt": "smbclient", "brew": "samba" } }
|
|
1047
|
+
* ]
|
|
1048
|
+
* }]
|
|
1049
|
+
* }
|
|
1050
|
+
* ```
|
|
1051
|
+
*
|
|
1052
|
+
* Failure is NOT fatal: the addon still loads and the capability that needs
|
|
1053
|
+
* the binary must refuse loudly on its own surface. A provider that cannot
|
|
1054
|
+
* run must never make its locations read as *empty* — `empty` is deletable
|
|
1055
|
+
* (D293).
|
|
1056
|
+
*/
|
|
1057
|
+
readonly systemDependencies?: readonly AddonSystemDependency[];
|
|
1028
1058
|
/** Default configuration values */
|
|
1029
1059
|
readonly defaultConfig?: Readonly<Record<string, unknown>>;
|
|
1030
1060
|
/**
|
|
@@ -1046,6 +1076,43 @@ export interface AddonPythonRequirements {
|
|
|
1046
1076
|
*/
|
|
1047
1077
|
readonly requirements: string;
|
|
1048
1078
|
}
|
|
1079
|
+
/**
|
|
1080
|
+
* One host binary an addon needs on PATH. See
|
|
1081
|
+
* `AddonDeclaration.systemDependencies`.
|
|
1082
|
+
*
|
|
1083
|
+
* Deliberately NOT a shell command. A manifest that carried
|
|
1084
|
+
* `"install": "apt-get install -y smbclient"` would be a per-addon script the
|
|
1085
|
+
* framework runs blind — unportable, unverifiable, and a second answer to a
|
|
1086
|
+
* question the Python mechanism already answered once.
|
|
1087
|
+
*/
|
|
1088
|
+
export interface AddonSystemDependency {
|
|
1089
|
+
/**
|
|
1090
|
+
* The executable that must resolve on PATH after the step completes. This
|
|
1091
|
+
* is both the presence check (cheap, and what makes the common path a
|
|
1092
|
+
* no-op) and the success check.
|
|
1093
|
+
*/
|
|
1094
|
+
readonly binary: string;
|
|
1095
|
+
/**
|
|
1096
|
+
* Package name per host package manager. A manager absent from this map
|
|
1097
|
+
* means "this addon has no way to install the binary on that host" — the
|
|
1098
|
+
* runner logs it and continues rather than guessing that the package shares
|
|
1099
|
+
* the binary's name.
|
|
1100
|
+
*/
|
|
1101
|
+
readonly packages: AddonSystemDependencyPackages;
|
|
1102
|
+
/** Operator-facing reason, surfaced in the log line when the install fails. */
|
|
1103
|
+
readonly reason?: string;
|
|
1104
|
+
}
|
|
1105
|
+
/** Package name per supported host package manager. */
|
|
1106
|
+
export interface AddonSystemDependencyPackages {
|
|
1107
|
+
/** Debian / Ubuntu (`apt-get`) — the container base. */
|
|
1108
|
+
readonly apt?: string;
|
|
1109
|
+
/** Alpine (`apk`). */
|
|
1110
|
+
readonly apk?: string;
|
|
1111
|
+
/** Fedora / RHEL (`dnf`). */
|
|
1112
|
+
readonly dnf?: string;
|
|
1113
|
+
/** macOS Homebrew (`brew`) — the desktop build. */
|
|
1114
|
+
readonly brew?: string;
|
|
1115
|
+
}
|
|
1049
1116
|
export interface AddonPackageManifest {
|
|
1050
1117
|
/**
|
|
1051
1118
|
* Human-readable package name for UI display (e.g., "Pipeline", "Core").
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Secret-field discovery over a {@link ConfigUISchema}.
|
|
3
|
+
*
|
|
4
|
+
* A provider already declares its own form. That declaration is the ONLY place
|
|
5
|
+
* that knows which of its config keys hold a credential, so redaction reads it
|
|
6
|
+
* rather than keeping a second, hand-maintained key list somewhere in the
|
|
7
|
+
* orchestrator — a list that would be right on the day it was written and
|
|
8
|
+
* wrong on the day a provider gained a field.
|
|
9
|
+
*
|
|
10
|
+
* The discriminator is deliberately NOT `type === 'password'` alone. That was
|
|
11
|
+
* the first design and it is wrong: the SFTP provider's `privateKey` is a
|
|
12
|
+
* `textarea` holding a full PEM key. Keying off the input widget would have
|
|
13
|
+
* left it on the wire while reporting that secrets were redacted. See
|
|
14
|
+
* `ConfigFieldBase.secret`.
|
|
15
|
+
*
|
|
16
|
+
* The walk takes `unknown` because that is what the caller has: the
|
|
17
|
+
* `storage-provider` cap declares `configSchema: z.unknown()`, so a provider's
|
|
18
|
+
* schema arrives structurally unverified. It narrows defensively at every
|
|
19
|
+
* step and ignores anything it cannot read — an unreadable node contributes no
|
|
20
|
+
* keys, which is the direction that under-reports rather than over-reports.
|
|
21
|
+
* A caller that needs "did I understand this schema at all" asks
|
|
22
|
+
* {@link schemaDeclaresAnyField}, which answers `false` for a shape this
|
|
23
|
+
* cannot read, so "no secrets" and "no schema" stay distinguishable.
|
|
24
|
+
*/
|
|
25
|
+
import type { ConfigField } from './config-ui.js';
|
|
26
|
+
/**
|
|
27
|
+
* The value a redacted secret is replaced with on every read surface.
|
|
28
|
+
*
|
|
29
|
+
* It is also the WRITE-BACK token: an `upsertLocation` that sends this value
|
|
30
|
+
* back for a key means "keep what is stored", which is what lets an operator
|
|
31
|
+
* rename a location without retyping its password. A literal an operator could
|
|
32
|
+
* plausibly choose as a real password would turn that convenience into a way
|
|
33
|
+
* to lock yourself out, hence the sentinel shape.
|
|
34
|
+
*/
|
|
35
|
+
export declare const REDACTED_SECRET = "__camstack_redacted__";
|
|
36
|
+
/** Is this field's stored value a credential? */
|
|
37
|
+
export declare function isSecretConfigField(field: ConfigField): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* Every config key in `schema` whose value is a secret, including keys nested
|
|
40
|
+
* inside `group` / `sub-tabs` containers.
|
|
41
|
+
*/
|
|
42
|
+
export declare function collectSecretConfigKeys(schema: unknown): ReadonlySet<string>;
|
|
43
|
+
/**
|
|
44
|
+
* True when the value is a readable schema declaring at least one field of any
|
|
45
|
+
* kind. Lets a caller tell "this provider has no secrets" from "this provider's
|
|
46
|
+
* schema could not be read", which are the same empty set otherwise.
|
|
47
|
+
*/
|
|
48
|
+
export declare function schemaDeclaresAnyField(schema: unknown): boolean;
|
|
@@ -124,6 +124,23 @@ export interface ConfigFieldBase {
|
|
|
124
124
|
readonly default?: unknown;
|
|
125
125
|
readonly span?: 1 | 2 | 3 | 4;
|
|
126
126
|
readonly showWhen?: ConfigCondition;
|
|
127
|
+
/**
|
|
128
|
+
* The field's stored value is a CREDENTIAL and must never leave the process
|
|
129
|
+
* on a read surface. Redaction machinery keys off
|
|
130
|
+
* {@link isSecretConfigField}, which treats `type: 'password'` as implicitly
|
|
131
|
+
* secret — this flag exists for the fields that carry a secret WITHOUT being
|
|
132
|
+
* a password input.
|
|
133
|
+
*
|
|
134
|
+
* The concrete miss it was added for: the SFTP provider's `privateKey` is a
|
|
135
|
+
* `textarea` (a PEM block does not fit a one-line password box) holding a
|
|
136
|
+
* complete SSH private key. A redaction that trusted `type: 'password'`
|
|
137
|
+
* alone would have shipped with that key still on the wire and an assurance
|
|
138
|
+
* that credentials were redacted — worse than no redaction at all.
|
|
139
|
+
*
|
|
140
|
+
* Mark it on any field whose value would compromise something if read by a
|
|
141
|
+
* non-admin session. It costs nothing when the field is already a password.
|
|
142
|
+
*/
|
|
143
|
+
readonly secret?: boolean;
|
|
127
144
|
/** Per-model knob applicability — see {@link ConfigFieldModelApplicability}. */
|
|
128
145
|
readonly modelApplicability?: ConfigFieldModelApplicability;
|
|
129
146
|
/**
|
|
@@ -39,6 +39,7 @@ export declare const RelocateJobSchema: z.ZodObject<{
|
|
|
39
39
|
filesMoved: z.ZodNumber;
|
|
40
40
|
bytesMoved: z.ZodNumber;
|
|
41
41
|
filesTotal: z.ZodNullable<z.ZodNumber>;
|
|
42
|
+
rowsReconciled: z.ZodOptional<z.ZodNumber>;
|
|
42
43
|
startedAt: z.ZodNumber;
|
|
43
44
|
finishedAt: z.ZodNullable<z.ZodNumber>;
|
|
44
45
|
error: z.ZodNullable<z.ZodString>;
|
|
@@ -128,14 +129,47 @@ export declare const RelocateMediaInputSchema: z.ZodObject<{
|
|
|
128
129
|
}>>;
|
|
129
130
|
}, z.core.$strip>;
|
|
130
131
|
export type RelocateMediaInput = z.infer<typeof RelocateMediaInputSchema>;
|
|
131
|
-
/**
|
|
132
|
-
*
|
|
133
|
-
*
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
132
|
+
/**
|
|
133
|
+
* The unstamped population of ONE collection — split, because the gate and the
|
|
134
|
+
* operator ask two different questions and only one of them has to be cheap.
|
|
135
|
+
*
|
|
136
|
+
* `present` is the GATE: "is there at least one row that would be orphaned by a
|
|
137
|
+
* repoint". It is a single indexed seek to the first matching row, so it stays
|
|
138
|
+
* answerable on a saturated disk and answers in O(log n) precisely in the state
|
|
139
|
+
* that matters — after a seal, when the population is empty.
|
|
140
|
+
*
|
|
141
|
+
* `rows` is the NUMBER, for the refusal message and the operator's sense of
|
|
142
|
+
* scale. It is a second, indexed `COUNT(*)`, and `null` means **not
|
|
143
|
+
* measurable** — never zero. `{ present: true, rows: null }` is a legitimate
|
|
144
|
+
* and useful answer: "there are some, and this read could not say how many"
|
|
145
|
+
* still refuses the cutover, which is the whole job.
|
|
146
|
+
*/
|
|
147
|
+
export declare const UnstampedRowsSchema: z.ZodObject<{
|
|
148
|
+
present: z.ZodBoolean;
|
|
149
|
+
rows: z.ZodNullable<z.ZodNumber>;
|
|
138
150
|
}, z.core.$strip>;
|
|
151
|
+
export type UnstampedRows = z.infer<typeof UnstampedRowsSchema>;
|
|
152
|
+
/**
|
|
153
|
+
* How many rows still carry NO `locationId` — the population a repoint would
|
|
154
|
+
* silently re-aim at a disk that does not hold their bytes.
|
|
155
|
+
*
|
|
156
|
+
* **`null` = the count could not be taken**, and it is NOT permission to cut
|
|
157
|
+
* over. The gate opens on a measured absence and on nothing else; an unread
|
|
158
|
+
* collection and an empty one are different facts, and this repo has already
|
|
159
|
+
* paid for conflating them (`RelocateResidueSchema`, D295).
|
|
160
|
+
*/
|
|
161
|
+
export declare const UnstampedEventMediaCountSchema: z.ZodNullable<z.ZodObject<{
|
|
162
|
+
media: z.ZodObject<{
|
|
163
|
+
present: z.ZodBoolean;
|
|
164
|
+
rows: z.ZodNullable<z.ZodNumber>;
|
|
165
|
+
}, z.core.$strip>;
|
|
166
|
+
retrainFrames: z.ZodObject<{
|
|
167
|
+
present: z.ZodBoolean;
|
|
168
|
+
rows: z.ZodNullable<z.ZodNumber>;
|
|
169
|
+
}, z.core.$strip>;
|
|
170
|
+
anyPresent: z.ZodBoolean;
|
|
171
|
+
total: z.ZodNullable<z.ZodNumber>;
|
|
172
|
+
}, z.core.$strip>>;
|
|
139
173
|
export type UnstampedEventMediaCount = z.infer<typeof UnstampedEventMediaCountSchema>;
|
|
140
174
|
export declare const StorageMigrationMediaMoveInputSchema: z.ZodObject<{
|
|
141
175
|
toLocationId: z.ZodString;
|
|
@@ -249,6 +283,35 @@ export declare const StorageMigrationParticipantSchema: z.ZodEnum<{
|
|
|
249
283
|
analytics: "analytics";
|
|
250
284
|
}>;
|
|
251
285
|
export type StorageMigrationParticipant = z.infer<typeof StorageMigrationParticipantSchema>;
|
|
286
|
+
/**
|
|
287
|
+
* The mover's own numbers, folded onto the coordinator's durable move record.
|
|
288
|
+
*
|
|
289
|
+
* The long half of a non-blocking migration is `draining`, and it is measured
|
|
290
|
+
* in hours: 136 885 files at ~4 MB/s is about five of them. Before this shape
|
|
291
|
+
* existed the only place those numbers appeared was a Loki line, so an operator
|
|
292
|
+
* watching the Admin UI saw `phase: draining` and nothing else for a whole
|
|
293
|
+
* afternoon.
|
|
294
|
+
*
|
|
295
|
+
* It is POLLED, never pushed. Events are telemetry and may be dropped
|
|
296
|
+
* (D8/D11), and a dropped progress event is indistinguishable from a stalled
|
|
297
|
+
* mover — which is the exact failure this is meant to end. The coordinator's
|
|
298
|
+
* `waitForMoves` already fetches the whole {@link RelocateJob} on every tick to
|
|
299
|
+
* read `state`; folding the counters costs no extra read and makes the durable
|
|
300
|
+
* record say afterwards how far a move actually got.
|
|
301
|
+
*
|
|
302
|
+
* `filesTotal` is `null` for "no honest denominator" and is never zero-filled:
|
|
303
|
+
* a windowed footage job (`sinceMs`) and a node with no ledger both genuinely
|
|
304
|
+
* cannot say M, and a 0 there would render as "100 % done".
|
|
305
|
+
*/
|
|
306
|
+
export declare const StorageMigrationMoveProgressSchema: z.ZodObject<{
|
|
307
|
+
filesMoved: z.ZodNumber;
|
|
308
|
+
filesTotal: z.ZodNullable<z.ZodNumber>;
|
|
309
|
+
bytesMoved: z.ZodNumber;
|
|
310
|
+
rowsReconciled: z.ZodOptional<z.ZodNumber>;
|
|
311
|
+
startedAt: z.ZodNumber;
|
|
312
|
+
observedAt: z.ZodNumber;
|
|
313
|
+
}, z.core.$strip>;
|
|
314
|
+
export type StorageMigrationMoveProgress = z.infer<typeof StorageMigrationMoveProgressSchema>;
|
|
252
315
|
export declare const StorageMigrationMoveSchema: z.ZodObject<{
|
|
253
316
|
storageClass: z.ZodEnum<{
|
|
254
317
|
recordings: "recordings";
|
|
@@ -268,6 +331,14 @@ export declare const StorageMigrationMoveSchema: z.ZodObject<{
|
|
|
268
331
|
cancelled: "cancelled";
|
|
269
332
|
}>>;
|
|
270
333
|
error: z.ZodNullable<z.ZodString>;
|
|
334
|
+
progress: z.ZodNullable<z.ZodObject<{
|
|
335
|
+
filesMoved: z.ZodNumber;
|
|
336
|
+
filesTotal: z.ZodNullable<z.ZodNumber>;
|
|
337
|
+
bytesMoved: z.ZodNumber;
|
|
338
|
+
rowsReconciled: z.ZodOptional<z.ZodNumber>;
|
|
339
|
+
startedAt: z.ZodNumber;
|
|
340
|
+
observedAt: z.ZodNumber;
|
|
341
|
+
}, z.core.$strip>>;
|
|
271
342
|
}, z.core.$strip>;
|
|
272
343
|
export type StorageMigrationMove = z.infer<typeof StorageMigrationMoveSchema>;
|
|
273
344
|
export declare const StorageMigrationJobSchema: z.ZodObject<{
|
|
@@ -317,6 +388,14 @@ export declare const StorageMigrationJobSchema: z.ZodObject<{
|
|
|
317
388
|
cancelled: "cancelled";
|
|
318
389
|
}>>;
|
|
319
390
|
error: z.ZodNullable<z.ZodString>;
|
|
391
|
+
progress: z.ZodNullable<z.ZodObject<{
|
|
392
|
+
filesMoved: z.ZodNumber;
|
|
393
|
+
filesTotal: z.ZodNullable<z.ZodNumber>;
|
|
394
|
+
bytesMoved: z.ZodNumber;
|
|
395
|
+
rowsReconciled: z.ZodOptional<z.ZodNumber>;
|
|
396
|
+
startedAt: z.ZodNumber;
|
|
397
|
+
observedAt: z.ZodNumber;
|
|
398
|
+
}, z.core.$strip>>;
|
|
320
399
|
}, z.core.$strip>>;
|
|
321
400
|
pauseLeaseId: z.ZodNullable<z.ZodString>;
|
|
322
401
|
pausedParticipants: z.ZodArray<z.ZodEnum<{
|
|
@@ -423,3 +502,143 @@ export declare const StorageMigrationPlanSchema: z.ZodObject<{
|
|
|
423
502
|
}, z.core.$strip>>;
|
|
424
503
|
}, z.core.$strip>;
|
|
425
504
|
export type StorageMigrationPlan = z.infer<typeof StorageMigrationPlanSchema>;
|
|
505
|
+
/**
|
|
506
|
+
* Which single-flight engine owns a class of work.
|
|
507
|
+
*
|
|
508
|
+
* Shared rather than re-declared per consumer: the coordinator lanes its moves
|
|
509
|
+
* by it, and `storageMigration.movers` labels a mover with it so an operator
|
|
510
|
+
* can see *which* engine is busy when a drain refuses to start beside another.
|
|
511
|
+
*/
|
|
512
|
+
export declare const StorageMigrationLaneSchema: z.ZodEnum<{
|
|
513
|
+
media: "media";
|
|
514
|
+
footage: "footage";
|
|
515
|
+
}>;
|
|
516
|
+
export type StorageMigrationLane = z.infer<typeof StorageMigrationLaneSchema>;
|
|
517
|
+
/**
|
|
518
|
+
* A mover as it exists RIGHT NOW, whether or not a migration job owns it.
|
|
519
|
+
*
|
|
520
|
+
* The coordinator's job record is the state of record for a migration, and its
|
|
521
|
+
* moves carry {@link StorageMigrationMoveProgress}. But the movers are usable
|
|
522
|
+
* standalone — `recording.relocateFootage` and `pipelineAnalytics.relocateMedia`
|
|
523
|
+
* are both operator-callable, and on 2026-08-29 a five-hour drain was armed that
|
|
524
|
+
* way because no supported UI path existed. A mover armed like that has no job
|
|
525
|
+
* to fold progress into, so it has to be readable on its own or it is invisible.
|
|
526
|
+
*
|
|
527
|
+
* `migrationJobId` is what tells the two apart: `null` means nothing here
|
|
528
|
+
* orchestrated it.
|
|
529
|
+
*/
|
|
530
|
+
export declare const StorageMigrationMoverSchema: z.ZodObject<{
|
|
531
|
+
lane: z.ZodEnum<{
|
|
532
|
+
media: "media";
|
|
533
|
+
footage: "footage";
|
|
534
|
+
}>;
|
|
535
|
+
job: z.ZodObject<{
|
|
536
|
+
jobId: z.ZodString;
|
|
537
|
+
state: z.ZodEnum<{
|
|
538
|
+
failed: "failed";
|
|
539
|
+
running: "running";
|
|
540
|
+
queued: "queued";
|
|
541
|
+
done: "done";
|
|
542
|
+
cancelled: "cancelled";
|
|
543
|
+
}>;
|
|
544
|
+
fromLocationId: z.ZodString;
|
|
545
|
+
toLocationId: z.ZodString;
|
|
546
|
+
deviceId: z.ZodNullable<z.ZodNumber>;
|
|
547
|
+
entities: z.ZodArray<z.ZodString>;
|
|
548
|
+
filesMoved: z.ZodNumber;
|
|
549
|
+
bytesMoved: z.ZodNumber;
|
|
550
|
+
filesTotal: z.ZodNullable<z.ZodNumber>;
|
|
551
|
+
rowsReconciled: z.ZodOptional<z.ZodNumber>;
|
|
552
|
+
startedAt: z.ZodNumber;
|
|
553
|
+
finishedAt: z.ZodNullable<z.ZodNumber>;
|
|
554
|
+
error: z.ZodNullable<z.ZodString>;
|
|
555
|
+
}, z.core.$strip>;
|
|
556
|
+
migrationJobId: z.ZodNullable<z.ZodString>;
|
|
557
|
+
observedAt: z.ZodNumber;
|
|
558
|
+
}, z.core.$strip>;
|
|
559
|
+
export type StorageMigrationMover = z.infer<typeof StorageMigrationMoverSchema>;
|
|
560
|
+
/**
|
|
561
|
+
* What a SOURCE still holds for one storage class — the number that makes a
|
|
562
|
+
* "drain remaining" action honest rather than hopeful.
|
|
563
|
+
*
|
|
564
|
+
* It comes from the archive (`SegmentHourLedger.census` for footage, the media
|
|
565
|
+
* engine's own selection count for media), never from the resident index: a
|
|
566
|
+
* drain sized off `RecordingIndex` is what reported `done` over 80.3 GB it had
|
|
567
|
+
* never been told about (D295).
|
|
568
|
+
*
|
|
569
|
+
* `items`/`bytes` are `null` for "the archive could not be asked", which is
|
|
570
|
+
* deliberately NOT zero: a drain is still offered for an unknown residue,
|
|
571
|
+
* because refusing on an unanswerable read would hide exactly the case an
|
|
572
|
+
* operator needs to act on.
|
|
573
|
+
*/
|
|
574
|
+
export declare const StorageMigrationResidueSchema: z.ZodObject<{
|
|
575
|
+
storageClass: z.ZodEnum<{
|
|
576
|
+
recordings: "recordings";
|
|
577
|
+
recordingsLow: "recordingsLow";
|
|
578
|
+
eventMedia: "eventMedia";
|
|
579
|
+
backups: "backups";
|
|
580
|
+
galleryMedia: "galleryMedia";
|
|
581
|
+
}>;
|
|
582
|
+
fromLocationId: z.ZodString;
|
|
583
|
+
toLocationId: z.ZodString;
|
|
584
|
+
items: z.ZodNullable<z.ZodNumber>;
|
|
585
|
+
bytes: z.ZodNullable<z.ZodNumber>;
|
|
586
|
+
}, z.core.$strip>;
|
|
587
|
+
export type StorageMigrationResidue = z.infer<typeof StorageMigrationResidueSchema>;
|
|
588
|
+
/**
|
|
589
|
+
* Run the DRAIN half and nothing else.
|
|
590
|
+
*
|
|
591
|
+
* A migration that reached `done` has already repointed, so `start` correctly
|
|
592
|
+
* refuses its destination ("already the default") — there is nothing left to
|
|
593
|
+
* repoint. But the drain can fail, be cancelled, be interrupted by a restart,
|
|
594
|
+
* or finish against a work list that was a tenth of the archive (D295), and
|
|
595
|
+
* before this there was no supported way to run only that half: the only way
|
|
596
|
+
* through was calling `recording.relocateFootage` by hand over admin tRPC.
|
|
597
|
+
*
|
|
598
|
+
* `drain` NEVER calls `setDefaultLocations`. That is what keeps `start`'s
|
|
599
|
+
* refusal meaningful: the two verbs are disjoint, so nothing here can silently
|
|
600
|
+
* re-repoint a class that is already migrated.
|
|
601
|
+
*/
|
|
602
|
+
export declare const StorageMigrationDrainInputSchema: z.ZodObject<{
|
|
603
|
+
classes: z.ZodArray<z.ZodEnum<{
|
|
604
|
+
recordings: "recordings";
|
|
605
|
+
recordingsLow: "recordingsLow";
|
|
606
|
+
eventMedia: "eventMedia";
|
|
607
|
+
backups: "backups";
|
|
608
|
+
galleryMedia: "galleryMedia";
|
|
609
|
+
}>>;
|
|
610
|
+
throttleMbps: z.ZodOptional<z.ZodNumber>;
|
|
611
|
+
}, z.core.$strip>;
|
|
612
|
+
export type StorageMigrationDrainInput = z.infer<typeof StorageMigrationDrainInputSchema>;
|
|
613
|
+
/** What a footage source still holds, asked of the durable hour ledger. */
|
|
614
|
+
export declare const RelocateResidueInputSchema: z.ZodObject<{
|
|
615
|
+
fromLocationId: z.ZodString;
|
|
616
|
+
footageClass: z.ZodOptional<z.ZodEnum<{
|
|
617
|
+
recordings: "recordings";
|
|
618
|
+
recordingsLow: "recordingsLow";
|
|
619
|
+
}>>;
|
|
620
|
+
}, z.core.$strip>;
|
|
621
|
+
export type RelocateResidueInput = z.infer<typeof RelocateResidueInputSchema>;
|
|
622
|
+
/** `null` = the archive could not answer (no ledger on this node, or the
|
|
623
|
+
* aggregate failed). Never conflated with an empty source. */
|
|
624
|
+
export declare const RelocateResidueSchema: z.ZodNullable<z.ZodObject<{
|
|
625
|
+
segments: z.ZodNumber;
|
|
626
|
+
bytes: z.ZodNumber;
|
|
627
|
+
}, z.core.$strip>>;
|
|
628
|
+
export type RelocateResidue = z.infer<typeof RelocateResidueSchema>;
|
|
629
|
+
/** How many rows a media pass would still act on against a given target — the
|
|
630
|
+
* media lane's denominator AND its residue, from ONE derivation so the two can
|
|
631
|
+
* never disagree. `null` = the count could not be taken. */
|
|
632
|
+
export declare const RelocatableMediaCountSchema: z.ZodNullable<z.ZodObject<{
|
|
633
|
+
rows: z.ZodNumber;
|
|
634
|
+
}, z.core.$strip>>;
|
|
635
|
+
export type RelocatableMediaCount = z.infer<typeof RelocatableMediaCountSchema>;
|
|
636
|
+
export declare const RelocatableMediaCountInputSchema: z.ZodObject<{
|
|
637
|
+
toLocationId: z.ZodString;
|
|
638
|
+
mode: z.ZodOptional<z.ZodEnum<{
|
|
639
|
+
move: "move";
|
|
640
|
+
seal: "seal";
|
|
641
|
+
gallery: "gallery";
|
|
642
|
+
}>>;
|
|
643
|
+
}, z.core.$strip>;
|
|
644
|
+
export type RelocatableMediaCountInput = z.infer<typeof RelocatableMediaCountInputSchema>;
|
|
@@ -19,6 +19,42 @@ import { z } from 'zod';
|
|
|
19
19
|
* two addons declaring the same `id` must agree on `cardinality` (validated
|
|
20
20
|
* at kernel aggregation time, not here).
|
|
21
21
|
*/
|
|
22
|
+
/**
|
|
23
|
+
* `StorageAccess` — how the service that DECLARED a storage-location kind
|
|
24
|
+
* actually reaches the bytes. It is the constraint that decides which
|
|
25
|
+
* `storage-provider`s may back a location of that kind.
|
|
26
|
+
*
|
|
27
|
+
* - `'local-path'` — the service asks `storage.resolve` for a path string and
|
|
28
|
+
* then does its own `node:fs` I/O on it (the recorder's segment writer, the
|
|
29
|
+
* post-analysis media roots). Only a provider that serves a genuine local
|
|
30
|
+
* filesystem (`getProviderInfo().nodeLocal === true`) can satisfy that: a
|
|
31
|
+
* remote provider's `resolve` returns a path on the REMOTE host, and
|
|
32
|
+
* `fs.readdir` of it on this node either fails or — far worse — succeeds
|
|
33
|
+
* against a same-named local directory that is something else entirely.
|
|
34
|
+
*
|
|
35
|
+
* - `'cap-mediated'` — every byte travels through the `storage` cap
|
|
36
|
+
* (`read`/`write`, or `beginUpload`/`writeChunk`/`finalizeUpload`). The
|
|
37
|
+
* service never sees a path, so any provider can back it. `backups` is the
|
|
38
|
+
* one kind that qualifies today.
|
|
39
|
+
*
|
|
40
|
+
* Before this existed, `recordings` was unreachable by SFTP/S3/WebDAV only as
|
|
41
|
+
* an EMERGENT property of how the recorder happened to be written. Nothing
|
|
42
|
+
* refused the configuration; the first write simply went somewhere wrong, and
|
|
43
|
+
* a recording write that goes wrong surfaces as a silent black window rather
|
|
44
|
+
* than an error (the read path does not `stat`). This turns that accident into
|
|
45
|
+
* a declared, enforced, testable refusal.
|
|
46
|
+
*/
|
|
47
|
+
export declare const StorageAccessSchema: z.ZodEnum<{
|
|
48
|
+
"local-path": "local-path";
|
|
49
|
+
"cap-mediated": "cap-mediated";
|
|
50
|
+
}>;
|
|
51
|
+
export type StorageAccess = z.infer<typeof StorageAccessSchema>;
|
|
52
|
+
/**
|
|
53
|
+
* What an ABSENT `access` means. Fail-closed: a declaration that says nothing
|
|
54
|
+
* is treated as if it does raw filesystem I/O, so a remote provider is
|
|
55
|
+
* refused for it. The permissive value must be written down.
|
|
56
|
+
*/
|
|
57
|
+
export declare const STORAGE_ACCESS_FALLBACK: StorageAccess;
|
|
22
58
|
export declare const StorageLocationDeclarationSchema: z.ZodObject<{
|
|
23
59
|
id: z.ZodString;
|
|
24
60
|
displayName: z.ZodString;
|
|
@@ -27,6 +63,10 @@ export declare const StorageLocationDeclarationSchema: z.ZodObject<{
|
|
|
27
63
|
single: "single";
|
|
28
64
|
multi: "multi";
|
|
29
65
|
}>;
|
|
66
|
+
access: z.ZodOptional<z.ZodEnum<{
|
|
67
|
+
"local-path": "local-path";
|
|
68
|
+
"cap-mediated": "cap-mediated";
|
|
69
|
+
}>>;
|
|
30
70
|
defaultsTo: z.ZodOptional<z.ZodString>;
|
|
31
71
|
defaultRoot: z.ZodOptional<z.ZodEnum<{
|
|
32
72
|
data: "data";
|
|
@@ -3446,6 +3446,7 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
3446
3446
|
cancelStorageMigrationMove: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "cancelStorageMigrationMove", "mutation", input),
|
|
3447
3447
|
relocateMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "relocateMedia", "mutation", input),
|
|
3448
3448
|
countUnstampedEventMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "countUnstampedEventMedia", "query", input),
|
|
3449
|
+
countRelocatableMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "countRelocatableMedia", "query", input),
|
|
3449
3450
|
listRelocateMediaJobs: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRelocateMediaJobs", "query", input),
|
|
3450
3451
|
cancelRelocateMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "cancelRelocateMedia", "mutation", input),
|
|
3451
3452
|
listOpsLog: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listOpsLog", "query", input),
|
|
@@ -3446,6 +3446,7 @@ function createDeviceProxy(api, binding, opts) {
|
|
|
3446
3446
|
cancelStorageMigrationMove: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "cancelStorageMigrationMove", "mutation", input),
|
|
3447
3447
|
relocateMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "relocateMedia", "mutation", input),
|
|
3448
3448
|
countUnstampedEventMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "countUnstampedEventMedia", "query", input),
|
|
3449
|
+
countRelocatableMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "countRelocatableMedia", "query", input),
|
|
3449
3450
|
listRelocateMediaJobs: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listRelocateMediaJobs", "query", input),
|
|
3450
3451
|
cancelRelocateMedia: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "cancelRelocateMedia", "mutation", input),
|
|
3451
3452
|
listOpsLog: (input) => dispatch("pipeline-analytics", "pipelineAnalytics", "listOpsLog", "query", input),
|