@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.
@@ -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
- /** How many rows still carry NO `locationId` — the population a repoint would
132
- * silently re-aim at a disk that does not hold their bytes. Zero is the only
133
- * value that permits a non-blocking `eventMedia` cutover. */
134
- export declare const UnstampedEventMediaCountSchema: z.ZodObject<{
135
- media: z.ZodNumber;
136
- retrainFrames: z.ZodNumber;
137
- total: z.ZodNumber;
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),
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/types",
3
- "version": "1.2.124",
3
+ "version": "1.2.127",
4
4
  "description": "Shared types, interfaces, and model catalogs for the CamStack detection ecosystem",
5
5
  "keywords": [
6
6
  "camstack",