@codigoconelmer/driftwatch 1.1.4 → 1.2.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/README.md +112 -78
- package/dist/checker.d.ts +2 -0
- package/dist/checker.d.ts.map +1 -1
- package/dist/checker.js +66 -26
- package/dist/checker.js.map +1 -1
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +11 -0
- package/dist/config.js.map +1 -1
- package/dist/daemon.d.ts +7 -0
- package/dist/daemon.d.ts.map +1 -0
- package/dist/daemon.js +50 -0
- package/dist/daemon.js.map +1 -0
- package/dist/history.d.ts +10 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +28 -0
- package/dist/history.js.map +1 -0
- package/dist/index.js +109 -21
- package/dist/index.js.map +1 -1
- package/dist/notifier.d.ts +6 -0
- package/dist/notifier.d.ts.map +1 -0
- package/dist/notifier.js +67 -0
- package/dist/notifier.js.map +1 -0
- package/dist/scheduler.d.ts.map +1 -1
- package/dist/scheduler.js +20 -4
- package/dist/scheduler.js.map +1 -1
- package/dist/snapshot.d.ts +1 -0
- package/dist/snapshot.d.ts.map +1 -1
- package/dist/snapshot.js +7 -0
- package/dist/snapshot.js.map +1 -1
- package/dist/status.d.ts +15 -0
- package/dist/status.d.ts.map +1 -0
- package/dist/status.js +43 -0
- package/dist/status.js.map +1 -0
- package/dist/telegram.d.ts +2 -0
- package/dist/telegram.d.ts.map +1 -1
- package/dist/telegram.js +12 -0
- package/dist/telegram.js.map +1 -1
- package/dist/types.d.ts +16 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/webui.d.ts +2 -0
- package/dist/webui.d.ts.map +1 -0
- package/dist/webui.js +115 -0
- package/dist/webui.js.map +1 -0
- package/package.json +7 -13
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# DriftWatch
|
|
2
2
|
|
|
3
|
-
External API schema drift detector
|
|
3
|
+
External API schema drift detector. Monitors HTTP endpoints and alerts you when the response shape changes — keys added, removed, or type changed. Runs as a standalone daemon or Docker container, no SDK to install in your apps.
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@codigoconelmer/driftwatch)
|
|
6
6
|
[](LICENSE)
|
|
@@ -10,7 +10,7 @@ External API schema drift detector with Telegram alerts. No SDK to install in yo
|
|
|
10
10
|
|
|
11
11
|
## What it does
|
|
12
12
|
|
|
13
|
-
DriftWatch periodically hits your API endpoints and extracts their response **schema** — keys and types, not values. When the schema changes
|
|
13
|
+
DriftWatch periodically hits your API endpoints and extracts their response **schema** — keys and types, not values. When the schema changes, it fires an alert via Telegram, Slack, or Discord instantly.
|
|
14
14
|
|
|
15
15
|
```
|
|
16
16
|
⚠️ Schema drift detected!
|
|
@@ -37,42 +37,14 @@ pnpm add -g @codigoconelmer/driftwatch
|
|
|
37
37
|
|
|
38
38
|
## Quick start
|
|
39
39
|
|
|
40
|
-
**1.
|
|
40
|
+
**1. Interactive setup**
|
|
41
41
|
|
|
42
42
|
```bash
|
|
43
43
|
cd my-project/
|
|
44
44
|
driftwatch init
|
|
45
45
|
```
|
|
46
46
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
```
|
|
50
|
-
── Endpoint setup ──────────────────────────
|
|
51
|
-
|
|
52
|
-
? Endpoint name: Cards
|
|
53
|
-
? URL: https://api.myapp.com/api/cards
|
|
54
|
-
? HTTP method: GET
|
|
55
|
-
? Requires auth header (Bearer token)? Yes
|
|
56
|
-
? Env var name for token: API_TOKEN
|
|
57
|
-
? Check interval: Every 5 minutes
|
|
58
|
-
? Add another endpoint? No
|
|
59
|
-
|
|
60
|
-
── Telegram alerts (optional) ──────────────
|
|
61
|
-
|
|
62
|
-
? Set up Telegram alerts now? Yes
|
|
63
|
-
? Bot token: ********
|
|
64
|
-
? Chat ID: 12345678
|
|
65
|
-
|
|
66
|
-
✓ Created driftwatch.config.yml
|
|
67
|
-
✓ Updated .env
|
|
68
|
-
✓ Updated .env.example
|
|
69
|
-
|
|
70
|
-
Run "driftwatch check" to create your first snapshot.
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
> Telegram is optional — skip it during init and add it later by setting `TELEGRAM_TOKEN` and `CHAT_ID` in `.env`. Without it, drift is logged to console only.
|
|
74
|
-
|
|
75
|
-
**2. First run** — creates snapshots, no alerts sent
|
|
47
|
+
**2. Create baseline snapshots** (first run, no alerts sent)
|
|
76
48
|
|
|
77
49
|
```bash
|
|
78
50
|
driftwatch check
|
|
@@ -81,7 +53,8 @@ driftwatch check
|
|
|
81
53
|
**3. Start the daemon**
|
|
82
54
|
|
|
83
55
|
```bash
|
|
84
|
-
driftwatch start
|
|
56
|
+
driftwatch start # foreground
|
|
57
|
+
driftwatch start --daemon # background
|
|
85
58
|
```
|
|
86
59
|
|
|
87
60
|
---
|
|
@@ -89,9 +62,18 @@ driftwatch start
|
|
|
89
62
|
## Config reference
|
|
90
63
|
|
|
91
64
|
```yaml
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
65
|
+
# Alert channels (all optional — pick one or more)
|
|
66
|
+
alerts:
|
|
67
|
+
telegram:
|
|
68
|
+
bot_token: '${TELEGRAM_TOKEN}'
|
|
69
|
+
chat_id: '${CHAT_ID}'
|
|
70
|
+
slack:
|
|
71
|
+
webhook_url: '${SLACK_WEBHOOK}'
|
|
72
|
+
discord:
|
|
73
|
+
webhook_url: '${DISCORD_WEBHOOK}'
|
|
74
|
+
|
|
75
|
+
# Global cooldown: minimum minutes between repeated alerts per endpoint
|
|
76
|
+
alert_cooldown: 30
|
|
95
77
|
|
|
96
78
|
endpoints:
|
|
97
79
|
- name: 'Cards'
|
|
@@ -101,6 +83,11 @@ endpoints:
|
|
|
101
83
|
Authorization: 'Bearer ${API_TOKEN}'
|
|
102
84
|
Accept: 'application/json'
|
|
103
85
|
interval: '*/5 * * * *'
|
|
86
|
+
retries: 3 # retry on 5xx/timeout before alerting (default: 0)
|
|
87
|
+
retry_delay: 10 # seconds between retries (default: 5)
|
|
88
|
+
ignore_fields: # field names to exclude from drift detection
|
|
89
|
+
- updated_at
|
|
90
|
+
- expires_at
|
|
104
91
|
|
|
105
92
|
- name: 'Create order'
|
|
106
93
|
url: 'https://api.myapp.com/api/orders'
|
|
@@ -114,6 +101,12 @@ endpoints:
|
|
|
114
101
|
interval: '0 * * * *'
|
|
115
102
|
```
|
|
116
103
|
|
|
104
|
+
`${VAR}` values are resolved from `.env` or environment variables.
|
|
105
|
+
|
|
106
|
+
> **Backwards compat:** The old top-level `telegram:` block still works — it's automatically normalized to `alerts.telegram` on load.
|
|
107
|
+
|
|
108
|
+
### Endpoint fields
|
|
109
|
+
|
|
117
110
|
| Field | Required | Description |
|
|
118
111
|
|---|---|---|
|
|
119
112
|
| `name` | yes | Display name used in alerts and snapshot filenames |
|
|
@@ -121,28 +114,46 @@ endpoints:
|
|
|
121
114
|
| `method` | no | HTTP method, defaults to `GET` |
|
|
122
115
|
| `headers` | no | Key/value headers sent with every request |
|
|
123
116
|
| `body` | no | JSON body for POST/PUT/PATCH requests |
|
|
124
|
-
| `interval` | yes | Cron expression (e.g. `*/5 * * * *`
|
|
117
|
+
| `interval` | yes | Cron expression (e.g. `*/5 * * * *`) |
|
|
118
|
+
| `retries` | no | Retry attempts on 5xx/timeout before alerting (default: 0) |
|
|
119
|
+
| `retry_delay` | no | Seconds between retries (default: 5) |
|
|
120
|
+
| `ignore_fields` | no | Field names to skip in drift detection |
|
|
125
121
|
|
|
126
|
-
|
|
122
|
+
### Global fields
|
|
123
|
+
|
|
124
|
+
| Field | Description |
|
|
125
|
+
|---|---|
|
|
126
|
+
| `alert_cooldown` | Minutes between repeated alerts per endpoint. Accepts `30`, `"30m"`, or `"2h"` |
|
|
127
|
+
| `alerts.telegram` | Telegram bot token + chat ID |
|
|
128
|
+
| `alerts.slack` | Slack incoming webhook URL |
|
|
129
|
+
| `alerts.discord` | Discord webhook URL |
|
|
127
130
|
|
|
128
131
|
---
|
|
129
132
|
|
|
130
133
|
## CLI
|
|
131
134
|
|
|
132
135
|
```bash
|
|
133
|
-
driftwatch init
|
|
134
|
-
driftwatch
|
|
135
|
-
driftwatch
|
|
136
|
-
driftwatch check
|
|
137
|
-
driftwatch
|
|
136
|
+
driftwatch init # interactive setup wizard
|
|
137
|
+
driftwatch check # one-shot check all endpoints
|
|
138
|
+
driftwatch check -e "Cards" # check a single endpoint by name
|
|
139
|
+
driftwatch check -c /path/to/cfg.yml # use custom config path
|
|
140
|
+
driftwatch start # start cron daemon (foreground)
|
|
141
|
+
driftwatch start --daemon # start as background process
|
|
142
|
+
driftwatch stop # stop the background daemon
|
|
143
|
+
driftwatch status # show daemon state + last check per endpoint
|
|
144
|
+
driftwatch reset "Cards" # delete snapshot to force re-baseline
|
|
145
|
+
driftwatch ui # open web dashboard at localhost:4573
|
|
146
|
+
driftwatch ui --port 8080 # custom port
|
|
138
147
|
```
|
|
139
148
|
|
|
140
149
|
---
|
|
141
150
|
|
|
142
|
-
##
|
|
151
|
+
## Alert channels
|
|
152
|
+
|
|
153
|
+
### Telegram
|
|
143
154
|
|
|
144
|
-
1. Open Telegram → search `@BotFather` →
|
|
145
|
-
2. Send any message to your
|
|
155
|
+
1. Open Telegram → search `@BotFather` → `/newbot` → copy the token
|
|
156
|
+
2. Send any message to your bot
|
|
146
157
|
3. Open `https://api.telegram.org/bot<YOUR_TOKEN>/getUpdates` in browser
|
|
147
158
|
4. Copy the `chat.id` from the JSON response
|
|
148
159
|
5. Add to `.env`:
|
|
@@ -152,7 +163,50 @@ TELEGRAM_TOKEN=your-bot-token
|
|
|
152
163
|
CHAT_ID=your-chat-id
|
|
153
164
|
```
|
|
154
165
|
|
|
155
|
-
|
|
166
|
+
### Slack
|
|
167
|
+
|
|
168
|
+
1. Go to [api.slack.com/apps](https://api.slack.com/apps) → Create App → Incoming Webhooks → Activate → Add Webhook
|
|
169
|
+
2. Copy the webhook URL
|
|
170
|
+
3. Add to `.env`: `SLACK_WEBHOOK=https://hooks.slack.com/services/...`
|
|
171
|
+
|
|
172
|
+
### Discord
|
|
173
|
+
|
|
174
|
+
1. Server Settings → Integrations → Webhooks → New Webhook → Copy URL
|
|
175
|
+
2. Add to `.env`: `DISCORD_WEBHOOK=https://discord.com/api/webhooks/...`
|
|
176
|
+
|
|
177
|
+
Without any alert channel configured, drift is logged to console only.
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Daemon mode
|
|
182
|
+
|
|
183
|
+
```bash
|
|
184
|
+
driftwatch start --daemon # forks to background, writes PID to .driftwatch/driftwatch.pid
|
|
185
|
+
# logs go to .driftwatch/driftwatch.log
|
|
186
|
+
driftwatch stop # kills daemon by PID
|
|
187
|
+
driftwatch status # shows running state + last result per endpoint
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
Example `status` output:
|
|
191
|
+
|
|
192
|
+
```
|
|
193
|
+
Daemon: running (PID 12345)
|
|
194
|
+
|
|
195
|
+
✅ Cards — ok — 6/1/2026, 10:30:00 AM
|
|
196
|
+
⚠️ Orders — drift — 6/1/2026, 09:15:00 AM
|
|
197
|
+
🔴 Login — down (503 Service Unavailable) — 6/1/2026, 10:29:00 AM
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Web UI
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
driftwatch ui
|
|
206
|
+
# → http://localhost:4573
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Local dashboard showing endpoint status and full drift history. Refreshes automatically every 30 seconds. No external dependencies.
|
|
156
210
|
|
|
157
211
|
---
|
|
158
212
|
|
|
@@ -160,53 +214,33 @@ Without Telegram configured, drift is logged to console only — no alerts sent.
|
|
|
160
214
|
|
|
161
215
|
1. **First run per endpoint** — saves the response schema to `.driftwatch/snapshots/<name>.json`. No alert sent.
|
|
162
216
|
2. **Subsequent runs** — compares live schema against the snapshot.
|
|
163
|
-
- No change →
|
|
164
|
-
- Change detected →
|
|
165
|
-
|
|
166
|
-
|
|
217
|
+
- No change → no alert.
|
|
218
|
+
- Change detected → alert sent (respecting cooldown), snapshot updated, event appended to `.driftwatch/history.json`.
|
|
219
|
+
- 5xx or timeout → "endpoint down" alert sent after exhausting retries.
|
|
220
|
+
3. **Schema** is a recursive key+type map. Values are ignored. Arrays are sampled from the first element.
|
|
221
|
+
4. **Auth** — supports Bearer token and any custom headers. Cookie/session auth is not supported — use stateless tokens.
|
|
167
222
|
|
|
168
223
|
---
|
|
169
224
|
|
|
170
|
-
## Docker
|
|
225
|
+
## Docker
|
|
171
226
|
|
|
172
227
|
```bash
|
|
173
228
|
cp .env.example .env
|
|
174
229
|
# fill in your tokens
|
|
175
230
|
|
|
176
231
|
docker compose up -d
|
|
177
|
-
```
|
|
178
|
-
|
|
179
|
-
Snapshots persist in a named Docker volume (`driftwatch-snapshots`) so they survive container restarts.
|
|
180
|
-
|
|
181
|
-
To view logs:
|
|
182
|
-
```bash
|
|
183
232
|
docker compose logs -f
|
|
184
233
|
```
|
|
185
234
|
|
|
235
|
+
Snapshots persist in a named Docker volume (`driftwatch-snapshots`) and survive container restarts.
|
|
236
|
+
|
|
186
237
|
---
|
|
187
238
|
|
|
188
239
|
## Snapshots
|
|
189
240
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
```json
|
|
194
|
-
{
|
|
195
|
-
"endpoint": "Cards",
|
|
196
|
-
"url": "https://api.myapp.com/api/cards",
|
|
197
|
-
"capturedAt": "2026-01-15T10:30:00.000Z",
|
|
198
|
-
"schema": {
|
|
199
|
-
"status": "boolean",
|
|
200
|
-
"data": {
|
|
201
|
-
"[]": {
|
|
202
|
-
"id": "string",
|
|
203
|
-
"name": "string",
|
|
204
|
-
"email": "string"
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
}
|
|
208
|
-
}
|
|
209
|
-
```
|
|
241
|
+
Stored in `.driftwatch/snapshots/` as JSON files named after each endpoint (slugified). Gitignored by default — each environment builds its own baseline on first run.
|
|
242
|
+
|
|
243
|
+
Use `driftwatch reset "Endpoint Name"` to delete a snapshot and force re-baseline on the next check.
|
|
210
244
|
|
|
211
245
|
---
|
|
212
246
|
|
package/dist/checker.d.ts
CHANGED
package/dist/checker.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAgB,UAAU,EAA2B,MAAM,YAAY,CAAC;AAIjH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,WAAW,CAiBpE;AAkBD,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAuB5E;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,cAAc,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,UAAU,CAAC;CAClB;
|
|
1
|
+
{"version":3,"file":"checker.d.ts","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,cAAc,EAAE,WAAW,EAAgB,UAAU,EAA2B,MAAM,YAAY,CAAC;AAIjH,wBAAgB,aAAa,CAAC,KAAK,EAAE,OAAO,EAAE,KAAK,SAAI,GAAG,WAAW,CAiBpE;AAkBD,wBAAgB,WAAW,CAAC,IAAI,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,GAAG,UAAU,CAuB5E;AAED,MAAM,WAAW,WAAW;IAC1B,QAAQ,EAAE,cAAc,CAAC;IACzB,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,OAAO,CAAC;IAClB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,EAAE,UAAU,CAAC;CAClB;AAoBD,wBAAsB,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAsDlF"}
|
package/dist/checker.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import axios from 'axios';
|
|
1
|
+
import axios, { isAxiosError } from 'axios';
|
|
2
2
|
import { readSnapshot, writeSnapshot } from './snapshot.js';
|
|
3
3
|
const MAX_DEPTH = 20;
|
|
4
4
|
export function extractSchema(value, depth = 0) {
|
|
@@ -58,32 +58,72 @@ export function diffSchemas(prev, curr) {
|
|
|
58
58
|
}
|
|
59
59
|
return { added, removed, changed };
|
|
60
60
|
}
|
|
61
|
+
function applyIgnoreFields(diff, ignoreFields) {
|
|
62
|
+
if (!ignoreFields.length)
|
|
63
|
+
return diff;
|
|
64
|
+
const ignored = new Set(ignoreFields);
|
|
65
|
+
const isIgnored = (p) => {
|
|
66
|
+
const last = p.split('.').pop() ?? p;
|
|
67
|
+
return ignored.has(last) || ignored.has(p);
|
|
68
|
+
};
|
|
69
|
+
return {
|
|
70
|
+
added: diff.added.filter(e => !isIgnored(e.path)),
|
|
71
|
+
removed: diff.removed.filter(e => !isIgnored(e.path)),
|
|
72
|
+
changed: diff.changed.filter(e => !isIgnored(e.path)),
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
function sleep(ms) {
|
|
76
|
+
return new Promise(resolve => setTimeout(resolve, ms));
|
|
77
|
+
}
|
|
61
78
|
export async function checkEndpoint(endpoint) {
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
79
|
+
const maxAttempts = (endpoint.retries ?? 0) + 1;
|
|
80
|
+
const retryDelayMs = (endpoint.retry_delay ?? 5) * 1000;
|
|
81
|
+
const emptyDiff = { added: [], removed: [], changed: [] };
|
|
82
|
+
let downReason;
|
|
83
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
84
|
+
try {
|
|
85
|
+
const response = await axios({
|
|
86
|
+
method: endpoint.method,
|
|
87
|
+
url: endpoint.url,
|
|
88
|
+
headers: endpoint.headers ?? {},
|
|
89
|
+
data: endpoint.body,
|
|
90
|
+
timeout: 15000,
|
|
91
|
+
maxContentLength: 5 * 1024 * 1024,
|
|
92
|
+
maxBodyLength: 1 * 1024 * 1024,
|
|
93
|
+
});
|
|
94
|
+
const schema = extractSchema(response.data);
|
|
95
|
+
const snapshot = readSnapshot(endpoint.name);
|
|
96
|
+
if (!snapshot) {
|
|
97
|
+
writeSnapshot(endpoint.name, endpoint.url, schema);
|
|
98
|
+
return { endpoint, isFirstRun: true, hasDrift: false, isDown: false, diff: emptyDiff };
|
|
99
|
+
}
|
|
100
|
+
const rawDiff = diffSchemas(snapshot.schema, schema);
|
|
101
|
+
const diff = applyIgnoreFields(rawDiff, endpoint.ignore_fields ?? []);
|
|
102
|
+
const hasDrift = diff.added.length > 0 || diff.removed.length > 0 || diff.changed.length > 0;
|
|
103
|
+
if (hasDrift)
|
|
104
|
+
writeSnapshot(endpoint.name, endpoint.url, schema);
|
|
105
|
+
return { endpoint, isFirstRun: false, hasDrift, isDown: false, diff };
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
if (isAxiosError(err)) {
|
|
109
|
+
if (err.response && err.response.status >= 500) {
|
|
110
|
+
downReason = `${err.response.status} ${err.response.statusText}`;
|
|
111
|
+
}
|
|
112
|
+
else if (!err.response) {
|
|
113
|
+
downReason = err.message;
|
|
114
|
+
}
|
|
115
|
+
else {
|
|
116
|
+
throw err; // 4xx — not "down", surface to caller
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
else {
|
|
120
|
+
throw err;
|
|
121
|
+
}
|
|
122
|
+
if (attempt < maxAttempts) {
|
|
123
|
+
await sleep(retryDelayMs);
|
|
124
|
+
}
|
|
125
|
+
}
|
|
86
126
|
}
|
|
87
|
-
return { endpoint, isFirstRun: false, hasDrift, diff };
|
|
127
|
+
return { endpoint, isFirstRun: false, hasDrift: false, isDown: true, downReason, diff: emptyDiff };
|
|
88
128
|
}
|
|
89
129
|
//# sourceMappingURL=checker.js.map
|
package/dist/checker.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAiBzC,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAiBzC,wBAAgB,UAAU,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,MAAM,CAmCtD"}
|
package/dist/config.js
CHANGED
|
@@ -34,6 +34,17 @@ export function loadConfig(configPath) {
|
|
|
34
34
|
if (!ep.method)
|
|
35
35
|
ep.method = 'GET';
|
|
36
36
|
}
|
|
37
|
+
// Normalize top-level telegram → alerts.telegram for backwards compat
|
|
38
|
+
if (resolved.telegram && !resolved.alerts?.telegram) {
|
|
39
|
+
resolved.alerts ??= {};
|
|
40
|
+
resolved.alerts.telegram = resolved.telegram;
|
|
41
|
+
}
|
|
42
|
+
// Parse alert_cooldown: support "30m", "2h", or plain number (minutes)
|
|
43
|
+
const cooldownRaw = resolved['alert_cooldown'];
|
|
44
|
+
if (typeof cooldownRaw === 'string') {
|
|
45
|
+
const match = cooldownRaw.match(/^(\d+)(m|h)?$/);
|
|
46
|
+
resolved.alert_cooldown = match ? parseInt(match[1], 10) * (match[2] === 'h' ? 60 : 1) : undefined;
|
|
47
|
+
}
|
|
37
48
|
return resolved;
|
|
38
49
|
}
|
|
39
50
|
//# sourceMappingURL=config.js.map
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,MAAM,MAAM,QAAQ,CAAC;AAG5B,MAAM,CAAC,MAAM,EAAE,CAAC;AAEhB,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,KAAK,CAAC,OAAO,CAAC,gBAAgB,EAAE,CAAC,CAAC,EAAE,IAAY,EAAE,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACvF,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAC3D,IAAI,KAAK,KAAK,IAAI,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAChD,OAAO,MAAM,CAAC,WAAW,CACvB,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,cAAc,CAAC,CAAC,CAAC,CAAC,CAAC,CACzF,CAAC;IACJ,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,UAAmB;IAC5C,MAAM,QAAQ,GAAG,UAAU,IAAI,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,uBAAuB,CAAC,CAAC;IAEpF,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,qBAAqB,QAAQ,wCAAwC,CAAC,CAAC;IACzF,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,CAAC;IACzD,MAAM,QAAQ,GAAG,cAAc,CAAC,GAAG,CAAW,CAAC;IAE/C,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,QAAQ,CAAC,SAAS,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1E,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;IAC5D,CAAC;IAED,KAAK,MAAM,EAAE,IAAI,QAAQ,CAAC,SAAS,EAAE,CAAC;QACpC,IAAI,CAAC,EAAE,CAAC,IAAI;YAAE,MAAM,IAAI,KAAK,CAAC,yBAAyB,CAAC,CAAC;QACzD,IAAI,CAAC,EAAE,CAAC,GAAG;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC,IAAI,iBAAiB,CAAC,CAAC;QACpE,IAAI,CAAC,EAAE,CAAC,QAAQ;YAAE,MAAM,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC,IAAI,sBAAsB,CAAC,CAAC;QAC9E,IAAI,CAAC,EAAE,CAAC,MAAM;YAAE,EAAE,CAAC,MAAM,GAAG,KAAK,CAAC;IACpC,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/daemon.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.d.ts","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAQA,wBAAgB,WAAW,CAAC,UAAU,CAAC,EAAE,MAAM,GAAG,IAAI,CAiBrD;AAED,wBAAgB,UAAU,IAAI,IAAI,CAcjC;AAED,wBAAgB,eAAe,IAAI;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,GAAG,CAAC,EAAE,MAAM,CAAA;CAAE,CASpE"}
|
package/dist/daemon.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import { spawn } from 'child_process';
|
|
4
|
+
const DRIFTWATCH_DIR = path.resolve(process.cwd(), '.driftwatch');
|
|
5
|
+
const PID_FILE = path.join(DRIFTWATCH_DIR, 'driftwatch.pid');
|
|
6
|
+
const LOG_FILE = path.join(DRIFTWATCH_DIR, 'driftwatch.log');
|
|
7
|
+
export function startDaemon(configPath) {
|
|
8
|
+
const args = ['start'];
|
|
9
|
+
if (configPath)
|
|
10
|
+
args.push('-c', configPath);
|
|
11
|
+
fs.mkdirSync(DRIFTWATCH_DIR, { recursive: true });
|
|
12
|
+
const out = fs.openSync(LOG_FILE, 'a');
|
|
13
|
+
const child = spawn(process.execPath, [process.argv[1], ...args], {
|
|
14
|
+
detached: true,
|
|
15
|
+
stdio: ['ignore', out, out],
|
|
16
|
+
});
|
|
17
|
+
fs.writeFileSync(PID_FILE, String(child.pid));
|
|
18
|
+
child.unref();
|
|
19
|
+
console.log(`[driftwatch] Daemon started (PID ${child.pid})`);
|
|
20
|
+
console.log(`[driftwatch] Logs: ${LOG_FILE}`);
|
|
21
|
+
}
|
|
22
|
+
export function stopDaemon() {
|
|
23
|
+
if (!fs.existsSync(PID_FILE)) {
|
|
24
|
+
console.log('[driftwatch] No daemon running (no PID file found).');
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
|
|
28
|
+
try {
|
|
29
|
+
process.kill(pid, 'SIGTERM');
|
|
30
|
+
fs.unlinkSync(PID_FILE);
|
|
31
|
+
console.log(`[driftwatch] Daemon stopped (PID ${pid}).`);
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
console.log(`[driftwatch] Process ${pid} not found — cleaning up PID file.`);
|
|
35
|
+
fs.unlinkSync(PID_FILE);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
export function isDaemonRunning() {
|
|
39
|
+
if (!fs.existsSync(PID_FILE))
|
|
40
|
+
return { running: false };
|
|
41
|
+
const pid = parseInt(fs.readFileSync(PID_FILE, 'utf8').trim(), 10);
|
|
42
|
+
try {
|
|
43
|
+
process.kill(pid, 0);
|
|
44
|
+
return { running: true, pid };
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return { running: false, pid };
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=daemon.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"daemon.js","sourceRoot":"","sources":["../src/daemon.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AACxB,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEtC,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,CAAC,CAAC;AAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;AAC7D,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAC,cAAc,EAAE,gBAAgB,CAAC,CAAC;AAE7D,MAAM,UAAU,WAAW,CAAC,UAAmB;IAC7C,MAAM,IAAI,GAAG,CAAC,OAAO,CAAC,CAAC;IACvB,IAAI,UAAU;QAAE,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;IAE5C,EAAE,CAAC,SAAS,CAAC,cAAc,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAClD,MAAM,GAAG,GAAG,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC;IAEvC,MAAM,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE;QAChE,QAAQ,EAAE,IAAI;QACd,KAAK,EAAE,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,CAAC;KAC5B,CAAC,CAAC;IAEH,EAAE,CAAC,aAAa,CAAC,QAAQ,EAAE,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAC9C,KAAK,CAAC,KAAK,EAAE,CAAC;IAEd,OAAO,CAAC,GAAG,CAAC,oCAAoC,KAAK,CAAC,GAAG,GAAG,CAAC,CAAC;IAC9D,OAAO,CAAC,GAAG,CAAC,sBAAsB,QAAQ,EAAE,CAAC,CAAC;AAChD,CAAC;AAED,MAAM,UAAU,UAAU;IACxB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC7B,OAAO,CAAC,GAAG,CAAC,qDAAqD,CAAC,CAAC;QACnE,OAAO;IACT,CAAC;IACD,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACnE,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,SAAS,CAAC,CAAC;QAC7B,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;QACxB,OAAO,CAAC,GAAG,CAAC,oCAAoC,GAAG,IAAI,CAAC,CAAC;IAC3D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,GAAG,CAAC,wBAAwB,GAAG,oCAAoC,CAAC,CAAC;QAC7E,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAC1B,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe;IAC7B,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC;IACxD,MAAM,GAAG,GAAG,QAAQ,CAAC,EAAE,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;IACnE,IAAI,CAAC;QACH,OAAO,CAAC,IAAI,CAAC,GAAG,EAAE,CAAC,CAAC,CAAC;QACrB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,CAAC;IAChC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;IACjC,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { DiffResult } from './types.js';
|
|
2
|
+
export interface HistoryEntry {
|
|
3
|
+
timestamp: string;
|
|
4
|
+
endpoint: string;
|
|
5
|
+
url: string;
|
|
6
|
+
diff: DiffResult;
|
|
7
|
+
}
|
|
8
|
+
export declare function appendHistory(endpoint: string, url: string, diff: DiffResult): void;
|
|
9
|
+
export declare function readHistory(): HistoryEntry[];
|
|
10
|
+
//# sourceMappingURL=history.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.d.ts","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAI7C,MAAM,WAAW,YAAY;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,UAAU,CAAC;CAClB;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,UAAU,GAAG,IAAI,CAUnF;AAED,wBAAgB,WAAW,IAAI,YAAY,EAAE,CAG5C"}
|
package/dist/history.js
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
const HISTORY_FILE = path.resolve(process.cwd(), '.driftwatch', 'history.json');
|
|
4
|
+
export function appendHistory(endpoint, url, diff) {
|
|
5
|
+
let entries = [];
|
|
6
|
+
if (fs.existsSync(HISTORY_FILE)) {
|
|
7
|
+
try {
|
|
8
|
+
entries = JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf8'));
|
|
9
|
+
}
|
|
10
|
+
catch { }
|
|
11
|
+
}
|
|
12
|
+
entries.push({ timestamp: new Date().toISOString(), endpoint, url, diff });
|
|
13
|
+
fs.mkdirSync(path.dirname(HISTORY_FILE), { recursive: true });
|
|
14
|
+
const tmp = `${HISTORY_FILE}.tmp`;
|
|
15
|
+
fs.writeFileSync(tmp, JSON.stringify(entries, null, 2));
|
|
16
|
+
fs.renameSync(tmp, HISTORY_FILE);
|
|
17
|
+
}
|
|
18
|
+
export function readHistory() {
|
|
19
|
+
if (!fs.existsSync(HISTORY_FILE))
|
|
20
|
+
return [];
|
|
21
|
+
try {
|
|
22
|
+
return JSON.parse(fs.readFileSync(HISTORY_FILE, 'utf8'));
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
//# sourceMappingURL=history.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"history.js","sourceRoot":"","sources":["../src/history.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AACpB,OAAO,IAAI,MAAM,MAAM,CAAC;AAGxB,MAAM,YAAY,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,aAAa,EAAE,cAAc,CAAC,CAAC;AAShF,MAAM,UAAU,aAAa,CAAC,QAAgB,EAAE,GAAW,EAAE,IAAgB;IAC3E,IAAI,OAAO,GAAmB,EAAE,CAAC;IACjC,IAAI,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAChC,IAAI,CAAC;YAAC,OAAO,GAAG,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;QAAC,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IAC/E,CAAC;IACD,OAAO,CAAC,IAAI,CAAC,EAAE,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE,EAAE,QAAQ,EAAE,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;IAC3E,EAAE,CAAC,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,YAAY,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAC9D,MAAM,GAAG,GAAG,GAAG,YAAY,MAAM,CAAC;IAClC,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;IACxD,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;AACnC,CAAC;AAED,MAAM,UAAU,WAAW;IACzB,IAAI,CAAC,EAAE,CAAC,UAAU,CAAC,YAAY,CAAC;QAAE,OAAO,EAAE,CAAC;IAC5C,IAAI,CAAC;QAAC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAC;IAAC,CAAC;IAAC,MAAM,CAAC;QAAC,OAAO,EAAE,CAAC;IAAC,CAAC;AACxF,CAAC"}
|