nudj 1.0.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/LICENSE +21 -0
- package/README.md +190 -0
- package/dist/cli/cli.test.d.ts +2 -0
- package/dist/cli/cli.test.d.ts.map +1 -0
- package/dist/cli/cli.test.js +129 -0
- package/dist/cli/cli.test.js.map +1 -0
- package/dist/cli/commands/config.d.ts +8 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +26 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/pair.d.ts +2 -0
- package/dist/cli/commands/pair.d.ts.map +1 -0
- package/dist/cli/commands/pair.js +66 -0
- package/dist/cli/commands/pair.js.map +1 -0
- package/dist/cli/commands/push.d.ts +24 -0
- package/dist/cli/commands/push.d.ts.map +1 -0
- package/dist/cli/commands/push.js +88 -0
- package/dist/cli/commands/push.js.map +1 -0
- package/dist/cli/commands/receivers.d.ts +8 -0
- package/dist/cli/commands/receivers.d.ts.map +1 -0
- package/dist/cli/commands/receivers.js +155 -0
- package/dist/cli/commands/receivers.js.map +1 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +22 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/lib/config.d.ts +43 -0
- package/dist/cli/lib/config.d.ts.map +1 -0
- package/dist/cli/lib/config.js +120 -0
- package/dist/cli/lib/config.js.map +1 -0
- package/dist/cli/lib/pairing.d.ts +7 -0
- package/dist/cli/lib/pairing.d.ts.map +1 -0
- package/dist/cli/lib/pairing.js +67 -0
- package/dist/cli/lib/pairing.js.map +1 -0
- package/dist/cli/lib/push.d.ts +13 -0
- package/dist/cli/lib/push.d.ts.map +1 -0
- package/dist/cli/lib/push.js +103 -0
- package/dist/cli/lib/push.js.map +1 -0
- package/dist/cli/lib/types.d.ts +20 -0
- package/dist/cli/lib/types.d.ts.map +1 -0
- package/dist/cli/lib/types.js +2 -0
- package/dist/cli/lib/types.js.map +1 -0
- package/dist/cli/version.d.ts +2 -0
- package/dist/cli/version.d.ts.map +1 -0
- package/dist/cli/version.js +17 -0
- package/dist/cli/version.js.map +1 -0
- package/package.json +69 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 nudj contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="assets/logo.svg" alt="nudj logo" width="120" height="120">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# nudj
|
|
6
|
+
|
|
7
|
+
**Send push notifications from your CLI to your phone**
|
|
8
|
+
|
|
9
|
+
[](https://github.com/redneb/nudj/actions/workflows/ci.yml)
|
|
10
|
+
[](https://www.npmjs.com/package/nudj)
|
|
11
|
+
[](https://opensource.org/licenses/MIT)
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## What is nudj?
|
|
16
|
+
|
|
17
|
+
nudj lets you send push notifications from your computer to your phone using a simple command-line tool. No account required, no servers — just Web Push and end-to-end encryption.
|
|
18
|
+
|
|
19
|
+
### Features
|
|
20
|
+
|
|
21
|
+
- 🔒 **End-to-end encrypted** — Only you can read your notifications
|
|
22
|
+
- 🚫 **No account required** — No sign-up, no login, no tracking
|
|
23
|
+
- 🌐 **Works everywhere** — Uses standard Web Push (works on iOS, Android, and desktop)
|
|
24
|
+
- ⚡ **Lightweight** — Single-file CLI, ~18KB PWA
|
|
25
|
+
- 🔓 **Open source** — MIT licensed, fully auditable
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Installation
|
|
30
|
+
|
|
31
|
+
### CLI
|
|
32
|
+
|
|
33
|
+
**Option 1: npm (recommended)**
|
|
34
|
+
|
|
35
|
+
```bash
|
|
36
|
+
npm install -g nudj
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**Option 2: Single-file download**
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Download the latest release
|
|
43
|
+
curl -L https://github.com/redneb/nudj/releases/latest/download/nudj.js -o ~/.local/bin/nudj
|
|
44
|
+
chmod +x ~/.local/bin/nudj
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires Node.js 22 or later.
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Quick Start
|
|
52
|
+
|
|
53
|
+
### 1. Install the PWA on your phone
|
|
54
|
+
|
|
55
|
+
Visit **[https://nudj.atopon.net](https://nudj.atopon.net)** on your phone.
|
|
56
|
+
|
|
57
|
+
**iOS users must install it as an app** (required for push notifications):
|
|
58
|
+
|
|
59
|
+
- **iOS**: Safari → Share → "Add to Home Screen" (required)
|
|
60
|
+
- **Android**: Chrome → Menu → "Install app" (optional — or just use the website)
|
|
61
|
+
|
|
62
|
+
Then tap **Enable Notifications** in the app.
|
|
63
|
+
|
|
64
|
+
### 2. Pair your phone with your computer
|
|
65
|
+
|
|
66
|
+
Copy the pairing code shown in the app, then on your computer:
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
nudj pair
|
|
70
|
+
# Paste the pairing code when prompted
|
|
71
|
+
# Give your phone a name (e.g., 'iPhone')
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
### 3. Send your first notification
|
|
75
|
+
|
|
76
|
+
```bash
|
|
77
|
+
nudj push 'Hello from my computer!'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
That's it! You should see a notification on your phone.
|
|
81
|
+
|
|
82
|
+
---
|
|
83
|
+
|
|
84
|
+
## CLI Reference
|
|
85
|
+
|
|
86
|
+
### Send a notification
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
nudj push 'Build completed'
|
|
90
|
+
nudj push --title 'CI' 'Build #1234 passed'
|
|
91
|
+
nudj push --to iPhone 'Your coffee is ready'
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
### Manage receivers
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
nudj receivers # List all paired devices
|
|
98
|
+
nudj receivers rename 'Old' 'New' # Rename a device
|
|
99
|
+
nudj receivers remove 'Phone' # Remove a device
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### Configuration
|
|
103
|
+
|
|
104
|
+
```bash
|
|
105
|
+
nudj config # Show config file location
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
Configuration is stored at:
|
|
109
|
+
- Linux/macOS: `~/.config/nudj/config.json`
|
|
110
|
+
- Windows: `%APPDATA%\nudj\config.json`
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
## Privacy & Security
|
|
115
|
+
|
|
116
|
+
nudj is designed with privacy as a core principle:
|
|
117
|
+
|
|
118
|
+
- **No accounts** — You don't need to sign up for anything
|
|
119
|
+
- **No cloud storage** — Your credentials stay on your devices
|
|
120
|
+
- **No server** — nudj operates no servers and stores none of your data
|
|
121
|
+
- **End-to-end encryption** — Messages are encrypted using Web Push standards (RFC 8291)
|
|
122
|
+
- **No tracking** — The app creator has no access to your data
|
|
123
|
+
|
|
124
|
+
### How it works
|
|
125
|
+
|
|
126
|
+
1. Your phone generates encryption keys
|
|
127
|
+
2. You transfer a pairing code to your computer (containing the keys)
|
|
128
|
+
3. Your computer encrypts notifications using those keys
|
|
129
|
+
4. Only your phone can decrypt them
|
|
130
|
+
|
|
131
|
+
The push service (Google FCM, Apple APNs, Mozilla) only sees encrypted blobs — they cannot read your notification content.
|
|
132
|
+
|
|
133
|
+
### Security considerations
|
|
134
|
+
|
|
135
|
+
- Treat the pairing code like a password — anyone with it can send you notifications
|
|
136
|
+
- You can reset your subscription at any time to revoke all access
|
|
137
|
+
- Store your CLI config file securely (it contains the encryption keys)
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## Development
|
|
142
|
+
|
|
143
|
+
### Prerequisites
|
|
144
|
+
|
|
145
|
+
- Node.js 22+
|
|
146
|
+
- npm
|
|
147
|
+
|
|
148
|
+
### Setup
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
git clone https://github.com/redneb/nudj.git
|
|
152
|
+
cd nudj
|
|
153
|
+
npm install
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
### Commands
|
|
157
|
+
|
|
158
|
+
```bash
|
|
159
|
+
npm run dev:cli # Run CLI in development mode
|
|
160
|
+
npm run dev:web # Start Vite dev server for PWA
|
|
161
|
+
npm run build # Build everything
|
|
162
|
+
npm run build:cli # Build CLI only
|
|
163
|
+
npm run build:web # Build PWA only
|
|
164
|
+
npm run test # Run all tests
|
|
165
|
+
npm run check # Type check and lint
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
### Project Structure
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
src/
|
|
172
|
+
├── cli/ # CLI implementation (Node.js)
|
|
173
|
+
├── web/ # PWA implementation (Solid.js)
|
|
174
|
+
└── common/ # Shared type definitions
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
---
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
MIT — see [LICENSE](LICENSE)
|
|
182
|
+
|
|
183
|
+
---
|
|
184
|
+
|
|
185
|
+
## Acknowledgments
|
|
186
|
+
|
|
187
|
+
Built with:
|
|
188
|
+
- [citty](https://github.com/unjs/citty) — CLI framework
|
|
189
|
+
- [Solid.js](https://www.solidjs.com/) — UI framework
|
|
190
|
+
- [@block65/webcrypto-web-push](https://github.com/block65/webcrypto-web-push) — Web Push library
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.test.d.ts","sourceRoot":"","sources":["../../src/cli/cli.test.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import * as fs from "node:fs";
|
|
3
|
+
import * as path from "node:path";
|
|
4
|
+
import * as os from "node:os";
|
|
5
|
+
import { decodePairingCode } from "./lib/pairing.js";
|
|
6
|
+
import { readConfig, writeConfig, addReceiver, removeReceiver, renameReceiver, getConfigPath, } from "./lib/config.js";
|
|
7
|
+
describe("pairing", () => {
|
|
8
|
+
it("should decode a valid pairing code", () => {
|
|
9
|
+
const pairingData = {
|
|
10
|
+
endpoint: "https://fcm.googleapis.com/fcm/send/abc123",
|
|
11
|
+
keys: {
|
|
12
|
+
p256dh: "BNcRdreALRFXTkOOUHK1EtK2wtaz5Ry4YfYCA",
|
|
13
|
+
auth: "tBHItJI5svbpez7KI4CCXg",
|
|
14
|
+
},
|
|
15
|
+
vapid: {
|
|
16
|
+
privateKey: "dGhpcyBpcyBhIHRlc3Qga2V5Li4u",
|
|
17
|
+
},
|
|
18
|
+
};
|
|
19
|
+
// Encode to base64url
|
|
20
|
+
const json = JSON.stringify(pairingData);
|
|
21
|
+
const base64 = btoa(json);
|
|
22
|
+
const base64url = base64
|
|
23
|
+
.replace(/\+/g, "-")
|
|
24
|
+
.replace(/\//g, "_")
|
|
25
|
+
.replace(/=+$/, "");
|
|
26
|
+
// Decode and verify
|
|
27
|
+
const result = decodePairingCode(base64url);
|
|
28
|
+
expect(result).not.toBeNull();
|
|
29
|
+
expect(result?.endpoint).toBe(pairingData.endpoint);
|
|
30
|
+
expect(result?.keys.p256dh).toBe(pairingData.keys.p256dh);
|
|
31
|
+
expect(result?.keys.auth).toBe(pairingData.keys.auth);
|
|
32
|
+
expect(result?.vapid.privateKey).toBe(pairingData.vapid.privateKey);
|
|
33
|
+
});
|
|
34
|
+
it("should return null for invalid pairing code", () => {
|
|
35
|
+
expect(decodePairingCode("invalid")).toBeNull();
|
|
36
|
+
expect(decodePairingCode("")).toBeNull();
|
|
37
|
+
expect(decodePairingCode("{}")).toBeNull();
|
|
38
|
+
});
|
|
39
|
+
it("should return null for missing required fields", () => {
|
|
40
|
+
const incomplete = { endpoint: "https://example.com" };
|
|
41
|
+
const json = JSON.stringify(incomplete);
|
|
42
|
+
const base64url = btoa(json).replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
43
|
+
expect(decodePairingCode(base64url)).toBeNull();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
describe("config", () => {
|
|
47
|
+
let tempDir;
|
|
48
|
+
const originalEnv = process.env["NUDJ_CONFIG"];
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
// Create temp directory for test config
|
|
51
|
+
tempDir = fs.mkdtempSync(path.join(os.tmpdir(), "nudj-test-"));
|
|
52
|
+
process.env["NUDJ_CONFIG"] = path.join(tempDir, "config.json");
|
|
53
|
+
});
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
// Restore original env and clean up temp dir
|
|
56
|
+
if (originalEnv)
|
|
57
|
+
process.env["NUDJ_CONFIG"] = originalEnv;
|
|
58
|
+
else
|
|
59
|
+
delete process.env["NUDJ_CONFIG"];
|
|
60
|
+
fs.rmSync(tempDir, { recursive: true, force: true });
|
|
61
|
+
});
|
|
62
|
+
it("should return empty config when file does not exist", () => {
|
|
63
|
+
const config = readConfig();
|
|
64
|
+
expect(config.receivers).toEqual([]);
|
|
65
|
+
});
|
|
66
|
+
it("should write and read config", () => {
|
|
67
|
+
const config = { receivers: [] };
|
|
68
|
+
writeConfig(config);
|
|
69
|
+
const read = readConfig();
|
|
70
|
+
expect(read.receivers).toEqual([]);
|
|
71
|
+
});
|
|
72
|
+
it("should add a receiver", () => {
|
|
73
|
+
const receiver = {
|
|
74
|
+
name: "Test Phone",
|
|
75
|
+
endpoint: "https://example.com/push",
|
|
76
|
+
keys: { p256dh: "abc", auth: "def" },
|
|
77
|
+
vapid: { privateKey: "ghi" },
|
|
78
|
+
addedAt: new Date().toISOString(),
|
|
79
|
+
lastUsedAt: null,
|
|
80
|
+
};
|
|
81
|
+
addReceiver(receiver);
|
|
82
|
+
const config = readConfig();
|
|
83
|
+
expect(config.receivers).toHaveLength(1);
|
|
84
|
+
expect(config.receivers[0]?.name).toBe("Test Phone");
|
|
85
|
+
});
|
|
86
|
+
it("should remove a receiver", () => {
|
|
87
|
+
const receiver = {
|
|
88
|
+
name: "Test Phone",
|
|
89
|
+
endpoint: "https://example.com/push",
|
|
90
|
+
keys: { p256dh: "abc", auth: "def" },
|
|
91
|
+
vapid: { privateKey: "ghi" },
|
|
92
|
+
addedAt: new Date().toISOString(),
|
|
93
|
+
lastUsedAt: null,
|
|
94
|
+
};
|
|
95
|
+
addReceiver(receiver);
|
|
96
|
+
expect(readConfig().receivers).toHaveLength(1);
|
|
97
|
+
const removed = removeReceiver("Test Phone");
|
|
98
|
+
expect(removed).toBe(true);
|
|
99
|
+
expect(readConfig().receivers).toHaveLength(0);
|
|
100
|
+
});
|
|
101
|
+
it("should return false when removing non-existent receiver", () => {
|
|
102
|
+
const removed = removeReceiver("Non-existent");
|
|
103
|
+
expect(removed).toBe(false);
|
|
104
|
+
});
|
|
105
|
+
it("should rename a receiver", () => {
|
|
106
|
+
const receiver = {
|
|
107
|
+
name: "Old Name",
|
|
108
|
+
endpoint: "https://example.com/push",
|
|
109
|
+
keys: { p256dh: "abc", auth: "def" },
|
|
110
|
+
vapid: { privateKey: "ghi" },
|
|
111
|
+
addedAt: new Date().toISOString(),
|
|
112
|
+
lastUsedAt: null,
|
|
113
|
+
};
|
|
114
|
+
addReceiver(receiver);
|
|
115
|
+
const renamed = renameReceiver("Old Name", "New Name");
|
|
116
|
+
expect(renamed).toBe(true);
|
|
117
|
+
const config = readConfig();
|
|
118
|
+
expect(config.receivers[0]?.name).toBe("New Name");
|
|
119
|
+
});
|
|
120
|
+
it("should return false when renaming non-existent receiver", () => {
|
|
121
|
+
const renamed = renameReceiver("Non-existent", "New Name");
|
|
122
|
+
expect(renamed).toBe(false);
|
|
123
|
+
});
|
|
124
|
+
it("should use NUDJ_CONFIG env var for config path", () => {
|
|
125
|
+
const configPath = getConfigPath();
|
|
126
|
+
expect(configPath).toBe(path.join(tempDir, "config.json"));
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
//# sourceMappingURL=cli.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.test.js","sourceRoot":"","sources":["../../src/cli/cli.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,UAAU,EAAE,SAAS,EAAC,MAAM,QAAQ,CAAC;AACnE,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,KAAK,IAAI,MAAM,WAAW,CAAC;AAClC,OAAO,KAAK,EAAE,MAAM,SAAS,CAAC;AAC9B,OAAO,EAAC,iBAAiB,EAAC,MAAM,kBAAkB,CAAC;AACnD,OAAO,EACN,UAAU,EACV,WAAW,EACX,WAAW,EACX,cAAc,EACd,cAAc,EACd,aAAa,GACb,MAAM,iBAAiB,CAAC;AAIzB,QAAQ,CAAC,SAAS,EAAE,GAAG,EAAE;IACxB,EAAE,CAAC,oCAAoC,EAAE,GAAG,EAAE;QAC7C,MAAM,WAAW,GAAgB;YAChC,QAAQ,EAAE,4CAA4C;YACtD,IAAI,EAAE;gBACL,MAAM,EAAE,uCAAuC;gBAC/C,IAAI,EAAE,wBAAwB;aAC9B;YACD,KAAK,EAAE;gBACN,UAAU,EAAE,8BAA8B;aAC1C;SACD,CAAC;QAEF,sBAAsB;QACtB,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,MAAM,SAAS,GAAG,MAAM;aACtB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;aACnB,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC;aACnB,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAErB,oBAAoB;QACpB,MAAM,MAAM,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;QAC5C,MAAM,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,QAAQ,EAAE,CAAC;QAC9B,MAAM,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;QACpD,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC;QAC1D,MAAM,CAAC,MAAM,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACtD,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;QACtD,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QAChD,MAAM,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;QACzC,MAAM,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IAC5C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACzD,MAAM,UAAU,GAAG,EAAC,QAAQ,EAAE,qBAAqB,EAAC,CAAC;QACrD,MAAM,IAAI,GAAG,IAAI,CAAC,SAAS,CAAC,UAAU,CAAC,CAAC;QACxC,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAExF,MAAM,CAAC,iBAAiB,CAAC,SAAS,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC;IACjD,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC;AAEH,QAAQ,CAAC,QAAQ,EAAE,GAAG,EAAE;IACvB,IAAI,OAAe,CAAC;IACpB,MAAM,WAAW,GAAG,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;IAE/C,UAAU,CAAC,GAAG,EAAE;QACf,wCAAwC;QACxC,OAAO,GAAG,EAAE,CAAC,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,MAAM,EAAE,EAAE,YAAY,CAAC,CAAC,CAAC;QAC/D,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC;IAChE,CAAC,CAAC,CAAC;IAEH,SAAS,CAAC,GAAG,EAAE;QACd,6CAA6C;QAC7C,IAAI,WAAW;YACd,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,GAAG,WAAW,CAAC;;YAGzC,OAAO,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;QAEnC,EAAE,CAAC,MAAM,CAAC,OAAO,EAAE,EAAC,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAC,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qDAAqD,EAAE,GAAG,EAAE;QAC9D,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACtC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACvC,MAAM,MAAM,GAAG,EAAC,SAAS,EAAE,EAAE,EAAC,CAAC;QAC/B,WAAW,CAAC,MAAM,CAAC,CAAC;QAEpB,MAAM,IAAI,GAAG,UAAU,EAAE,CAAC;QAC1B,MAAM,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;IACpC,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,uBAAuB,EAAE,GAAG,EAAE;QAChC,MAAM,QAAQ,GAAmB;YAChC,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,0BAA0B;YACpC,IAAI,EAAE,EAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAC;YAClC,KAAK,EAAE,EAAC,UAAU,EAAE,KAAK,EAAC;YAC1B,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACjC,UAAU,EAAE,IAAI;SAChB,CAAC;QAEF,WAAW,CAAC,QAAQ,CAAC,CAAC;QAEtB,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QACzC,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;IACtD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACnC,MAAM,QAAQ,GAAmB;YAChC,IAAI,EAAE,YAAY;YAClB,QAAQ,EAAE,0BAA0B;YACpC,IAAI,EAAE,EAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAC;YAClC,KAAK,EAAE,EAAC,UAAU,EAAE,KAAK,EAAC;YAC1B,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACjC,UAAU,EAAE,IAAI;SAChB,CAAC;QAEF,WAAW,CAAC,QAAQ,CAAC,CAAC;QACtB,MAAM,CAAC,UAAU,EAAE,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;QAE/C,MAAM,OAAO,GAAG,cAAc,CAAC,YAAY,CAAC,CAAC;QAC7C,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,CAAC,UAAU,EAAE,CAAC,SAAS,CAAC,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC;IAChD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QAClE,MAAM,OAAO,GAAG,cAAc,CAAC,cAAc,CAAC,CAAC;QAC/C,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,0BAA0B,EAAE,GAAG,EAAE;QACnC,MAAM,QAAQ,GAAmB;YAChC,IAAI,EAAE,UAAU;YAChB,QAAQ,EAAE,0BAA0B;YACpC,IAAI,EAAE,EAAC,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAC;YAClC,KAAK,EAAE,EAAC,UAAU,EAAE,KAAK,EAAC;YAC1B,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACjC,UAAU,EAAE,IAAI;SAChB,CAAC;QAEF,WAAW,CAAC,QAAQ,CAAC,CAAC;QACtB,MAAM,OAAO,GAAG,cAAc,CAAC,UAAU,EAAE,UAAU,CAAC,CAAC;QAEvD,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3B,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,yDAAyD,EAAE,GAAG,EAAE;QAClE,MAAM,OAAO,GAAG,cAAc,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;QAC3D,MAAM,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;QACzD,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;QACnC,MAAM,CAAC,UAAU,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC;IAC5D,CAAC,CAAC,CAAC;AACJ,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,aAAa;;;;;;EAwBxB,CAAC"}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { getConfigPath, getReceivers } from "../lib/config.js";
|
|
3
|
+
export const configCommand = defineCommand({
|
|
4
|
+
meta: {
|
|
5
|
+
name: "config",
|
|
6
|
+
description: "Show configuration file location",
|
|
7
|
+
},
|
|
8
|
+
args: {
|
|
9
|
+
path: {
|
|
10
|
+
type: "boolean",
|
|
11
|
+
description: "Print only the path (for scripting)",
|
|
12
|
+
default: false,
|
|
13
|
+
},
|
|
14
|
+
},
|
|
15
|
+
run({ args }) {
|
|
16
|
+
const configPath = getConfigPath();
|
|
17
|
+
if (args.path) {
|
|
18
|
+
console.log(configPath);
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
const receivers = getReceivers();
|
|
22
|
+
console.log(`Configuration file: ${configPath}`);
|
|
23
|
+
console.log(`Receivers configured: ${receivers.length}`);
|
|
24
|
+
},
|
|
25
|
+
});
|
|
26
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../../src/cli/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,OAAO,CAAC;AACpC,OAAO,EAAC,aAAa,EAAE,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAE7D,MAAM,CAAC,MAAM,aAAa,GAAG,aAAa,CAAC;IAC1C,IAAI,EAAE;QACL,IAAI,EAAE,QAAQ;QACd,WAAW,EAAE,kCAAkC;KAC/C;IACD,IAAI,EAAE;QACL,IAAI,EAAE;YACL,IAAI,EAAE,SAAS;YACf,WAAW,EAAE,qCAAqC;YAClD,OAAO,EAAE,KAAK;SACd;KACD;IACD,GAAG,CAAC,EAAC,IAAI,EAAC;QACT,MAAM,UAAU,GAAG,aAAa,EAAE,CAAC;QAEnC,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;YACf,OAAO,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;YACxB,OAAO;QACR,CAAC;QAED,MAAM,SAAS,GAAG,YAAY,EAAE,CAAC;QACjC,OAAO,CAAC,GAAG,CAAC,uBAAuB,UAAU,EAAE,CAAC,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,yBAAyB,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC;IAC1D,CAAC;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pair.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/pair.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,WAAW,gCAuEtB,CAAC"}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import * as readline from "node:readline";
|
|
3
|
+
import { decodePairingCode } from "../lib/pairing.js";
|
|
4
|
+
import { addReceiver, getReceivers } from "../lib/config.js";
|
|
5
|
+
export const pairCommand = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "pair",
|
|
8
|
+
description: "Add a new receiver by entering a pairing code",
|
|
9
|
+
},
|
|
10
|
+
args: {},
|
|
11
|
+
async run() {
|
|
12
|
+
const rl = readline.createInterface({
|
|
13
|
+
input: process.stdin,
|
|
14
|
+
output: process.stdout,
|
|
15
|
+
});
|
|
16
|
+
const question = (prompt) => {
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
rl.question(prompt, resolve);
|
|
19
|
+
});
|
|
20
|
+
};
|
|
21
|
+
try {
|
|
22
|
+
// Get pairing code
|
|
23
|
+
const code = await question("Paste pairing code: ");
|
|
24
|
+
const pairingData = decodePairingCode(code);
|
|
25
|
+
if (!pairingData) {
|
|
26
|
+
console.error("\n✗ Invalid pairing code. Make sure you copied the entire code from the nudj app.");
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
// Get receiver name
|
|
30
|
+
let name = "";
|
|
31
|
+
const existingReceivers = getReceivers();
|
|
32
|
+
while (!name) {
|
|
33
|
+
const inputName = await question("Name for this receiver: ");
|
|
34
|
+
const trimmedName = inputName.trim();
|
|
35
|
+
if (!trimmedName) {
|
|
36
|
+
console.error("Name cannot be empty.");
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
// Check for duplicate name
|
|
40
|
+
if (existingReceivers.some(r => r.name === trimmedName)) {
|
|
41
|
+
console.error(`✗ A receiver named '${trimmedName}' already exists. Choose a different name.`);
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
name = trimmedName;
|
|
45
|
+
}
|
|
46
|
+
// Create receiver config
|
|
47
|
+
const receiver = {
|
|
48
|
+
name,
|
|
49
|
+
endpoint: pairingData.endpoint,
|
|
50
|
+
keys: pairingData.keys,
|
|
51
|
+
vapid: pairingData.vapid,
|
|
52
|
+
addedAt: new Date().toISOString(),
|
|
53
|
+
lastUsedAt: null,
|
|
54
|
+
};
|
|
55
|
+
// Save receiver
|
|
56
|
+
addReceiver(receiver);
|
|
57
|
+
const totalReceivers = existingReceivers.length + 1;
|
|
58
|
+
console.log(`\n✓ Receiver '${name}' added successfully`);
|
|
59
|
+
console.log(`\nYou now have ${totalReceivers} receiver(s) configured.`);
|
|
60
|
+
}
|
|
61
|
+
finally {
|
|
62
|
+
rl.close();
|
|
63
|
+
}
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
//# sourceMappingURL=pair.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"pair.js","sourceRoot":"","sources":["../../../src/cli/commands/pair.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,aAAa,EAAC,MAAM,OAAO,CAAC;AACpC,OAAO,KAAK,QAAQ,MAAM,eAAe,CAAC;AAC1C,OAAO,EAAC,iBAAiB,EAAC,MAAM,mBAAmB,CAAC;AACpD,OAAO,EAAC,WAAW,EAAE,YAAY,EAAC,MAAM,kBAAkB,CAAC;AAG3D,MAAM,CAAC,MAAM,WAAW,GAAG,aAAa,CAAC;IACxC,IAAI,EAAE;QACL,IAAI,EAAE,MAAM;QACZ,WAAW,EAAE,+CAA+C;KAC5D;IACD,IAAI,EAAE,EAAE;IACR,KAAK,CAAC,GAAG;QACR,MAAM,EAAE,GAAG,QAAQ,CAAC,eAAe,CAAC;YACnC,KAAK,EAAE,OAAO,CAAC,KAAK;YACpB,MAAM,EAAE,OAAO,CAAC,MAAM;SACtB,CAAC,CAAC;QAEH,MAAM,QAAQ,GAAG,CAAC,MAAc,EAAmB,EAAE;YACpD,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;gBAC9B,EAAE,CAAC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;YAC9B,CAAC,CAAC,CAAC;QACJ,CAAC,CAAC;QAEF,IAAI,CAAC;YACJ,mBAAmB;YACnB,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,sBAAsB,CAAC,CAAC;YACpD,MAAM,WAAW,GAAG,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAE5C,IAAI,CAAC,WAAW,EAAE,CAAC;gBAClB,OAAO,CAAC,KAAK,CAAC,mFAAmF,CAAC,CAAC;gBACnG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;YACjB,CAAC;YAED,oBAAoB;YACpB,IAAI,IAAI,GAAG,EAAE,CAAC;YACd,MAAM,iBAAiB,GAAG,YAAY,EAAE,CAAC;YAEzC,OAAO,CAAC,IAAI,EAAE,CAAC;gBACd,MAAM,SAAS,GAAG,MAAM,QAAQ,CAAC,0BAA0B,CAAC,CAAC;gBAC7D,MAAM,WAAW,GAAG,SAAS,CAAC,IAAI,EAAE,CAAC;gBAErC,IAAI,CAAC,WAAW,EAAE,CAAC;oBAClB,OAAO,CAAC,KAAK,CAAC,uBAAuB,CAAC,CAAC;oBACvC,SAAS;gBACV,CAAC;gBAED,2BAA2B;gBAC3B,IAAI,iBAAiB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,WAAW,CAAC,EAAE,CAAC;oBACzD,OAAO,CAAC,KAAK,CAAC,uBAAuB,WAAW,4CAA4C,CAAC,CAAC;oBAC9F,SAAS;gBACV,CAAC;gBAED,IAAI,GAAG,WAAW,CAAC;YACpB,CAAC;YAED,yBAAyB;YACzB,MAAM,QAAQ,GAAmB;gBAChC,IAAI;gBACJ,QAAQ,EAAE,WAAW,CAAC,QAAQ;gBAC9B,IAAI,EAAE,WAAW,CAAC,IAAI;gBACtB,KAAK,EAAE,WAAW,CAAC,KAAK;gBACxB,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;gBACjC,UAAU,EAAE,IAAI;aAChB,CAAC;YAEF,gBAAgB;YAChB,WAAW,CAAC,QAAQ,CAAC,CAAC;YAEtB,MAAM,cAAc,GAAG,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC;YACpD,OAAO,CAAC,GAAG,CAAC,iBAAiB,IAAI,sBAAsB,CAAC,CAAC;YACzD,OAAO,CAAC,GAAG,CAAC,kBAAkB,cAAc,0BAA0B,CAAC,CAAC;QACzE,CAAC;gBACO,CAAC;YACR,EAAE,CAAC,KAAK,EAAE,CAAC;QACZ,CAAC;IACF,CAAC;CACD,CAAC,CAAC"}
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export declare const pushCommand: import("citty").CommandDef<{
|
|
2
|
+
readonly message: {
|
|
3
|
+
readonly type: "positional";
|
|
4
|
+
readonly description: "The notification body text (use '-' to read from stdin)";
|
|
5
|
+
readonly required: true;
|
|
6
|
+
};
|
|
7
|
+
readonly title: {
|
|
8
|
+
readonly type: "string";
|
|
9
|
+
readonly alias: "t";
|
|
10
|
+
readonly description: "Notification title";
|
|
11
|
+
readonly default: "nudj";
|
|
12
|
+
};
|
|
13
|
+
readonly to: {
|
|
14
|
+
readonly type: "string";
|
|
15
|
+
readonly description: "Send only to this receiver (repeat for multiple)";
|
|
16
|
+
};
|
|
17
|
+
readonly quiet: {
|
|
18
|
+
readonly type: "boolean";
|
|
19
|
+
readonly alias: "q";
|
|
20
|
+
readonly description: "Suppress output on success";
|
|
21
|
+
readonly default: false;
|
|
22
|
+
};
|
|
23
|
+
}>;
|
|
24
|
+
//# sourceMappingURL=push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/push.ts"],"names":[],"mappings":"AAMA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;EAwFtB,CAAC"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { defineCommand } from "citty";
|
|
2
|
+
import { text } from "node:stream/consumers";
|
|
3
|
+
import { getReceivers, updateReceiverLastUsed, removeReceiver } from "../lib/config.js";
|
|
4
|
+
import { sendPush } from "../lib/push.js";
|
|
5
|
+
export const pushCommand = defineCommand({
|
|
6
|
+
meta: {
|
|
7
|
+
name: "push",
|
|
8
|
+
description: "Send a push notification to receivers",
|
|
9
|
+
},
|
|
10
|
+
args: {
|
|
11
|
+
message: {
|
|
12
|
+
type: "positional",
|
|
13
|
+
description: "The notification body text (use '-' to read from stdin)",
|
|
14
|
+
required: true,
|
|
15
|
+
},
|
|
16
|
+
title: {
|
|
17
|
+
type: "string",
|
|
18
|
+
alias: "t",
|
|
19
|
+
description: "Notification title",
|
|
20
|
+
default: "nudj",
|
|
21
|
+
},
|
|
22
|
+
to: {
|
|
23
|
+
type: "string",
|
|
24
|
+
// Can be repeated to send to multiple specific receivers, e.g.:
|
|
25
|
+
// nudj push --to iPhone --to iPad 'Meeting starting'
|
|
26
|
+
description: "Send only to this receiver (repeat for multiple)",
|
|
27
|
+
},
|
|
28
|
+
quiet: {
|
|
29
|
+
type: "boolean",
|
|
30
|
+
alias: "q",
|
|
31
|
+
description: "Suppress output on success",
|
|
32
|
+
default: false,
|
|
33
|
+
},
|
|
34
|
+
},
|
|
35
|
+
async run({ args }) {
|
|
36
|
+
// Get message (handle stdin)
|
|
37
|
+
let message = args.message;
|
|
38
|
+
if (message === "-")
|
|
39
|
+
message = (await text(process.stdin)).trimEnd();
|
|
40
|
+
// Get receivers
|
|
41
|
+
const allReceivers = getReceivers();
|
|
42
|
+
if (allReceivers.length === 0) {
|
|
43
|
+
console.error("No receivers configured. Run 'nudj pair' to add one.");
|
|
44
|
+
process.exit(2);
|
|
45
|
+
}
|
|
46
|
+
// Filter receivers if --to is specified
|
|
47
|
+
let targetReceivers = allReceivers;
|
|
48
|
+
if (args.to) {
|
|
49
|
+
// Handle both single and multiple --to values
|
|
50
|
+
const targetNames = Array.isArray(args.to) ? args.to : [args.to];
|
|
51
|
+
targetReceivers = allReceivers.filter(r => targetNames.includes(r.name));
|
|
52
|
+
if (targetReceivers.length === 0) {
|
|
53
|
+
const names = targetNames.join(", ");
|
|
54
|
+
console.error(`No receivers found matching: ${names}`);
|
|
55
|
+
console.error("Run 'nudj receivers' to see available receivers.");
|
|
56
|
+
process.exit(2);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Create payload
|
|
60
|
+
const payload = {
|
|
61
|
+
title: args.title,
|
|
62
|
+
body: message,
|
|
63
|
+
timestamp: Date.now(),
|
|
64
|
+
};
|
|
65
|
+
// Send notifications, processing each result as it completes
|
|
66
|
+
let hasFailure = false;
|
|
67
|
+
await Promise.all(targetReceivers.map(async (receiver) => {
|
|
68
|
+
const result = await sendPush(receiver, payload);
|
|
69
|
+
if (result.success) {
|
|
70
|
+
updateReceiverLastUsed(result.name);
|
|
71
|
+
if (!args.quiet)
|
|
72
|
+
console.log(`✓ ${result.name}: delivered`);
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
hasFailure = true;
|
|
76
|
+
if (result.expired) {
|
|
77
|
+
removeReceiver(result.name);
|
|
78
|
+
console.error(`✗ ${result.name}: subscription expired (removed)`);
|
|
79
|
+
}
|
|
80
|
+
else
|
|
81
|
+
console.error(`✗ ${result.name}: ${result.error}`);
|
|
82
|
+
}
|
|
83
|
+
}));
|
|
84
|
+
if (hasFailure)
|
|
85
|
+
process.exit(1);
|
|
86
|
+
},
|
|
87
|
+
});
|
|
88
|
+
//# sourceMappingURL=push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"push.js","sourceRoot":"","sources":["../../../src/cli/commands/push.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"receivers.d.ts","sourceRoot":"","sources":["../../../src/cli/commands/receivers.ts"],"names":[],"mappings":"AA8FA,eAAO,MAAM,gBAAgB;;;;;;EAgD3B,CAAC"}
|