@camstack/addon-remote-storage 1.1.12 → 1.1.14

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/s3.addon.js CHANGED
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-BpumIDGo.js");
5
+ const require_shared = require("./shared-N5OfEciV.js");
6
6
  let node_stream = require("node:stream");
7
7
  let _aws_sdk_client_s3 = require("@aws-sdk/client-s3");
8
8
  let _aws_sdk_lib_storage = require("@aws-sdk/lib-storage");
package/dist/s3.addon.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { c as BaseAddon, i as rearmIdleAbort, n as getOptionalBasePath, o as scheduleIdleAbort, s as storageProviderCapability, t as createSessionId } from "./shared-fV3ZB0lu.mjs";
1
+ import { c as BaseAddon, i as rearmIdleAbort, n as getOptionalBasePath, o as scheduleIdleAbort, s as storageProviderCapability, t as createSessionId } from "./shared-D9u4CXu8.mjs";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { DeleteObjectCommand, GetObjectCommand, HeadBucketCommand, HeadObjectCommand, ListObjectsV2Command, PutObjectCommand, S3Client } from "@aws-sdk/client-s3";
4
4
  import { Upload } from "@aws-sdk/lib-storage";
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-BpumIDGo.js");
5
+ const require_shared = require("./shared-N5OfEciV.js");
6
6
  let ssh2 = require("ssh2");
7
7
  let node_path = require("node:path");
8
8
  node_path = require_shared.__toESM(node_path);
@@ -1,4 +1,4 @@
1
- import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-fV3ZB0lu.mjs";
1
+ import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-D9u4CXu8.mjs";
2
2
  import { Client } from "ssh2";
3
3
  import * as path from "node:path";
4
4
  //#region src/providers/sftp/sftp-config-schema.ts
@@ -4629,7 +4629,7 @@ function _instanceof(cls, params = {}) {
4629
4629
  return inst;
4630
4630
  }
4631
4631
  //#endregion
4632
- //#region ../types/dist/sleep-C2M2zF7x.mjs
4632
+ //#region ../types/dist/sleep-MHm--th-.mjs
4633
4633
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4634
4634
  EventCategory["SystemBoot"] = "system.boot";
4635
4635
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5891,6 +5891,7 @@ var CamStreamKindSchema = _enum([
5891
5891
  "pull-rtsp",
5892
5892
  "pull-rtmp",
5893
5893
  "pull-http",
5894
+ "pull-flv",
5894
5895
  "pull-rfc4571",
5895
5896
  "push-annexb",
5896
5897
  "derived"
@@ -7034,7 +7035,21 @@ var StorageLocationDeclarationSchema = object({
7034
7035
  * slots (e.g. `recordingsLow` → `recordings`) so operators only need to
7035
7036
  * configure the primary location.
7036
7037
  */
7037
- defaultsTo: string().optional()
7038
+ defaultsTo: string().optional(),
7039
+ /**
7040
+ * Which node root the seeded `<id>:default` instance is placed under on a
7041
+ * FRESH install:
7042
+ * - `'data'` (default) — the node's data dir (`CAMSTACK_DATA` / boot dir),
7043
+ * the appData volume. Right for small/durable data (backups, logs, models).
7044
+ * - `'media'` — the dedicated media volume (`CAMSTACK_MEDIA_ROOT`) when that
7045
+ * env is set, else falls back to the data root. Right for bulky, hot media
7046
+ * (recordings, event media) that should stay off the appData disk.
7047
+ *
7048
+ * Only affects the seeded default's `basePath`; operators can repoint any
7049
+ * location afterwards, and a `defaultsTo` slot inherits its parent's root
7050
+ * regardless of this field. Absent (the common case) is treated as `'data'`.
7051
+ */
7052
+ defaultRoot: _enum(["data", "media"]).optional()
7038
7053
  });
7039
7054
  var DecoderStatsSchema = object({
7040
7055
  inputFps: number(),
@@ -7881,6 +7896,10 @@ var RtspRestreamEntrySchema = object({
7881
7896
  var BrokerRtspClientSchema = object({
7882
7897
  sessionId: string(),
7883
7898
  remoteAddr: string(),
7899
+ /** Client `User-Agent` (e.g. `recorder` for the recorder's RTSP pull), or
7900
+ * null/absent when the client sent none. Lets the UI label a consumer by
7901
+ * purpose. Optional so a client built against an older schema stays valid. */
7902
+ userAgent: string().nullish(),
7884
7903
  playing: boolean(),
7885
7904
  muted: boolean(),
7886
7905
  connectedAt: number(),
@@ -13170,7 +13189,7 @@ var AddBrokerInputSchema = object({
13170
13189
  });
13171
13190
  var AddBrokerResultSchema = object({ id: string() });
13172
13191
  var IdInputSchema = object({ id: string() });
13173
- var TestResultSchema = discriminatedUnion("ok", [object({
13192
+ var TestResultSchema$1 = discriminatedUnion("ok", [object({
13174
13193
  ok: literal(true),
13175
13194
  latencyMs: number()
13176
13195
  }), object({
@@ -13193,7 +13212,7 @@ var StatusSchema = object({
13193
13212
  brokerCount: number(),
13194
13213
  embeddedRunning: boolean()
13195
13214
  });
13196
- method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
13215
+ method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema$1, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
13197
13216
  var NetworkEndpointSchema = object({
13198
13217
  url: string(),
13199
13218
  hostname: string(),
@@ -13227,23 +13246,198 @@ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
13227
13246
  sourcePort: number().optional()
13228
13247
  });
13229
13248
  method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
13230
- method(object({
13231
- title: string(),
13249
+ /**
13250
+ * notification-output — canonical, capability-gated notification delivery.
13251
+ *
13252
+ * Apprise-derived model (see
13253
+ * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
13254
+ * callers emit ONE canonical `Notification`; each provider declares a
13255
+ * per-kind capability descriptor (`TargetKind`), and the pure degrade
13256
+ * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
13257
+ * message to what the kind supports — callers never special-case a service.
13258
+ *
13259
+ * DESIGN DECISIONS (locked):
13260
+ * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
13261
+ * `setTargetEnabled`), each provider persisting via the `settings-store`
13262
+ * cap. Rationale: the admin UI needs one uniform surface across the
13263
+ * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
13264
+ * alternative would fork the UI per addon and cannot host the
13265
+ * discovery→adopt flow.
13266
+ * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
13267
+ * the generated cap-mount auto-`concatCollection`-fans them across every
13268
+ * registered provider (notifiers addon + HA addon) so one catalog is
13269
+ * routable. `send` / `testTarget` / CRUD route to ONE provider by the
13270
+ * `addonId` the generated collection router extracts from the call input.
13271
+ * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
13272
+ * transformer) + UDS MsgPack both round-trip typed arrays — already used by
13273
+ * `storage` / `storage-provider` / `recording` caps over the same path. No
13274
+ * base64 fallback needed.
13275
+ *
13276
+ * TODO (deferred, closed-set change — separate decision): add
13277
+ * `providerKind: 'notify'` so notification providers surface on the unified
13278
+ * admin "Integrations" page.
13279
+ */
13280
+ /**
13281
+ * Zentik-derived typed-media enum — the superset across every kind. Each
13282
+ * adapter picks what it supports and the degrade engine filters the rest.
13283
+ */
13284
+ var AttachmentMediaTypeSchema = _enum([
13285
+ "image",
13286
+ "video",
13287
+ "gif",
13288
+ "audio",
13289
+ "icon"
13290
+ ]);
13291
+ /**
13292
+ * A single attachment. Exactly one of `url` (remote source, most adapters
13293
+ * prefer this) or `bytes` (inline source; required for Pushover-style
13294
+ * bytes-only kinds) MUST be present — the degrade engine expresses a
13295
+ * url→bytes fetch as a `needsFetch` directive the adapter executes.
13296
+ */
13297
+ var AttachmentSchema = object({
13298
+ mediaType: AttachmentMediaTypeSchema,
13299
+ url: string().optional(),
13300
+ bytes: _instanceof(Uint8Array).optional(),
13301
+ mime: string().optional(),
13302
+ name: string().optional()
13303
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
13304
+ var NotificationFormatSchema = _enum([
13305
+ "text",
13306
+ "markdown",
13307
+ "html"
13308
+ ]);
13309
+ /** A single tap-through action button. */
13310
+ var NotificationActionSchema = object({
13311
+ id: string(),
13312
+ label: string(),
13313
+ url: string().optional()
13314
+ });
13315
+ /**
13316
+ * The canonical notification. `body` is the only hard field (Apprise model).
13317
+ * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
13318
+ * NOT a fixed severity enum — each kind declares its own `caps.levels` and
13319
+ * the adapter maps this ordinal onto its native level. `level?` is an
13320
+ * optional kind-native level id (`emergency`, `silent`, …) that overrides
13321
+ * `priority` for that one target.
13322
+ */
13323
+ var NotificationSchema = object({
13232
13324
  body: string(),
13233
- imageUrl: string().optional(),
13325
+ title: string().optional(),
13326
+ format: NotificationFormatSchema.default("text"),
13327
+ priority: number().int().min(1).max(5).default(3),
13328
+ level: string().optional(),
13329
+ attachments: array(AttachmentSchema).optional(),
13330
+ clickUrl: string().optional(),
13331
+ actions: array(NotificationActionSchema).optional(),
13332
+ sound: string().optional(),
13333
+ ttl: number().optional(),
13334
+ tag: string().optional(),
13234
13335
  deviceId: number().optional(),
13235
13336
  eventId: string().optional(),
13236
- priority: _enum([
13237
- "low",
13238
- "normal",
13239
- "high",
13240
- "critical"
13241
- ]).default("normal"),
13242
13337
  metadata: record(string(), unknown()).optional()
13243
- }), _void(), { kind: "mutation" }), method(_void(), object({
13338
+ });
13339
+ /** One declared native severity/priority level for a kind. */
13340
+ var TargetKindLevelSchema = object({
13341
+ id: string(),
13342
+ label: string(),
13343
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
13344
+ ordinal: number().int().min(1).max(5).nullable(),
13345
+ flags: object({
13346
+ critical: boolean().optional(),
13347
+ silent: boolean().optional(),
13348
+ noPush: boolean().optional()
13349
+ }).optional(),
13350
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
13351
+ requires: array(string()).optional(),
13352
+ description: string().optional()
13353
+ });
13354
+ /** The full capability block consulted before dispatch. */
13355
+ var TargetKindCapsSchema = object({
13356
+ attachments: object({
13357
+ mediaTypes: array(AttachmentMediaTypeSchema),
13358
+ mode: _enum([
13359
+ "url",
13360
+ "bytes",
13361
+ "both"
13362
+ ]),
13363
+ max: number().int().nonnegative(),
13364
+ maxBytes: number().int().positive().optional()
13365
+ }),
13366
+ /** Max action buttons (0 = none). */
13367
+ actions: number().int().nonnegative(),
13368
+ levels: array(TargetKindLevelSchema),
13369
+ format: array(NotificationFormatSchema),
13370
+ clickUrl: boolean(),
13371
+ sound: boolean(),
13372
+ ttl: boolean(),
13373
+ bodyMaxLen: number().int().positive()
13374
+ });
13375
+ /**
13376
+ * `configSchema` is a `ConfigUISchema` tree passed through to the admin
13377
+ * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
13378
+ * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
13379
+ * the union is large and not meant for runtime validation here; the exported
13380
+ * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
13381
+ */
13382
+ var ConfigSchemaPassthrough = unknown();
13383
+ var TargetKindSchema = object({
13384
+ kind: string(),
13385
+ label: string(),
13386
+ icon: string(),
13387
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
13388
+ addonId: string(),
13389
+ configSchema: ConfigSchemaPassthrough,
13390
+ supportsDiscovery: boolean(),
13391
+ caps: TargetKindCapsSchema
13392
+ });
13393
+ /**
13394
+ * A persisted target. `config` holds secrets; providers REDACT secret fields
13395
+ * (return a presence marker only) when serving `listTargets` — never
13396
+ * round-trip a stored secret to the UI.
13397
+ */
13398
+ var TargetSchema = object({
13399
+ id: string(),
13400
+ name: string(),
13401
+ kind: string(),
13402
+ addonId: string(),
13403
+ enabled: boolean(),
13404
+ config: record(string(), unknown())
13405
+ });
13406
+ /** A discovery-surfaced candidate (config is partial + non-secret). */
13407
+ var DiscoveredTargetSchema = object({
13408
+ kind: string(),
13409
+ suggestedName: string(),
13410
+ config: record(string(), unknown())
13411
+ });
13412
+ /** The degrade engine's report — what was resolved / dropped / degraded. */
13413
+ var RenderedAsSchema = object({
13414
+ level: string(),
13415
+ format: NotificationFormatSchema,
13416
+ attachmentsSent: number().int().nonnegative(),
13417
+ actionsSent: number().int().nonnegative(),
13418
+ truncated: boolean(),
13419
+ dropped: array(string())
13420
+ });
13421
+ var SendResultSchema = object({
13244
13422
  success: boolean(),
13245
- error: string().optional()
13246
- }), { kind: "mutation" });
13423
+ error: string().optional(),
13424
+ renderedAs: RenderedAsSchema.optional()
13425
+ });
13426
+ /** Same shape as SendResult — kept as a distinct name for the test panel. */
13427
+ var TestResultSchema = SendResultSchema;
13428
+ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema)), method(object({
13429
+ kind: string(),
13430
+ config: record(string(), unknown()).optional()
13431
+ }), array(DiscoveredTargetSchema)), method(object({
13432
+ targetId: string(),
13433
+ notification: NotificationSchema
13434
+ }), SendResultSchema, { kind: "mutation" }), method(object({
13435
+ targetId: string(),
13436
+ sample: NotificationSchema.optional()
13437
+ }), TestResultSchema, { kind: "mutation" }), method(object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }), method(object({ targetId: string() }), _void(), { kind: "mutation" }), method(object({
13438
+ targetId: string(),
13439
+ enabled: boolean()
13440
+ }), _void(), { kind: "mutation" });
13247
13441
  /**
13248
13442
  * Zod schemas for persisted record types.
13249
13443
  *
@@ -19283,13 +19477,49 @@ Object.freeze({
19283
19477
  addonId: null,
19284
19478
  access: "create"
19285
19479
  },
19480
+ "notificationOutput.deleteTarget": {
19481
+ capName: "notification-output",
19482
+ capScope: "system",
19483
+ addonId: null,
19484
+ access: "delete"
19485
+ },
19486
+ "notificationOutput.discoverTargets": {
19487
+ capName: "notification-output",
19488
+ capScope: "system",
19489
+ addonId: null,
19490
+ access: "view"
19491
+ },
19492
+ "notificationOutput.listTargetKinds": {
19493
+ capName: "notification-output",
19494
+ capScope: "system",
19495
+ addonId: null,
19496
+ access: "view"
19497
+ },
19498
+ "notificationOutput.listTargets": {
19499
+ capName: "notification-output",
19500
+ capScope: "system",
19501
+ addonId: null,
19502
+ access: "view"
19503
+ },
19286
19504
  "notificationOutput.send": {
19287
19505
  capName: "notification-output",
19288
19506
  capScope: "system",
19289
19507
  addonId: null,
19290
19508
  access: "create"
19291
19509
  },
19292
- "notificationOutput.sendTest": {
19510
+ "notificationOutput.setTargetEnabled": {
19511
+ capName: "notification-output",
19512
+ capScope: "system",
19513
+ addonId: null,
19514
+ access: "create"
19515
+ },
19516
+ "notificationOutput.testTarget": {
19517
+ capName: "notification-output",
19518
+ capScope: "system",
19519
+ addonId: null,
19520
+ access: "create"
19521
+ },
19522
+ "notificationOutput.upsertTarget": {
19293
19523
  capName: "notification-output",
19294
19524
  capScope: "system",
19295
19525
  addonId: null,
@@ -4652,7 +4652,7 @@ function _instanceof(cls, params = {}) {
4652
4652
  return inst;
4653
4653
  }
4654
4654
  //#endregion
4655
- //#region ../types/dist/sleep-C2M2zF7x.mjs
4655
+ //#region ../types/dist/sleep-MHm--th-.mjs
4656
4656
  var EventCategory = /* @__PURE__ */ function(EventCategory) {
4657
4657
  EventCategory["SystemBoot"] = "system.boot";
4658
4658
  EventCategory["SystemAddonsReady"] = "system.addons-ready";
@@ -5914,6 +5914,7 @@ var CamStreamKindSchema = _enum([
5914
5914
  "pull-rtsp",
5915
5915
  "pull-rtmp",
5916
5916
  "pull-http",
5917
+ "pull-flv",
5917
5918
  "pull-rfc4571",
5918
5919
  "push-annexb",
5919
5920
  "derived"
@@ -7057,7 +7058,21 @@ var StorageLocationDeclarationSchema = object({
7057
7058
  * slots (e.g. `recordingsLow` → `recordings`) so operators only need to
7058
7059
  * configure the primary location.
7059
7060
  */
7060
- defaultsTo: string().optional()
7061
+ defaultsTo: string().optional(),
7062
+ /**
7063
+ * Which node root the seeded `<id>:default` instance is placed under on a
7064
+ * FRESH install:
7065
+ * - `'data'` (default) — the node's data dir (`CAMSTACK_DATA` / boot dir),
7066
+ * the appData volume. Right for small/durable data (backups, logs, models).
7067
+ * - `'media'` — the dedicated media volume (`CAMSTACK_MEDIA_ROOT`) when that
7068
+ * env is set, else falls back to the data root. Right for bulky, hot media
7069
+ * (recordings, event media) that should stay off the appData disk.
7070
+ *
7071
+ * Only affects the seeded default's `basePath`; operators can repoint any
7072
+ * location afterwards, and a `defaultsTo` slot inherits its parent's root
7073
+ * regardless of this field. Absent (the common case) is treated as `'data'`.
7074
+ */
7075
+ defaultRoot: _enum(["data", "media"]).optional()
7061
7076
  });
7062
7077
  var DecoderStatsSchema = object({
7063
7078
  inputFps: number(),
@@ -7904,6 +7919,10 @@ var RtspRestreamEntrySchema = object({
7904
7919
  var BrokerRtspClientSchema = object({
7905
7920
  sessionId: string(),
7906
7921
  remoteAddr: string(),
7922
+ /** Client `User-Agent` (e.g. `recorder` for the recorder's RTSP pull), or
7923
+ * null/absent when the client sent none. Lets the UI label a consumer by
7924
+ * purpose. Optional so a client built against an older schema stays valid. */
7925
+ userAgent: string().nullish(),
7907
7926
  playing: boolean(),
7908
7927
  muted: boolean(),
7909
7928
  connectedAt: number(),
@@ -13193,7 +13212,7 @@ var AddBrokerInputSchema = object({
13193
13212
  });
13194
13213
  var AddBrokerResultSchema = object({ id: string() });
13195
13214
  var IdInputSchema = object({ id: string() });
13196
- var TestResultSchema = discriminatedUnion("ok", [object({
13215
+ var TestResultSchema$1 = discriminatedUnion("ok", [object({
13197
13216
  ok: literal(true),
13198
13217
  latencyMs: number()
13199
13218
  }), object({
@@ -13216,7 +13235,7 @@ var StatusSchema = object({
13216
13235
  brokerCount: number(),
13217
13236
  embeddedRunning: boolean()
13218
13237
  });
13219
- method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
13238
+ method(_void(), array(BrokerInfoSchema)), method(IdInputSchema, BrokerConnectionDetailsSchema), method(AddBrokerInputSchema, AddBrokerResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(IdInputSchema, TestResultSchema$1, { kind: "mutation" }), method(StartEmbeddedInputSchema, StartEmbeddedResultSchema, { kind: "mutation" }), method(IdInputSchema, _void(), { kind: "mutation" }), method(_void(), StatusSchema);
13220
13239
  var NetworkEndpointSchema = object({
13221
13240
  url: string(),
13222
13241
  hostname: string(),
@@ -13250,23 +13269,198 @@ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
13250
13269
  sourcePort: number().optional()
13251
13270
  });
13252
13271
  method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
13253
- method(object({
13254
- title: string(),
13272
+ /**
13273
+ * notification-output — canonical, capability-gated notification delivery.
13274
+ *
13275
+ * Apprise-derived model (see
13276
+ * `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
13277
+ * callers emit ONE canonical `Notification`; each provider declares a
13278
+ * per-kind capability descriptor (`TargetKind`), and the pure degrade
13279
+ * engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
13280
+ * message to what the kind supports — callers never special-case a service.
13281
+ *
13282
+ * DESIGN DECISIONS (locked):
13283
+ * - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
13284
+ * `setTargetEnabled`), each provider persisting via the `settings-store`
13285
+ * cap. Rationale: the admin UI needs one uniform surface across the
13286
+ * notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
13287
+ * alternative would fork the UI per addon and cannot host the
13288
+ * discovery→adopt flow.
13289
+ * - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
13290
+ * the generated cap-mount auto-`concatCollection`-fans them across every
13291
+ * registered provider (notifiers addon + HA addon) so one catalog is
13292
+ * routable. `send` / `testTarget` / CRUD route to ONE provider by the
13293
+ * `addonId` the generated collection router extracts from the call input.
13294
+ * - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
13295
+ * transformer) + UDS MsgPack both round-trip typed arrays — already used by
13296
+ * `storage` / `storage-provider` / `recording` caps over the same path. No
13297
+ * base64 fallback needed.
13298
+ *
13299
+ * TODO (deferred, closed-set change — separate decision): add
13300
+ * `providerKind: 'notify'` so notification providers surface on the unified
13301
+ * admin "Integrations" page.
13302
+ */
13303
+ /**
13304
+ * Zentik-derived typed-media enum — the superset across every kind. Each
13305
+ * adapter picks what it supports and the degrade engine filters the rest.
13306
+ */
13307
+ var AttachmentMediaTypeSchema = _enum([
13308
+ "image",
13309
+ "video",
13310
+ "gif",
13311
+ "audio",
13312
+ "icon"
13313
+ ]);
13314
+ /**
13315
+ * A single attachment. Exactly one of `url` (remote source, most adapters
13316
+ * prefer this) or `bytes` (inline source; required for Pushover-style
13317
+ * bytes-only kinds) MUST be present — the degrade engine expresses a
13318
+ * url→bytes fetch as a `needsFetch` directive the adapter executes.
13319
+ */
13320
+ var AttachmentSchema = object({
13321
+ mediaType: AttachmentMediaTypeSchema,
13322
+ url: string().optional(),
13323
+ bytes: _instanceof(Uint8Array).optional(),
13324
+ mime: string().optional(),
13325
+ name: string().optional()
13326
+ }).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
13327
+ var NotificationFormatSchema = _enum([
13328
+ "text",
13329
+ "markdown",
13330
+ "html"
13331
+ ]);
13332
+ /** A single tap-through action button. */
13333
+ var NotificationActionSchema = object({
13334
+ id: string(),
13335
+ label: string(),
13336
+ url: string().optional()
13337
+ });
13338
+ /**
13339
+ * The canonical notification. `body` is the only hard field (Apprise model).
13340
+ * `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
13341
+ * NOT a fixed severity enum — each kind declares its own `caps.levels` and
13342
+ * the adapter maps this ordinal onto its native level. `level?` is an
13343
+ * optional kind-native level id (`emergency`, `silent`, …) that overrides
13344
+ * `priority` for that one target.
13345
+ */
13346
+ var NotificationSchema = object({
13255
13347
  body: string(),
13256
- imageUrl: string().optional(),
13348
+ title: string().optional(),
13349
+ format: NotificationFormatSchema.default("text"),
13350
+ priority: number().int().min(1).max(5).default(3),
13351
+ level: string().optional(),
13352
+ attachments: array(AttachmentSchema).optional(),
13353
+ clickUrl: string().optional(),
13354
+ actions: array(NotificationActionSchema).optional(),
13355
+ sound: string().optional(),
13356
+ ttl: number().optional(),
13357
+ tag: string().optional(),
13257
13358
  deviceId: number().optional(),
13258
13359
  eventId: string().optional(),
13259
- priority: _enum([
13260
- "low",
13261
- "normal",
13262
- "high",
13263
- "critical"
13264
- ]).default("normal"),
13265
13360
  metadata: record(string(), unknown()).optional()
13266
- }), _void(), { kind: "mutation" }), method(_void(), object({
13361
+ });
13362
+ /** One declared native severity/priority level for a kind. */
13363
+ var TargetKindLevelSchema = object({
13364
+ id: string(),
13365
+ label: string(),
13366
+ /** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
13367
+ ordinal: number().int().min(1).max(5).nullable(),
13368
+ flags: object({
13369
+ critical: boolean().optional(),
13370
+ silent: boolean().optional(),
13371
+ noPush: boolean().optional()
13372
+ }).optional(),
13373
+ /** e.g. Pushover `emergency` requires `retry` / `expire`. */
13374
+ requires: array(string()).optional(),
13375
+ description: string().optional()
13376
+ });
13377
+ /** The full capability block consulted before dispatch. */
13378
+ var TargetKindCapsSchema = object({
13379
+ attachments: object({
13380
+ mediaTypes: array(AttachmentMediaTypeSchema),
13381
+ mode: _enum([
13382
+ "url",
13383
+ "bytes",
13384
+ "both"
13385
+ ]),
13386
+ max: number().int().nonnegative(),
13387
+ maxBytes: number().int().positive().optional()
13388
+ }),
13389
+ /** Max action buttons (0 = none). */
13390
+ actions: number().int().nonnegative(),
13391
+ levels: array(TargetKindLevelSchema),
13392
+ format: array(NotificationFormatSchema),
13393
+ clickUrl: boolean(),
13394
+ sound: boolean(),
13395
+ ttl: boolean(),
13396
+ bodyMaxLen: number().int().positive()
13397
+ });
13398
+ /**
13399
+ * `configSchema` is a `ConfigUISchema` tree passed through to the admin
13400
+ * FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
13401
+ * `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
13402
+ * the union is large and not meant for runtime validation here; the exported
13403
+ * `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
13404
+ */
13405
+ var ConfigSchemaPassthrough = unknown();
13406
+ var TargetKindSchema = object({
13407
+ kind: string(),
13408
+ label: string(),
13409
+ icon: string(),
13410
+ /** Stamped by each provider so the concat-fanned catalog stays routable. */
13411
+ addonId: string(),
13412
+ configSchema: ConfigSchemaPassthrough,
13413
+ supportsDiscovery: boolean(),
13414
+ caps: TargetKindCapsSchema
13415
+ });
13416
+ /**
13417
+ * A persisted target. `config` holds secrets; providers REDACT secret fields
13418
+ * (return a presence marker only) when serving `listTargets` — never
13419
+ * round-trip a stored secret to the UI.
13420
+ */
13421
+ var TargetSchema = object({
13422
+ id: string(),
13423
+ name: string(),
13424
+ kind: string(),
13425
+ addonId: string(),
13426
+ enabled: boolean(),
13427
+ config: record(string(), unknown())
13428
+ });
13429
+ /** A discovery-surfaced candidate (config is partial + non-secret). */
13430
+ var DiscoveredTargetSchema = object({
13431
+ kind: string(),
13432
+ suggestedName: string(),
13433
+ config: record(string(), unknown())
13434
+ });
13435
+ /** The degrade engine's report — what was resolved / dropped / degraded. */
13436
+ var RenderedAsSchema = object({
13437
+ level: string(),
13438
+ format: NotificationFormatSchema,
13439
+ attachmentsSent: number().int().nonnegative(),
13440
+ actionsSent: number().int().nonnegative(),
13441
+ truncated: boolean(),
13442
+ dropped: array(string())
13443
+ });
13444
+ var SendResultSchema = object({
13267
13445
  success: boolean(),
13268
- error: string().optional()
13269
- }), { kind: "mutation" });
13446
+ error: string().optional(),
13447
+ renderedAs: RenderedAsSchema.optional()
13448
+ });
13449
+ /** Same shape as SendResult — kept as a distinct name for the test panel. */
13450
+ var TestResultSchema = SendResultSchema;
13451
+ method(object({}), array(TargetKindSchema)), method(object({}), array(TargetSchema)), method(object({
13452
+ kind: string(),
13453
+ config: record(string(), unknown()).optional()
13454
+ }), array(DiscoveredTargetSchema)), method(object({
13455
+ targetId: string(),
13456
+ notification: NotificationSchema
13457
+ }), SendResultSchema, { kind: "mutation" }), method(object({
13458
+ targetId: string(),
13459
+ sample: NotificationSchema.optional()
13460
+ }), TestResultSchema, { kind: "mutation" }), method(object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }), method(object({ targetId: string() }), _void(), { kind: "mutation" }), method(object({
13461
+ targetId: string(),
13462
+ enabled: boolean()
13463
+ }), _void(), { kind: "mutation" });
13270
13464
  /**
13271
13465
  * Zod schemas for persisted record types.
13272
13466
  *
@@ -19306,13 +19500,49 @@ Object.freeze({
19306
19500
  addonId: null,
19307
19501
  access: "create"
19308
19502
  },
19503
+ "notificationOutput.deleteTarget": {
19504
+ capName: "notification-output",
19505
+ capScope: "system",
19506
+ addonId: null,
19507
+ access: "delete"
19508
+ },
19509
+ "notificationOutput.discoverTargets": {
19510
+ capName: "notification-output",
19511
+ capScope: "system",
19512
+ addonId: null,
19513
+ access: "view"
19514
+ },
19515
+ "notificationOutput.listTargetKinds": {
19516
+ capName: "notification-output",
19517
+ capScope: "system",
19518
+ addonId: null,
19519
+ access: "view"
19520
+ },
19521
+ "notificationOutput.listTargets": {
19522
+ capName: "notification-output",
19523
+ capScope: "system",
19524
+ addonId: null,
19525
+ access: "view"
19526
+ },
19309
19527
  "notificationOutput.send": {
19310
19528
  capName: "notification-output",
19311
19529
  capScope: "system",
19312
19530
  addonId: null,
19313
19531
  access: "create"
19314
19532
  },
19315
- "notificationOutput.sendTest": {
19533
+ "notificationOutput.setTargetEnabled": {
19534
+ capName: "notification-output",
19535
+ capScope: "system",
19536
+ addonId: null,
19537
+ access: "create"
19538
+ },
19539
+ "notificationOutput.testTarget": {
19540
+ capName: "notification-output",
19541
+ capScope: "system",
19542
+ addonId: null,
19543
+ access: "create"
19544
+ },
19545
+ "notificationOutput.upsertTarget": {
19316
19546
  capName: "notification-output",
19317
19547
  capScope: "system",
19318
19548
  addonId: null,
@@ -2,7 +2,7 @@ Object.defineProperties(exports, {
2
2
  __esModule: { value: true },
3
3
  [Symbol.toStringTag]: { value: "Module" }
4
4
  });
5
- const require_shared = require("./shared-BpumIDGo.js");
5
+ const require_shared = require("./shared-N5OfEciV.js");
6
6
  let node_stream = require("node:stream");
7
7
  let webdav = require("webdav");
8
8
  //#region src/providers/webdav/webdav-config-schema.ts
@@ -1,4 +1,4 @@
1
- import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-fV3ZB0lu.mjs";
1
+ import { a as safeJoinRemotePath, c as BaseAddon, i as rearmIdleAbort, o as scheduleIdleAbort, r as getRequiredBasePath, s as storageProviderCapability, t as createSessionId } from "./shared-D9u4CXu8.mjs";
2
2
  import { PassThrough } from "node:stream";
3
3
  import { AuthType, createClient } from "webdav";
4
4
  //#region src/providers/webdav/webdav-config-schema.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@camstack/addon-remote-storage",
3
- "version": "1.1.12",
3
+ "version": "1.1.14",
4
4
  "description": "Remote storage providers (SFTP, S3, WebDAV) — unifies remote backends behind the storage-provider cap",
5
5
  "keywords": [
6
6
  "camstack",
@@ -36,6 +36,9 @@
36
36
  "addons": [
37
37
  {
38
38
  "id": "sftp-storage",
39
+ "execution": {
40
+ "group": "remote-storage"
41
+ },
39
42
  "category": "system",
40
43
  "name": "SFTP Storage",
41
44
  "version": "0.1.1",
@@ -52,6 +55,9 @@
52
55
  },
53
56
  {
54
57
  "id": "s3-storage",
58
+ "execution": {
59
+ "group": "remote-storage"
60
+ },
55
61
  "category": "system",
56
62
  "name": "S3 Storage",
57
63
  "version": "0.1.1",
@@ -68,6 +74,9 @@
68
74
  },
69
75
  {
70
76
  "id": "webdav-storage",
77
+ "execution": {
78
+ "group": "remote-storage"
79
+ },
71
80
  "category": "system",
72
81
  "name": "WebDAV Storage",
73
82
  "version": "0.1.1",