@camstack/addon-advanced-notifier 1.1.13 → 1.1.15
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 +387 -133
- package/dist/addon.mjs +388 -134
- package/dist/index.js +20 -12
- package/dist/index.mjs +21 -13
- package/package.json +1 -1
package/dist/addon.js
CHANGED
|
@@ -4632,7 +4632,7 @@ function _instanceof(cls, params = {}) {
|
|
|
4632
4632
|
return inst;
|
|
4633
4633
|
}
|
|
4634
4634
|
//#endregion
|
|
4635
|
-
//#region ../types/dist/sleep-
|
|
4635
|
+
//#region ../types/dist/sleep-MHm--th-.mjs
|
|
4636
4636
|
var EventCategory = /* @__PURE__ */ function(EventCategory) {
|
|
4637
4637
|
EventCategory["SystemBoot"] = "system.boot";
|
|
4638
4638
|
EventCategory["SystemAddonsReady"] = "system.addons-ready";
|
|
@@ -5894,6 +5894,7 @@ var CamStreamKindSchema = _enum([
|
|
|
5894
5894
|
"pull-rtsp",
|
|
5895
5895
|
"pull-rtmp",
|
|
5896
5896
|
"pull-http",
|
|
5897
|
+
"pull-flv",
|
|
5897
5898
|
"pull-rfc4571",
|
|
5898
5899
|
"push-annexb",
|
|
5899
5900
|
"derived"
|
|
@@ -13217,7 +13218,7 @@ var AddBrokerInputSchema = object({
|
|
|
13217
13218
|
});
|
|
13218
13219
|
var AddBrokerResultSchema = object({ id: string() });
|
|
13219
13220
|
var IdInputSchema = object({ id: string() });
|
|
13220
|
-
var TestResultSchema = discriminatedUnion("ok", [object({
|
|
13221
|
+
var TestResultSchema$1 = discriminatedUnion("ok", [object({
|
|
13221
13222
|
ok: literal(true),
|
|
13222
13223
|
latencyMs: number()
|
|
13223
13224
|
}), object({
|
|
@@ -13240,7 +13241,7 @@ var StatusSchema = object({
|
|
|
13240
13241
|
brokerCount: number(),
|
|
13241
13242
|
embeddedRunning: boolean()
|
|
13242
13243
|
});
|
|
13243
|
-
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);
|
|
13244
|
+
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);
|
|
13244
13245
|
var NetworkEndpointSchema = object({
|
|
13245
13246
|
url: string(),
|
|
13246
13247
|
hostname: string(),
|
|
@@ -13274,29 +13275,210 @@ var NetworkEndpointEntrySchema = NetworkEndpointSchema.extend({
|
|
|
13274
13275
|
sourcePort: number().optional()
|
|
13275
13276
|
});
|
|
13276
13277
|
method(_void(), NetworkEndpointSchema, { kind: "mutation" }), method(_void(), _void(), { kind: "mutation" }), method(_void(), NetworkEndpointSchema.nullable()), method(_void(), NetworkAccessStatusSchema), method(_void(), array(NetworkEndpointEntrySchema).readonly());
|
|
13278
|
+
/**
|
|
13279
|
+
* notification-output — canonical, capability-gated notification delivery.
|
|
13280
|
+
*
|
|
13281
|
+
* Apprise-derived model (see
|
|
13282
|
+
* `docs/superpowers/specs/2026-07-03-notification-output-notifier-matrix.md`):
|
|
13283
|
+
* callers emit ONE canonical `Notification`; each provider declares a
|
|
13284
|
+
* per-kind capability descriptor (`TargetKind`), and the pure degrade
|
|
13285
|
+
* engine (`@camstack/types` `prepareNotification`) transcodes / degrades the
|
|
13286
|
+
* message to what the kind supports — callers never special-case a service.
|
|
13287
|
+
*
|
|
13288
|
+
* DESIGN DECISIONS (locked):
|
|
13289
|
+
* - Target CRUD lives on THIS cap (`upsertTarget` / `deleteTarget` /
|
|
13290
|
+
* `setTargetEnabled`), each provider persisting via the `settings-store`
|
|
13291
|
+
* cap. Rationale: the admin UI needs one uniform surface across the
|
|
13292
|
+
* notifiers addon AND the HA addon; the addon-`globalSettingsSchema`-array
|
|
13293
|
+
* alternative would fork the UI per addon and cannot host the
|
|
13294
|
+
* discovery→adopt flow.
|
|
13295
|
+
* - `listTargetKinds` / `listTargets` / `discoverTargets` return arrays →
|
|
13296
|
+
* the generated cap-mount auto-`concatCollection`-fans them across every
|
|
13297
|
+
* registered provider (notifiers addon + HA addon) so one catalog is
|
|
13298
|
+
* routable. `send` / `testTarget` / CRUD route to ONE provider by the
|
|
13299
|
+
* `addonId` the generated collection router extracts from the call input.
|
|
13300
|
+
* - `Attachment.bytes` is `Uint8Array`. Transport-safe: superjson (the tRPC
|
|
13301
|
+
* transformer) + UDS MsgPack both round-trip typed arrays — already used by
|
|
13302
|
+
* `storage` / `storage-provider` / `recording` caps over the same path. No
|
|
13303
|
+
* base64 fallback needed.
|
|
13304
|
+
*
|
|
13305
|
+
* TODO (deferred, closed-set change — separate decision): add
|
|
13306
|
+
* `providerKind: 'notify'` so notification providers surface on the unified
|
|
13307
|
+
* admin "Integrations" page.
|
|
13308
|
+
*/
|
|
13309
|
+
/**
|
|
13310
|
+
* Zentik-derived typed-media enum — the superset across every kind. Each
|
|
13311
|
+
* adapter picks what it supports and the degrade engine filters the rest.
|
|
13312
|
+
*/
|
|
13313
|
+
var AttachmentMediaTypeSchema = _enum([
|
|
13314
|
+
"image",
|
|
13315
|
+
"video",
|
|
13316
|
+
"gif",
|
|
13317
|
+
"audio",
|
|
13318
|
+
"icon"
|
|
13319
|
+
]);
|
|
13320
|
+
/**
|
|
13321
|
+
* A single attachment. Exactly one of `url` (remote source, most adapters
|
|
13322
|
+
* prefer this) or `bytes` (inline source; required for Pushover-style
|
|
13323
|
+
* bytes-only kinds) MUST be present — the degrade engine expresses a
|
|
13324
|
+
* url→bytes fetch as a `needsFetch` directive the adapter executes.
|
|
13325
|
+
*/
|
|
13326
|
+
var AttachmentSchema = object({
|
|
13327
|
+
mediaType: AttachmentMediaTypeSchema,
|
|
13328
|
+
url: string().optional(),
|
|
13329
|
+
bytes: _instanceof(Uint8Array).optional(),
|
|
13330
|
+
mime: string().optional(),
|
|
13331
|
+
name: string().optional()
|
|
13332
|
+
}).refine((a) => a.url !== void 0 || a.bytes !== void 0, { message: "Attachment requires either `url` or `bytes`" });
|
|
13333
|
+
var NotificationFormatSchema = _enum([
|
|
13334
|
+
"text",
|
|
13335
|
+
"markdown",
|
|
13336
|
+
"html"
|
|
13337
|
+
]);
|
|
13338
|
+
/** A single tap-through action button. */
|
|
13339
|
+
var NotificationActionSchema = object({
|
|
13340
|
+
id: string(),
|
|
13341
|
+
label: string(),
|
|
13342
|
+
url: string().optional()
|
|
13343
|
+
});
|
|
13344
|
+
/**
|
|
13345
|
+
* The canonical notification. `body` is the only hard field (Apprise model).
|
|
13346
|
+
* `priority` is a 5-level ORDINAL (1=lowest … 3=normal(default) … 5=urgent),
|
|
13347
|
+
* NOT a fixed severity enum — each kind declares its own `caps.levels` and
|
|
13348
|
+
* the adapter maps this ordinal onto its native level. `level?` is an
|
|
13349
|
+
* optional kind-native level id (`emergency`, `silent`, …) that overrides
|
|
13350
|
+
* `priority` for that one target.
|
|
13351
|
+
*/
|
|
13352
|
+
var NotificationSchema = object({
|
|
13353
|
+
body: string(),
|
|
13354
|
+
title: string().optional(),
|
|
13355
|
+
format: NotificationFormatSchema.default("text"),
|
|
13356
|
+
priority: number().int().min(1).max(5).default(3),
|
|
13357
|
+
level: string().optional(),
|
|
13358
|
+
attachments: array(AttachmentSchema).optional(),
|
|
13359
|
+
clickUrl: string().optional(),
|
|
13360
|
+
actions: array(NotificationActionSchema).optional(),
|
|
13361
|
+
sound: string().optional(),
|
|
13362
|
+
ttl: number().optional(),
|
|
13363
|
+
tag: string().optional(),
|
|
13364
|
+
deviceId: number().optional(),
|
|
13365
|
+
eventId: string().optional(),
|
|
13366
|
+
metadata: record(string(), unknown()).optional()
|
|
13367
|
+
});
|
|
13368
|
+
/** One declared native severity/priority level for a kind. */
|
|
13369
|
+
var TargetKindLevelSchema = object({
|
|
13370
|
+
id: string(),
|
|
13371
|
+
label: string(),
|
|
13372
|
+
/** Which canonical priority (1..5) this level maps to. `null` = qualitative-only. */
|
|
13373
|
+
ordinal: number().int().min(1).max(5).nullable(),
|
|
13374
|
+
flags: object({
|
|
13375
|
+
critical: boolean().optional(),
|
|
13376
|
+
silent: boolean().optional(),
|
|
13377
|
+
noPush: boolean().optional()
|
|
13378
|
+
}).optional(),
|
|
13379
|
+
/** e.g. Pushover `emergency` requires `retry` / `expire`. */
|
|
13380
|
+
requires: array(string()).optional(),
|
|
13381
|
+
description: string().optional()
|
|
13382
|
+
});
|
|
13383
|
+
/** The full capability block consulted before dispatch. */
|
|
13384
|
+
var TargetKindCapsSchema = object({
|
|
13385
|
+
attachments: object({
|
|
13386
|
+
mediaTypes: array(AttachmentMediaTypeSchema),
|
|
13387
|
+
mode: _enum([
|
|
13388
|
+
"url",
|
|
13389
|
+
"bytes",
|
|
13390
|
+
"both"
|
|
13391
|
+
]),
|
|
13392
|
+
max: number().int().nonnegative(),
|
|
13393
|
+
maxBytes: number().int().positive().optional()
|
|
13394
|
+
}),
|
|
13395
|
+
/** Max action buttons (0 = none). */
|
|
13396
|
+
actions: number().int().nonnegative(),
|
|
13397
|
+
levels: array(TargetKindLevelSchema),
|
|
13398
|
+
format: array(NotificationFormatSchema),
|
|
13399
|
+
clickUrl: boolean(),
|
|
13400
|
+
sound: boolean(),
|
|
13401
|
+
ttl: boolean(),
|
|
13402
|
+
bodyMaxLen: number().int().positive()
|
|
13403
|
+
});
|
|
13404
|
+
/**
|
|
13405
|
+
* `configSchema` is a `ConfigUISchema` tree passed through to the admin
|
|
13406
|
+
* FormBuilder. Stored as `z.unknown()` at the cap seam (mirrors
|
|
13407
|
+
* `device-provider.getChildCreationSchema` `CreationSchemaOutputSchema`) —
|
|
13408
|
+
* the union is large and not meant for runtime validation here; the exported
|
|
13409
|
+
* `TargetKind` type re-tightens `configSchema` to `ConfigUISchema`.
|
|
13410
|
+
*/
|
|
13411
|
+
var ConfigSchemaPassthrough = unknown();
|
|
13412
|
+
var TargetKindSchema = object({
|
|
13413
|
+
kind: string(),
|
|
13414
|
+
label: string(),
|
|
13415
|
+
icon: string(),
|
|
13416
|
+
/** Stamped by each provider so the concat-fanned catalog stays routable. */
|
|
13417
|
+
addonId: string(),
|
|
13418
|
+
configSchema: ConfigSchemaPassthrough,
|
|
13419
|
+
supportsDiscovery: boolean(),
|
|
13420
|
+
caps: TargetKindCapsSchema
|
|
13421
|
+
});
|
|
13422
|
+
/**
|
|
13423
|
+
* A persisted target. `config` holds secrets; providers REDACT secret fields
|
|
13424
|
+
* (return a presence marker only) when serving `listTargets` — never
|
|
13425
|
+
* round-trip a stored secret to the UI.
|
|
13426
|
+
*/
|
|
13427
|
+
var TargetSchema = object({
|
|
13428
|
+
id: string(),
|
|
13429
|
+
name: string(),
|
|
13430
|
+
kind: string(),
|
|
13431
|
+
addonId: string(),
|
|
13432
|
+
enabled: boolean(),
|
|
13433
|
+
config: record(string(), unknown())
|
|
13434
|
+
});
|
|
13435
|
+
/** A discovery-surfaced candidate (config is partial + non-secret). */
|
|
13436
|
+
var DiscoveredTargetSchema = object({
|
|
13437
|
+
kind: string(),
|
|
13438
|
+
suggestedName: string(),
|
|
13439
|
+
config: record(string(), unknown())
|
|
13440
|
+
});
|
|
13441
|
+
/** The degrade engine's report — what was resolved / dropped / degraded. */
|
|
13442
|
+
var RenderedAsSchema = object({
|
|
13443
|
+
level: string(),
|
|
13444
|
+
format: NotificationFormatSchema,
|
|
13445
|
+
attachmentsSent: number().int().nonnegative(),
|
|
13446
|
+
actionsSent: number().int().nonnegative(),
|
|
13447
|
+
truncated: boolean(),
|
|
13448
|
+
dropped: array(string())
|
|
13449
|
+
});
|
|
13450
|
+
var SendResultSchema = object({
|
|
13451
|
+
success: boolean(),
|
|
13452
|
+
error: string().optional(),
|
|
13453
|
+
renderedAs: RenderedAsSchema.optional()
|
|
13454
|
+
});
|
|
13455
|
+
/** Same shape as SendResult — kept as a distinct name for the test panel. */
|
|
13456
|
+
var TestResultSchema = SendResultSchema;
|
|
13277
13457
|
var notificationOutputCapability = {
|
|
13278
13458
|
name: "notification-output",
|
|
13279
13459
|
scope: "system",
|
|
13280
13460
|
mode: "collection",
|
|
13281
13461
|
methods: {
|
|
13462
|
+
listTargetKinds: method(object({}), array(TargetKindSchema)),
|
|
13463
|
+
listTargets: method(object({}), array(TargetSchema)),
|
|
13464
|
+
discoverTargets: method(object({
|
|
13465
|
+
kind: string(),
|
|
13466
|
+
config: record(string(), unknown()).optional()
|
|
13467
|
+
}), array(DiscoveredTargetSchema)),
|
|
13282
13468
|
send: method(object({
|
|
13283
|
-
|
|
13284
|
-
|
|
13285
|
-
|
|
13286
|
-
|
|
13287
|
-
|
|
13288
|
-
|
|
13289
|
-
|
|
13290
|
-
|
|
13291
|
-
|
|
13292
|
-
|
|
13293
|
-
|
|
13294
|
-
|
|
13295
|
-
}), _void(), { kind: "mutation" })
|
|
13296
|
-
sendTest: method(_void(), object({
|
|
13297
|
-
success: boolean(),
|
|
13298
|
-
error: string().optional()
|
|
13299
|
-
}), { kind: "mutation" })
|
|
13469
|
+
targetId: string(),
|
|
13470
|
+
notification: NotificationSchema
|
|
13471
|
+
}), SendResultSchema, { kind: "mutation" }),
|
|
13472
|
+
testTarget: method(object({
|
|
13473
|
+
targetId: string(),
|
|
13474
|
+
sample: NotificationSchema.optional()
|
|
13475
|
+
}), TestResultSchema, { kind: "mutation" }),
|
|
13476
|
+
upsertTarget: method(object({ target: TargetSchema }), TargetSchema, { kind: "mutation" }),
|
|
13477
|
+
deleteTarget: method(object({ targetId: string() }), _void(), { kind: "mutation" }),
|
|
13478
|
+
setTargetEnabled: method(object({
|
|
13479
|
+
targetId: string(),
|
|
13480
|
+
enabled: boolean()
|
|
13481
|
+
}), _void(), { kind: "mutation" })
|
|
13300
13482
|
}
|
|
13301
13483
|
};
|
|
13302
13484
|
/**
|
|
@@ -19293,13 +19475,49 @@ Object.freeze({
|
|
|
19293
19475
|
addonId: null,
|
|
19294
19476
|
access: "create"
|
|
19295
19477
|
},
|
|
19478
|
+
"notificationOutput.deleteTarget": {
|
|
19479
|
+
capName: "notification-output",
|
|
19480
|
+
capScope: "system",
|
|
19481
|
+
addonId: null,
|
|
19482
|
+
access: "delete"
|
|
19483
|
+
},
|
|
19484
|
+
"notificationOutput.discoverTargets": {
|
|
19485
|
+
capName: "notification-output",
|
|
19486
|
+
capScope: "system",
|
|
19487
|
+
addonId: null,
|
|
19488
|
+
access: "view"
|
|
19489
|
+
},
|
|
19490
|
+
"notificationOutput.listTargetKinds": {
|
|
19491
|
+
capName: "notification-output",
|
|
19492
|
+
capScope: "system",
|
|
19493
|
+
addonId: null,
|
|
19494
|
+
access: "view"
|
|
19495
|
+
},
|
|
19496
|
+
"notificationOutput.listTargets": {
|
|
19497
|
+
capName: "notification-output",
|
|
19498
|
+
capScope: "system",
|
|
19499
|
+
addonId: null,
|
|
19500
|
+
access: "view"
|
|
19501
|
+
},
|
|
19296
19502
|
"notificationOutput.send": {
|
|
19297
19503
|
capName: "notification-output",
|
|
19298
19504
|
capScope: "system",
|
|
19299
19505
|
addonId: null,
|
|
19300
19506
|
access: "create"
|
|
19301
19507
|
},
|
|
19302
|
-
"notificationOutput.
|
|
19508
|
+
"notificationOutput.setTargetEnabled": {
|
|
19509
|
+
capName: "notification-output",
|
|
19510
|
+
capScope: "system",
|
|
19511
|
+
addonId: null,
|
|
19512
|
+
access: "create"
|
|
19513
|
+
},
|
|
19514
|
+
"notificationOutput.testTarget": {
|
|
19515
|
+
capName: "notification-output",
|
|
19516
|
+
capScope: "system",
|
|
19517
|
+
addonId: null,
|
|
19518
|
+
access: "create"
|
|
19519
|
+
},
|
|
19520
|
+
"notificationOutput.upsertTarget": {
|
|
19303
19521
|
capName: "notification-output",
|
|
19304
19522
|
capScope: "system",
|
|
19305
19523
|
addonId: null,
|
|
@@ -21285,42 +21503,120 @@ object({
|
|
|
21285
21503
|
schemaVersion: literal(1)
|
|
21286
21504
|
});
|
|
21287
21505
|
//#endregion
|
|
21288
|
-
//#region src/
|
|
21506
|
+
//#region src/history/audit-log.ts
|
|
21289
21507
|
var COLLECTION$1 = "addon-settings";
|
|
21290
|
-
var
|
|
21291
|
-
var
|
|
21508
|
+
var LOG_KEY = "notifier-history:log";
|
|
21509
|
+
var AuditLog = class {
|
|
21292
21510
|
api;
|
|
21293
|
-
|
|
21511
|
+
maxEntries;
|
|
21512
|
+
constructor(api, maxEntries = 1e3) {
|
|
21294
21513
|
this.api = api;
|
|
21514
|
+
this.maxEntries = maxEntries;
|
|
21295
21515
|
}
|
|
21296
|
-
async
|
|
21516
|
+
async record(entry) {
|
|
21517
|
+
const updated = [...await this.getAll(), entry];
|
|
21518
|
+
const trimmed = updated.length > this.maxEntries ? updated.slice(updated.length - this.maxEntries) : updated;
|
|
21519
|
+
await this.api.settingsStore.set.mutate({
|
|
21520
|
+
collection: COLLECTION$1,
|
|
21521
|
+
key: LOG_KEY,
|
|
21522
|
+
value: trimmed
|
|
21523
|
+
});
|
|
21524
|
+
}
|
|
21525
|
+
async getHistory(filter) {
|
|
21526
|
+
let entries = await this.getAll();
|
|
21527
|
+
if (filter?.ruleId) entries = entries.filter((e) => e.ruleId === filter.ruleId);
|
|
21528
|
+
if (filter?.deviceId) {
|
|
21529
|
+
const deviceId = filter.deviceId;
|
|
21530
|
+
entries = entries.filter((e) => e.deviceId === deviceId);
|
|
21531
|
+
}
|
|
21532
|
+
if (filter?.from !== void 0) {
|
|
21533
|
+
const from = filter.from;
|
|
21534
|
+
entries = entries.filter((e) => e.timestamp >= from);
|
|
21535
|
+
}
|
|
21536
|
+
if (filter?.to !== void 0) {
|
|
21537
|
+
const to = filter.to;
|
|
21538
|
+
entries = entries.filter((e) => e.timestamp <= to);
|
|
21539
|
+
}
|
|
21540
|
+
if (filter?.limit) entries = entries.slice(-filter.limit);
|
|
21541
|
+
return entries;
|
|
21542
|
+
}
|
|
21543
|
+
async getAll() {
|
|
21297
21544
|
const raw = await this.api.settingsStore.get.query({
|
|
21298
21545
|
collection: COLLECTION$1,
|
|
21299
|
-
key:
|
|
21546
|
+
key: LOG_KEY
|
|
21300
21547
|
});
|
|
21301
21548
|
if (!raw || !Array.isArray(raw)) return [];
|
|
21302
21549
|
return raw;
|
|
21303
21550
|
}
|
|
21304
|
-
|
|
21305
|
-
|
|
21306
|
-
|
|
21307
|
-
|
|
21308
|
-
|
|
21551
|
+
};
|
|
21552
|
+
//#endregion
|
|
21553
|
+
//#region src/outputs/console.ts
|
|
21554
|
+
var ConsoleOutput = class {
|
|
21555
|
+
id = "console";
|
|
21556
|
+
name = "Console Log";
|
|
21557
|
+
icon = "terminal";
|
|
21558
|
+
logger;
|
|
21559
|
+
constructor(logger) {
|
|
21560
|
+
this.logger = logger;
|
|
21561
|
+
}
|
|
21562
|
+
async send(notification) {
|
|
21563
|
+
this.logger.info(`[p${notification.priority}] ${notification.title ?? ""}: ${notification.body}`, notification.deviceId !== void 0 ? { tags: { deviceId: notification.deviceId } } : void 0);
|
|
21564
|
+
}
|
|
21565
|
+
async sendTest() {
|
|
21566
|
+
await this.send({
|
|
21567
|
+
title: "Test",
|
|
21568
|
+
body: "Console output working",
|
|
21569
|
+
format: "text",
|
|
21570
|
+
priority: 3
|
|
21309
21571
|
});
|
|
21572
|
+
return { success: true };
|
|
21310
21573
|
}
|
|
21311
|
-
|
|
21312
|
-
|
|
21313
|
-
|
|
21314
|
-
|
|
21315
|
-
|
|
21316
|
-
|
|
21574
|
+
};
|
|
21575
|
+
//#endregion
|
|
21576
|
+
//#region src/rules/cooldown.ts
|
|
21577
|
+
var CooldownTracker = class {
|
|
21578
|
+
lastFired = /* @__PURE__ */ new Map();
|
|
21579
|
+
canFire(ruleId, cooldownSeconds) {
|
|
21580
|
+
const last = this.lastFired.get(ruleId);
|
|
21581
|
+
if (last === void 0) return true;
|
|
21582
|
+
return (Date.now() - last) / 1e3 >= cooldownSeconds;
|
|
21317
21583
|
}
|
|
21318
|
-
|
|
21319
|
-
|
|
21320
|
-
|
|
21584
|
+
recordFire(ruleId) {
|
|
21585
|
+
this.lastFired.set(ruleId, Date.now());
|
|
21586
|
+
}
|
|
21587
|
+
reset() {
|
|
21588
|
+
this.lastFired.clear();
|
|
21321
21589
|
}
|
|
21322
21590
|
};
|
|
21323
21591
|
//#endregion
|
|
21592
|
+
//#region src/rules/recent-events.ts
|
|
21593
|
+
/**
|
|
21594
|
+
* Rehydrate serialized recent events into `SystemEvent`s and drop any that
|
|
21595
|
+
* fall before `since`. Order is preserved (the hub returns oldest → newest).
|
|
21596
|
+
*/
|
|
21597
|
+
function reconstructRecentEvents(serialized, since) {
|
|
21598
|
+
const sinceMs = since.getTime();
|
|
21599
|
+
const events = [];
|
|
21600
|
+
for (const e of serialized) {
|
|
21601
|
+
const timestamp = new Date(e.timestamp);
|
|
21602
|
+
if (timestamp.getTime() < sinceMs) continue;
|
|
21603
|
+
events.push({
|
|
21604
|
+
id: e.id,
|
|
21605
|
+
timestamp,
|
|
21606
|
+
source: {
|
|
21607
|
+
type: e.source.type,
|
|
21608
|
+
id: e.source.id,
|
|
21609
|
+
...e.source.nodeId !== void 0 ? { nodeId: e.source.nodeId } : {},
|
|
21610
|
+
...e.source.addonId !== void 0 ? { addonId: e.source.addonId } : {},
|
|
21611
|
+
...e.source.deviceId !== void 0 ? { deviceId: e.source.deviceId } : {}
|
|
21612
|
+
},
|
|
21613
|
+
category: e.category,
|
|
21614
|
+
data: e.data
|
|
21615
|
+
});
|
|
21616
|
+
}
|
|
21617
|
+
return events;
|
|
21618
|
+
}
|
|
21619
|
+
//#endregion
|
|
21324
21620
|
//#region src/rules/rule-engine.ts
|
|
21325
21621
|
var RuleEngine = class {
|
|
21326
21622
|
clip;
|
|
@@ -21393,19 +21689,39 @@ var RuleEngine = class {
|
|
|
21393
21689
|
}
|
|
21394
21690
|
};
|
|
21395
21691
|
//#endregion
|
|
21396
|
-
//#region src/rules/
|
|
21397
|
-
var
|
|
21398
|
-
|
|
21399
|
-
|
|
21400
|
-
|
|
21401
|
-
|
|
21402
|
-
|
|
21692
|
+
//#region src/rules/rule-store.ts
|
|
21693
|
+
var COLLECTION = "addon-settings";
|
|
21694
|
+
var KEY = "advanced-notifier:rules";
|
|
21695
|
+
var RuleStore = class {
|
|
21696
|
+
api;
|
|
21697
|
+
constructor(api) {
|
|
21698
|
+
this.api = api;
|
|
21403
21699
|
}
|
|
21404
|
-
|
|
21405
|
-
this.
|
|
21700
|
+
async getRules() {
|
|
21701
|
+
const raw = await this.api.settingsStore.get.query({
|
|
21702
|
+
collection: COLLECTION,
|
|
21703
|
+
key: KEY
|
|
21704
|
+
});
|
|
21705
|
+
if (!raw || !Array.isArray(raw)) return [];
|
|
21706
|
+
return raw;
|
|
21406
21707
|
}
|
|
21407
|
-
|
|
21408
|
-
this.
|
|
21708
|
+
async saveRules(rules) {
|
|
21709
|
+
await this.api.settingsStore.set.mutate({
|
|
21710
|
+
collection: COLLECTION,
|
|
21711
|
+
key: KEY,
|
|
21712
|
+
value: [...rules]
|
|
21713
|
+
});
|
|
21714
|
+
}
|
|
21715
|
+
async upsertRule(rule) {
|
|
21716
|
+
const rules = await this.getRules();
|
|
21717
|
+
const idx = rules.findIndex((r) => r.id === rule.id);
|
|
21718
|
+
if (idx >= 0) rules[idx] = rule;
|
|
21719
|
+
else rules.push(rule);
|
|
21720
|
+
await this.saveRules(rules);
|
|
21721
|
+
}
|
|
21722
|
+
async deleteRule(ruleId) {
|
|
21723
|
+
const rules = await this.getRules();
|
|
21724
|
+
await this.saveRules(rules.filter((r) => r.id !== ruleId));
|
|
21409
21725
|
}
|
|
21410
21726
|
};
|
|
21411
21727
|
//#endregion
|
|
@@ -21417,77 +21733,6 @@ function renderTemplate(template, variables) {
|
|
|
21417
21733
|
});
|
|
21418
21734
|
}
|
|
21419
21735
|
//#endregion
|
|
21420
|
-
//#region src/outputs/console.ts
|
|
21421
|
-
var ConsoleOutput = class {
|
|
21422
|
-
id = "console";
|
|
21423
|
-
name = "Console Log";
|
|
21424
|
-
icon = "terminal";
|
|
21425
|
-
logger;
|
|
21426
|
-
constructor(logger) {
|
|
21427
|
-
this.logger = logger;
|
|
21428
|
-
}
|
|
21429
|
-
async send(notification) {
|
|
21430
|
-
this.logger.info(`[${notification.severity}] ${notification.title}: ${notification.message}`, notification.deviceId !== void 0 ? { tags: { deviceId: notification.deviceId } } : void 0);
|
|
21431
|
-
}
|
|
21432
|
-
async sendTest() {
|
|
21433
|
-
await this.send({
|
|
21434
|
-
title: "Test",
|
|
21435
|
-
message: "Console output working",
|
|
21436
|
-
severity: "info",
|
|
21437
|
-
category: "test",
|
|
21438
|
-
timestamp: Date.now()
|
|
21439
|
-
});
|
|
21440
|
-
return { success: true };
|
|
21441
|
-
}
|
|
21442
|
-
};
|
|
21443
|
-
//#endregion
|
|
21444
|
-
//#region src/history/audit-log.ts
|
|
21445
|
-
var COLLECTION = "addon-settings";
|
|
21446
|
-
var LOG_KEY = "notifier-history:log";
|
|
21447
|
-
var AuditLog = class {
|
|
21448
|
-
api;
|
|
21449
|
-
maxEntries;
|
|
21450
|
-
constructor(api, maxEntries = 1e3) {
|
|
21451
|
-
this.api = api;
|
|
21452
|
-
this.maxEntries = maxEntries;
|
|
21453
|
-
}
|
|
21454
|
-
async record(entry) {
|
|
21455
|
-
const updated = [...await this.getAll(), entry];
|
|
21456
|
-
const trimmed = updated.length > this.maxEntries ? updated.slice(updated.length - this.maxEntries) : updated;
|
|
21457
|
-
await this.api.settingsStore.set.mutate({
|
|
21458
|
-
collection: COLLECTION,
|
|
21459
|
-
key: LOG_KEY,
|
|
21460
|
-
value: trimmed
|
|
21461
|
-
});
|
|
21462
|
-
}
|
|
21463
|
-
async getHistory(filter) {
|
|
21464
|
-
let entries = await this.getAll();
|
|
21465
|
-
if (filter?.ruleId) entries = entries.filter((e) => e.ruleId === filter.ruleId);
|
|
21466
|
-
if (filter?.deviceId) {
|
|
21467
|
-
const deviceId = filter.deviceId;
|
|
21468
|
-
entries = entries.filter((e) => e.deviceId === deviceId);
|
|
21469
|
-
}
|
|
21470
|
-
if (filter?.from !== void 0) {
|
|
21471
|
-
const from = filter.from;
|
|
21472
|
-
entries = entries.filter((e) => e.timestamp >= from);
|
|
21473
|
-
}
|
|
21474
|
-
if (filter?.to !== void 0) {
|
|
21475
|
-
const to = filter.to;
|
|
21476
|
-
entries = entries.filter((e) => e.timestamp <= to);
|
|
21477
|
-
}
|
|
21478
|
-
if (filter?.limit) entries = entries.slice(-filter.limit);
|
|
21479
|
-
return entries;
|
|
21480
|
-
}
|
|
21481
|
-
async getAll() {
|
|
21482
|
-
const raw = await this.api.settingsStore.get.query({
|
|
21483
|
-
collection: COLLECTION,
|
|
21484
|
-
key: LOG_KEY
|
|
21485
|
-
});
|
|
21486
|
-
if (!raw || !Array.isArray(raw)) return [];
|
|
21487
|
-
return raw;
|
|
21488
|
-
}
|
|
21489
|
-
};
|
|
21490
|
-
//#endregion
|
|
21491
21736
|
//#region src/addon.ts
|
|
21492
21737
|
var EVENT_CATEGORIES = [
|
|
21493
21738
|
EventCategory.DetectionRaw,
|
|
@@ -21539,7 +21784,9 @@ var AdvancedNotifierAddon = class extends BaseAddon {
|
|
|
21539
21784
|
const rule = this.cachedRules.find((r) => r.id === ruleId);
|
|
21540
21785
|
if (!rule) return { results: [] };
|
|
21541
21786
|
const since = /* @__PURE__ */ new Date(Date.now() - lookbackMinutes * 60 * 1e3);
|
|
21542
|
-
|
|
21787
|
+
const api = this.ctx.api;
|
|
21788
|
+
if (!api) return { results: [] };
|
|
21789
|
+
return { results: reconstructRecentEvents(await api.eventBusProxy.getRecent.query({ limit: 1e3 }), since).map((event) => {
|
|
21543
21790
|
const matched = this.ruleEngine.evaluate(event, [rule]);
|
|
21544
21791
|
return {
|
|
21545
21792
|
ruleId,
|
|
@@ -21703,12 +21950,11 @@ var AdvancedNotifierAddon = class extends BaseAddon {
|
|
|
21703
21950
|
const deviceId = Number.isFinite(deviceIdNum) ? deviceIdNum : void 0;
|
|
21704
21951
|
const notification = {
|
|
21705
21952
|
title,
|
|
21706
|
-
message,
|
|
21707
|
-
|
|
21708
|
-
|
|
21953
|
+
body: message,
|
|
21954
|
+
format: "text",
|
|
21955
|
+
priority: this.priorityToOrdinal(rule.priority),
|
|
21709
21956
|
deviceId,
|
|
21710
|
-
|
|
21711
|
-
timestamp: event.timestamp.getTime()
|
|
21957
|
+
metadata: event.data
|
|
21712
21958
|
};
|
|
21713
21959
|
let success = true;
|
|
21714
21960
|
let dispatchError;
|
|
@@ -21753,10 +21999,18 @@ var AdvancedNotifierAddon = class extends BaseAddon {
|
|
|
21753
21999
|
timestamp: event.timestamp.toISOString()
|
|
21754
22000
|
};
|
|
21755
22001
|
}
|
|
21756
|
-
|
|
21757
|
-
|
|
21758
|
-
|
|
21759
|
-
|
|
22002
|
+
/**
|
|
22003
|
+
* Map a rule's qualitative priority onto the canonical 1..5 ordinal.
|
|
22004
|
+
* Callers now pass ordinal priority; each `notification-output` provider
|
|
22005
|
+
* maps the ordinal onto its declared native level.
|
|
22006
|
+
*/
|
|
22007
|
+
priorityToOrdinal(priority) {
|
|
22008
|
+
switch (priority) {
|
|
22009
|
+
case "critical": return 5;
|
|
22010
|
+
case "high": return 4;
|
|
22011
|
+
case "low": return 2;
|
|
22012
|
+
default: return 3;
|
|
22013
|
+
}
|
|
21760
22014
|
}
|
|
21761
22015
|
};
|
|
21762
22016
|
//#endregion
|