@calebcall/camera-ui-notify 0.4.0

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/CHANGELOG.md ADDED
@@ -0,0 +1,106 @@
1
+ # Changelog
2
+
3
+ All notable changes to **Notify** are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [0.4.0] - 2026-07-25
9
+
10
+ ### Added
11
+
12
+ - **Pushover backend** — hosted push to the Pushover app (app token + user key); sends the snapshot
13
+ as an attachment and maps severity to Pushover priority (Info=0, higher=1).
14
+ - **Telegram backend** — delivery to a chat via a bot (bot token + chat ID); `sendPhoto` with the
15
+ snapshot when present, else `sendMessage`, with an inline "Open camera" button for the deep link.
16
+ - **Discord backend** — delivery via a channel webhook; a severity-colored embed with the snapshot
17
+ attached and the title linked to the deep link.
18
+
19
+ ### Security
20
+
21
+ - **Redact secrets from transport errors.** Telegram's bot token (in the request URL path), a
22
+ Discord webhook URL (itself a credential), and any secret embedded in a generic webhook URL are
23
+ no longer leaked into logs when a delivery request fails — request URLs are stripped from
24
+ `*url.Error` before wrapping.
25
+
26
+ ## [0.3.0] - 2026-07-25
27
+
28
+ ### Added
29
+
30
+ - **Inline thumbnail image (ntfy).** When a notification carries inline thumbnail bytes (e.g. the
31
+ NVR's detection snapshot) and no image URL, the ntfy backend uploads it as a file attachment so
32
+ the image renders in the notification.
33
+ - **Absolute deep links.** A new optional plugin config field, **camera.ui Base URL**, turns the
34
+ publisher's relative deep link (e.g. `/cameras/<name>?startTs=…`) into an absolute URL so ntfy's
35
+ tap-through (`Click`) works. Empty base URL leaves the link untouched.
36
+ - **Webhook thumbnail.** The generic webhook backend now includes the thumbnail as `thumbnailBase64`
37
+ in its JSON payload when present.
38
+
39
+ ### Fixed
40
+
41
+ - **Gotify visibility.** Severity now maps to Gotify priority 4–10 (Info=4 … Critical=10). Gotify
42
+ treats priority 0–3 as silent (no system notification), so the previous 0–10 mapping made
43
+ Info-severity notifications appear to deliver nothing.
44
+
45
+ ### Notes
46
+
47
+ - Gotify cannot display an inline thumbnail image (it requires a hosted image URL, not raw bytes);
48
+ images are delivered on ntfy only.
49
+
50
+ ## [0.2.0] - 2026-07-24
51
+
52
+ ### Changed
53
+
54
+ - **Config-driven single target replaces device registration.** The stock camera.ui UI has no
55
+ way to *create* a notifier device — `registerDevice` is only ever called by the mobile app's
56
+ push-registration flow, hardcoded to the official NVR's plugin name, and the generic
57
+ notification settings panel renders read-only. The plugin's own settings page, however, does
58
+ render an editable, savable form from any plugin's `StorageSchema`. Notify now holds exactly
59
+ one target — a selected service (`ntfy`/`gotify`/`webhook`) plus that service's fields — in its
60
+ own persisted config, set from the plugin's settings page in camera.ui rather than through a
61
+ device-add flow.
62
+ - **`getDevices` synthesizes the device from config.** Instead of reading a stored device list, it
63
+ reads the configured service and fields on every call, validates them (`ParseTarget`), and
64
+ returns a single `sdk.NotifierDevice` (or none, if unconfigured/incomplete). `getDevice` mirrors
65
+ this for the one synthesized id (`cfg:<service>`).
66
+ - **`registerDevice` / `updateDevice` / `revokeDevice` are now no-ops** (the first returns an
67
+ error directing users to the plugin's settings page) — targets are configured, not registered.
68
+ These remain only to satisfy the `Notifier` interface.
69
+ - **`notificationSettings()` returns nothing.** Target configuration now lives on the plugin's
70
+ config tab; duplicating it in the notification "send test" panel would only confuse.
71
+
72
+ ### Removed
73
+
74
+ - The device store (`src/store.go`) and its persisted `devices` key — replaced by the
75
+ `StorageSchema`-based config described above.
76
+
77
+ Backends (ntfy, Gotify, generic webhook), their config fields, delivery format, and severity
78
+ mapping are unchanged from 0.1.0.
79
+
80
+ ## [0.1.0] - 2026-07-24
81
+
82
+ Initial release — a fully-local, multi-backend camera.ui `Notifier` plugin.
83
+
84
+ ### Added
85
+
86
+ - **Notifier plugin** — implements the full `Notifier` RPC surface (`getDevices`, `getDevice`,
87
+ `registerDevice`, `updateDevice`, `revokeDevice`, `sendNotification`, `notificationSettings`)
88
+ against a `Notifier`-only contract (`role: Hub`, `interfaces: [Notifier]`).
89
+ - **Pluggable-backend registry** — a `Backend` interface (id, label, config schema, target
90
+ validation, delivery) with a package-level registry; each backend self-registers from its own
91
+ `init()`, so adding a new backend later is one new file and a version bump.
92
+ - **Per-device backend selection** — each registered device is bound to one backend and holds
93
+ that backend's validated config; the registration form is built dynamically from the union of
94
+ every registered backend's schema, condition-gated on the selected service.
95
+ - **ntfy backend** — publishes to [ntfy.sh](https://ntfy.sh) or a self-hosted server (`server`,
96
+ `topic`, optional `token`), mapping severity to ntfy's 1–5 priority scale.
97
+ - **Gotify backend** — publishes to a self-hosted [Gotify](https://gotify.net) server (`server`,
98
+ `token`), mapping severity to Gotify's 0–10 priority scale.
99
+ - **Generic webhook backend** — delivers a JSON payload of the full notification to any HTTP
100
+ endpoint (`url`, `method` [`POST`/`PUT`], optional custom header) for services without a
101
+ dedicated backend.
102
+ - **Device persistence** — devices are stored as JSON under the plugin's own `DeviceStorage`,
103
+ requiring no external database.
104
+
105
+ [0.2.0]: https://github.com/calebcall/camera-ui-notify/releases/tag/v0.2.0
106
+ [0.1.0]: https://github.com/calebcall/camera-ui-notify/releases/tag/v0.1.0
package/LICENSE.md ADDED
@@ -0,0 +1,22 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 calebcall
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,295 @@
1
+ <h1 align="center">Notify</h1>
2
+
3
+ <p align="center">
4
+ A fully-local, multi-backend <a href="https://github.com/seydx/camera.ui">camera.ui</a> notifier
5
+ plugin — delivers notifications to a service you control (<a href="https://ntfy.sh">ntfy</a>,
6
+ <a href="https://gotify.net">Gotify</a>, Pushover, Telegram, Discord, or a generic webhook),
7
+ entirely on your own hardware, with no cloud dependency and no license check.
8
+ </p>
9
+
10
+ ---
11
+
12
+ ## Why this exists
13
+
14
+ camera.ui separates two notification roles:
15
+
16
+ - **Publishers** (capability `PublishNotifications`) emit events. Our open-source NVR plugin
17
+ (`@calebcall/camera-ui-nvr-local`) is a publisher — it calls `NotificationManager.Publish` for
18
+ object-detection events.
19
+ - **Notifiers** (interface `Notifier`) own delivery *devices* and actually deliver notifications.
20
+ The host's `NotificationManager.notify()` fans every notification out to **all** running plugins
21
+ implementing `Notifier`.
22
+
23
+ There is intentionally no notifier in the open ecosystem: the closed, official NVR bundled a
24
+ notifier that pushed to the camera.ui mobile app through camera.ui's proprietary FCM/APNs cloud
25
+ relay — a license-gated path we neither have credentials for nor can replicate. This plugin takes
26
+ the only fully-local road to real background push instead: delivering to a push/webhook service
27
+ *you* run or control.
28
+
29
+ This plugin is a pure **notifier**. It is decoupled from any single publisher — it delivers
30
+ notifications from **any** publisher, including our NVR plugin's detection events and camera.ui's
31
+ own system notifications, not just one plugin's output.
32
+
33
+ ## How it works
34
+
35
+ One plugin, contract `interfaces: [Notifier]`, built around a pluggable-backend (strategy) pattern:
36
+
37
+ - A `Backend` interface abstracts one delivery service (id, label, config schema, target
38
+ validation, and delivery).
39
+ - A package-level registry maps each backend's stable id to its implementation; every backend
40
+ self-registers from its own `init()`.
41
+ - The plugin holds **one active target** in its own persisted config (`StorageSchema`): which
42
+ service is selected, plus that service's validated fields. `getDevices` synthesizes a single
43
+ `sdk.NotifierDevice` from this config on every call — there is no device registry.
44
+ - `sendNotification` dispatches the synthesized device to its backend's `Send`. A backend failure
45
+ is logged and returned to the host.
46
+
47
+ ### Why config, not device registration
48
+
49
+ Earlier versions of this plugin modeled targets as registrable `NotifierDevice`s, the way
50
+ camera.ui's own mobile-push notifier does. That model turned out to be unreachable from the stock
51
+ UI: `registerDevice` is only ever called by the camera.ui mobile app's push-registration flow,
52
+ which is hardcoded to the official NVR's plugin name. The generic notification settings panel
53
+ renders `notificationSettings()` read-only — it has no "add a device" affordance for third-party
54
+ plugins. The one part of the stock UI that **does** render an editable, savable form for a
55
+ third-party plugin is its own settings page, which renders whatever `StorageSchema` the plugin
56
+ declares. So Notify now models its target as plugin config instead of a device: you configure it
57
+ once, in the plugin's own settings, and `getDevices` synthesizes the device the host's
58
+ `Notifier` interface expects from that config.
59
+
60
+ Adding a new backend later is **one new file** — `src/backend/<name>.go` implementing `Backend`
61
+ plus `Register(...)` in its `init()` — and a version bump. No new plugin, no core change, no
62
+ changes to `plugin.go`.
63
+
64
+ ## Backends (v1)
65
+
66
+ Severity is mapped consistently across backends via `backend.PriorityScale`, which spreads
67
+ camera.ui's four severity levels (`info` → `warn` → `error` → `critical`) evenly across each
68
+ backend's native priority range, `info` at the low end and `critical` at the high end.
69
+
70
+ ### ntfy
71
+
72
+ Publishes to [ntfy.sh](https://ntfy.sh) or a self-hosted ntfy server.
73
+
74
+ | Field | Required | Default | Notes |
75
+ | -------- | -------- | --------------------- | --------------------------------------------------- |
76
+ | `server` | no | `https://ntfy.sh` | Base URL of the ntfy server. Trailing `/` trimmed. |
77
+ | `topic` | yes | — | The ntfy topic to publish to. |
78
+ | `token` | no | — | Access token for a protected/self-hosted topic, sent as `Authorization: Bearer <token>`. |
79
+
80
+ Delivery: `POST {server}/{topic}` with the notification body as the request body, plus `Title`,
81
+ `Priority` (1–5, from severity), `Click` (deep link, if set), and `Attach`/`Icon` (image URL, if
82
+ set) headers.
83
+
84
+ ### Gotify
85
+
86
+ Publishes to a self-hosted [Gotify](https://gotify.net) server.
87
+
88
+ | Field | Required | Notes |
89
+ | -------- | -------- | ----------------------------------------------- |
90
+ | `server` | yes | Base URL of the Gotify server. Trailing `/` trimmed. |
91
+ | `token` | yes | Gotify application token, used to authenticate published messages. |
92
+
93
+ Delivery: `POST {server}/message?token={token}` with a JSON body `{title, message, priority}`
94
+ (priority 0–10, from severity), plus a `client::notification.click` extra for the deep link and a
95
+ `bigImageUrl` extra when an image URL is set.
96
+
97
+ ### Generic webhook
98
+
99
+ Delivers to any HTTP endpoint you provide — the fallback for anything without a dedicated backend.
100
+
101
+ | Field | Required | Default | Notes |
102
+ | ------------- | -------- | ------- | -------------------------------------------------------------- |
103
+ | `url` | yes | — | Endpoint that receives the notification. |
104
+ | `method` | no | `POST` | `POST` or `PUT`. |
105
+ | `headerName` | no | — | Optional custom header name (e.g. for a shared secret). Requires `headerValue` if set. |
106
+ | `headerValue` | no | — | Value of the custom header. Requires `headerName` if set. |
107
+
108
+ Delivery: `{method} {url}` with `Content-Type: application/json` and (if configured) the custom
109
+ header, carrying a JSON body of `{title, subtitle, body, severity, tag, imageUrl, deepLink, data,
110
+ createdAt, thumbnailBase64}`.
111
+
112
+ ### Pushover
113
+
114
+ Hosted push to the [Pushover](https://pushover.net) app.
115
+
116
+ | Field | Required | Notes |
117
+ | -------- | -------- | ---------------------------------------------- |
118
+ | `token` | yes | Pushover application API token/key. |
119
+ | `user` | yes | Your Pushover user or group key. |
120
+
121
+ Delivery: `POST https://api.pushover.net/1/messages.json` with title/message and a priority
122
+ (Info→0 normal, everything higher→1 high; never emergency). The snapshot **image** is sent as an
123
+ `attachment`, and an absolute deep link becomes a supplementary `url`.
124
+
125
+ ### Telegram
126
+
127
+ Delivers to a chat via a [Telegram bot](https://core.telegram.org/bots).
128
+
129
+ | Field | Required | Notes |
130
+ | ------- | -------- | -------------------------------------------------------- |
131
+ | `token` | yes | Bot token from @BotFather. |
132
+ | `chat` | yes | Chat ID to deliver to. |
133
+
134
+ Delivery: `sendPhoto` (with the snapshot **image** + caption) when a thumbnail is present, otherwise
135
+ `sendMessage`. An absolute deep link is added as an inline "Open camera" button.
136
+
137
+ ### Discord
138
+
139
+ Delivers to a channel via a Discord [webhook](https://support.discord.com/hc/en-us/articles/228383668).
140
+
141
+ | Field | Required | Notes |
142
+ | --------- | -------- | --------------------------------------- |
143
+ | `webhook` | yes | Channel webhook URL. |
144
+
145
+ Delivery: a rich embed (title, body, severity color — blue/yellow/red) with the snapshot **image**
146
+ attached; an absolute deep link makes the title a link.
147
+
148
+ > **Images:** ntfy, Pushover, Telegram, and Discord all render the detection snapshot. Gotify is
149
+ > text + link only (it needs a hosted image URL, which this fully-local plugin doesn't provide).
150
+
151
+ > **Secrets in logs:** transport failures never log the bot token / webhook URL / other
152
+ > URL-embedded secret — request URLs are redacted from delivery errors.
153
+
154
+ ## Configuring your target (v1: one active target)
155
+
156
+ There is no "add device" flow. Instead, configure the plugin itself:
157
+
158
+ 1. Open the **Notify** plugin's page in camera.ui (Plugins → Notify).
159
+ 2. In its settings, pick a **Service** (`ntfy`, `Gotify`, `Generic webhook`, `Pushover`, `Telegram`,
160
+ or `Discord`) from the dropdown built from the registered backends.
161
+ 3. Fill in that service's fields — only the selected service's fields are shown; the rest are
162
+ condition-gated out.
163
+ 4. Save. The config is validated (`ParseTarget`) the next time a notification is dispatched;
164
+ `getDevices` then synthesizes one delivery target from it, and notifications from any publisher
165
+ are delivered there.
166
+
167
+ This is a **single, instance-wide target** in v1 — there's no way to register several devices at
168
+ once. Changing the config replaces the previous target rather than adding to it. Delivery for that
169
+ one target is a single request per notification (no fan-out to worry about, since there's only one
170
+ device).
171
+
172
+ ## Tech stack
173
+
174
+ - **Go** (matches the NVR plugin; same build/deploy toolchain).
175
+ - **camera.ui SDK (Go)** for the plugin runtime, the `Notifier` RPC surface, and device storage.
176
+ - Delivery via each backend's plain HTTP API using the Go stdlib `net/http` — no third-party HTTP
177
+ client libraries.
178
+ - Config persistence via the plugin's own `DeviceStorage`, holding the selected service and its
179
+ fields as declared by `StorageSchema()` — no SQLite; it's a handful of scalar values.
180
+
181
+ ## Prerequisites
182
+
183
+ - **Go 1.26+** (to build the plugin binary).
184
+ - **Node.js 22+** and the plugin's dev dependencies (`npm install`) to produce the `contract.cjs`
185
+ bundle via the camera.ui CLI.
186
+ - A running camera.ui instance you control (the host where the plugin gets installed).
187
+
188
+ ## Build & deploy locally
189
+
190
+ camera.ui loads the **built artifact**, not source — a `git pull` alone does nothing. You must
191
+ build the bundle (`contract.cjs` + the platform binary) and copy it into the install slot.
192
+
193
+ The install slot is:
194
+
195
+ ```
196
+ <camera.ui-install>/plugins/@calebcall/camera-ui-notify/
197
+ ```
198
+
199
+ and the plugin's config (the selected service and its fields) lives under
200
+ `<camera.ui-install>/volume/plugins/storage/@calebcall/camera-ui-notify/` — reinstalling the code
201
+ does not touch it.
202
+
203
+ > **Unlike the NVR plugin**, there is no hardcoded-package-id requirement here: the camera.ui host
204
+ > discovers notifiers by the `Notifier` **interface** declared in the plugin's contract, not by a
205
+ > fixed package name. This plugin can be installed under its own real name/namespace.
206
+
207
+ ### Option A — build natively on the server (preferred)
208
+
209
+ If Go 1.26+ and Node are available on the host, build there and skip the cross-compile round-trip:
210
+
211
+ ```bash
212
+ # 1. Get the latest source
213
+ cd ~/path/to/plugins && git pull
214
+ cd camera-ui-notify
215
+ npm install # first time only — pulls the camera.ui CLI bundler
216
+
217
+ # 2. Build the bundle (produces bundle/{contract.cjs, package.json, dist/bin/plugin})
218
+ npm run bundle:dev
219
+
220
+ # 3. Install into the @calebcall/camera-ui-notify slot
221
+ D=<camera.ui-install>/plugins/@calebcall/camera-ui-notify
222
+ mkdir -p "$D" && rm -rf "$D"/* && cp -a bundle/. "$D/"
223
+
224
+ # 4. First time only: enable it — remove the "@calebcall/camera-ui-notify" line
225
+ # from disabledPlugins in <camera.ui-install>/volume/camera.ui.yaml
226
+
227
+ # 5. Restart camera.ui
228
+ systemctl restart cameraui # or however your instance is managed
229
+ ```
230
+
231
+ ### Option B — cross-compile from a workstation
232
+
233
+ Build a statically-linked Linux binary locally, then ship the bundle to the server:
234
+
235
+ ```bash
236
+ cd camera-ui-notify
237
+
238
+ # 1. Produce contract.cjs (+ package.json) under bundle/
239
+ npm install
240
+ npm run bundle:dev
241
+
242
+ # 2. Cross-compile the Linux binary into the bundle's dev path
243
+ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 \
244
+ go build -ldflags "-s -w" -o bundle/dist/bin/plugin ./src/
245
+ chmod 755 bundle/dist/bin/plugin
246
+
247
+ # 3. Ship it (strip macOS junk so the server dir stays clean)
248
+ COPYFILE_DISABLE=1 tar czf /tmp/notify-install.tgz -C bundle .
249
+ scp /tmp/notify-install.tgz root@YOUR_SERVER:/tmp/
250
+
251
+ # 4. On the server: install into the slot, enable (first time), restart
252
+ ssh root@YOUR_SERVER '
253
+ D=<camera.ui-install>/plugins/@calebcall/camera-ui-notify
254
+ mkdir -p "$D" && rm -rf "$D"/* && tar xzf /tmp/notify-install.tgz -C "$D"
255
+ systemctl restart cameraui
256
+ '
257
+ ```
258
+
259
+ Set `GOARCH=arm64` for ARM hosts. The binary is resolved from the dev path
260
+ `<slot>/dist/bin/plugin` before any platform `node_modules` package, so no npm publish is involved.
261
+
262
+ ### Verify it loaded and is being used
263
+
264
+ Tail the camera.ui log after restart:
265
+
266
+ ```bash
267
+ grep -iE "Notify|Spawning Go|notify: rpc" <camera.ui-install>/volume/camera.ui.log | tail
268
+ ```
269
+
270
+ You want to see the plugin spawn (`Spawning Go plugin ... dist/bin/plugin`) **and** the host
271
+ actually calling into it — e.g. `notify: rpc getDevices` / `notificationSettings` /
272
+ `sendNotification`.
273
+
274
+ ## Adding a new backend
275
+
276
+ 1. Create `src/backend/<name>.go` implementing the `Backend` interface (`ID`, `Label`, `Schema`,
277
+ `ParseTarget`, `Send`) — see `ntfy.go`, `gotify.go`, or `webhook.go` for the shape.
278
+ 2. Gate every schema field with `Condition: []sdk.SchemaCondition{{Key: "service", Value: ID()}}`
279
+ so it only renders when the new backend is selected.
280
+ 3. Call `backend.Register(new<Name>())` from an `init()` function in the same file.
281
+ 4. Bump the version in `package.json`.
282
+
283
+ Nothing outside `src/backend/` needs to change — the registry, the `service` enum, and the
284
+ dispatch logic in `notifier.go` all pick up the new backend automatically.
285
+
286
+ ## Development
287
+
288
+ ```bash
289
+ go test ./src/... # full suite
290
+ go test ./src/... -race -count=1 # race detector
291
+ ```
292
+
293
+ ## License
294
+
295
+ [MIT](./LICENSE.md).
package/bundle.zip ADDED
Binary file
package/package.json ADDED
@@ -0,0 +1,61 @@
1
+ {
2
+ "displayName": "Notify",
3
+ "name": "@calebcall/camera-ui-notify",
4
+ "version": "0.4.0",
5
+ "description": "A fully-local, multi-backend camera.ui notifier plugin: delivers notifications to ntfy, Gotify, or a generic webhook, entirely on your own hardware with no cloud dependency.",
6
+ "author": "calebcall (https://github.com/calebcall/camera-ui-notify)",
7
+ "main": "./main.go",
8
+ "type": "commonjs",
9
+ "scripts": {
10
+ "build": "rimraf dist && go build -o /dev/null ./src/",
11
+ "bundle": "npm run build && cui bundle",
12
+ "bundle:dev": "npm run build && cross-env MODE=development cui bundle",
13
+ "format": "gofmt -w ./src/* && go fix ./src/...",
14
+ "install-updates": "npm i --save --force && go mod tidy",
15
+ "lint": "staticcheck ./src/... && golangci-lint run ./src/...",
16
+ "prepublishOnly": "node -e \"if(!process.env.SAFE_PUBLISH){console.error('Error: Please use @camera.ui/cli to publish the plugin:\\n npm run publish:alpha\\n npm run publish:beta\\n npm run publish:latest\\n');process.exit(1)}\"",
17
+ "publish:alpha": "npm i --save --force && npm run bundle && cui publish --alpha",
18
+ "publish:beta": "npm i --save --force && npm run bundle && cui publish --beta",
19
+ "publish:latest": "npm i --save --force && npm run bundle && cui publish --latest",
20
+ "test": "go test ./...",
21
+ "update": "updates --update ./"
22
+ },
23
+ "license": "MIT",
24
+ "bugs": {
25
+ "url": "https://github.com/calebcall/camera-ui-notify/issues"
26
+ },
27
+ "engines": {
28
+ "camera.ui": ">=2.0.15",
29
+ "node": ">=22.0.0"
30
+ },
31
+ "homepage": "https://github.com/calebcall/camera-ui-notify/tree/main#readme",
32
+ "keywords": [
33
+ "camera-ui-plugin",
34
+ "notifier",
35
+ "notifications",
36
+ "ntfy",
37
+ "gotify",
38
+ "webhook",
39
+ "local"
40
+ ],
41
+ "repository": {
42
+ "type": "git",
43
+ "url": "git+https://github.com/calebcall/camera-ui-notify.git"
44
+ },
45
+ "os": [],
46
+ "cpu": [],
47
+ "optionalDependencies": {
48
+ "@calebcall/camera-ui-notify-darwin-arm64": "0.4.0",
49
+ "@calebcall/camera-ui-notify-darwin-amd64": "0.4.0",
50
+ "@calebcall/camera-ui-notify-linux-amd64": "0.4.0",
51
+ "@calebcall/camera-ui-notify-linux-arm64": "0.4.0",
52
+ "@calebcall/camera-ui-notify-windows-amd64": "0.4.0",
53
+ "@calebcall/camera-ui-notify-windows-arm64": "0.4.0",
54
+ "@calebcall/camera-ui-notify-linux-amd64-musl": "0.4.0",
55
+ "@calebcall/camera-ui-notify-linux-arm64-musl": "0.4.0"
56
+ },
57
+ "files": [
58
+ "bundle.zip",
59
+ "CHANGELOG.md"
60
+ ]
61
+ }