@codigoconelmer/driftwatch 1.1.5 → 1.2.1

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # DriftWatch
2
2
 
3
- External API schema drift detector with Telegram alerts. No SDK to install in your apps runs as a standalone daemon or Docker container, pointing at any HTTP endpoint you want to monitor.
3
+ External API schema drift detector. Monitors HTTP endpoints and alerts you when the response shape changeskeys added, removed, or type changed. Runs as a standalone daemon or Docker container, no SDK to install in your apps.
4
4
 
5
5
  [![npm version](https://img.shields.io/npm/v/@codigoconelmer/driftwatch)](https://www.npmjs.com/package/@codigoconelmer/driftwatch)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](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 (keys added, removed, or type changed), it fires a Telegram alert instantly.
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. Run the interactive setup**
40
+ **1. Interactive setup**
41
41
 
42
42
  ```bash
43
43
  cd my-project/
44
44
  driftwatch init
45
45
  ```
46
46
 
47
- The CLI will ask you everything — endpoint URLs, auth tokens, check interval, and optionally Telegram. No manual YAML editing required.
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
- ? Bearer token value (will be saved to .env as 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
- telegram:
93
- bot_token: '${TELEGRAM_TOKEN}'
94
- chat_id: '${CHAT_ID}'
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 * * * *` = every 5 min) |
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
- `${VAR}` values are resolved from `.env` or environment variables.
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 # generate driftwatch.config.yml
134
- driftwatch start # start the daemon (cron-based)
135
- driftwatch start -c /path/to/cfg.yml # custom config path
136
- driftwatch check # one-shot check all endpoints now
137
- driftwatch check -c /path/to/cfg.yml # custom config path
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
- ## Telegram setup (optional)
151
+ ## Alert channels
152
+
153
+ ### Telegram
143
154
 
144
- 1. Open Telegram → search `@BotFather` → send `/newbot` → copy the token
145
- 2. Send any message to your new bot
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,58 @@ TELEGRAM_TOKEN=your-bot-token
152
163
  CHAT_ID=your-chat-id
153
164
  ```
154
165
 
155
- Without Telegram configured, drift is logged to console only — no alerts sent.
166
+ ### Slack
167
+
168
+ 1. Go to [api.slack.com/apps](https://api.slack.com/apps) → **Create an App** → **From scratch**
169
+ 2. Give it a name (e.g. `DriftWatch`) and select your workspace → **Create App**
170
+ 3. In the left menu → **Incoming Webhooks** → toggle **Activate Incoming Webhooks** on
171
+ 4. Scroll down → **Add New Webhook to Workspace** → select the channel → **Allow**
172
+ 5. Copy the webhook URL that appears
173
+ 6. Add to `.env`: `SLACK_WEBHOOK=https://hooks.slack.com/services/...`
174
+
175
+ ### Discord
176
+
177
+ 1. Create a Discord server (or use an existing one)
178
+ 2. Create a text channel for alerts (e.g. `#driftwatch-alerts`)
179
+ 3. Right-click the channel → **Edit Channel** (in Spanish: **Editar canal**)
180
+ 4. **Integrations** → **Webhooks** → **New Webhook** → Copy URL
181
+ 5. Add to `.env`: `DISCORD_WEBHOOK=https://discord.com/api/webhooks/...`
182
+
183
+ > No developer account needed. You must be the server owner or have "Manage Webhooks" permission.
184
+
185
+ Without any alert channel configured, drift is logged to console only.
186
+
187
+ ---
188
+
189
+ ## Daemon mode
190
+
191
+ ```bash
192
+ driftwatch start --daemon # forks to background, writes PID to .driftwatch/driftwatch.pid
193
+ # logs go to .driftwatch/driftwatch.log
194
+ driftwatch stop # kills daemon by PID
195
+ driftwatch status # shows running state + last result per endpoint
196
+ ```
197
+
198
+ Example `status` output:
199
+
200
+ ```
201
+ Daemon: running (PID 12345)
202
+
203
+ ✅ Cards — ok — 6/1/2026, 10:30:00 AM
204
+ ⚠️ Orders — drift — 6/1/2026, 09:15:00 AM
205
+ 🔴 Login — down (503 Service Unavailable) — 6/1/2026, 10:29:00 AM
206
+ ```
207
+
208
+ ---
209
+
210
+ ## Web UI
211
+
212
+ ```bash
213
+ driftwatch ui
214
+ # → http://localhost:4573
215
+ ```
216
+
217
+ Local dashboard showing endpoint status and full drift history. Refreshes automatically every 30 seconds. No external dependencies.
156
218
 
157
219
  ---
158
220
 
@@ -160,53 +222,33 @@ Without Telegram configured, drift is logged to console only — no alerts sent.
160
222
 
161
223
  1. **First run per endpoint** — saves the response schema to `.driftwatch/snapshots/<name>.json`. No alert sent.
162
224
  2. **Subsequent runs** — compares live schema against the snapshot.
163
- - No change → logs "no drift", no alert.
164
- - Change detected → logs drift + sends Telegram alert (if configured), updates snapshot as new baseline.
165
- 3. **Schema** is a recursive key+type map. Values are ignored. Arrays are sampled from the first element — nested object structure is preserved.
166
- 4. **Auth** supports Bearer token and any custom headers. Cookie-based auth (e.g. Sanctum sessions) is not supported use stateless tokens only.
225
+ - No change → no alert.
226
+ - Change detected → alert sent (respecting cooldown), snapshot updated, event appended to `.driftwatch/history.json`.
227
+ - 5xx or timeout "endpoint down" alert sent after exhausting retries.
228
+ 3. **Schema** is a recursive key+type map. Values are ignored. Arrays are sampled from the first element.
229
+ 4. **Auth** — supports Bearer token and any custom headers. Cookie/session auth is not supported — use stateless tokens.
167
230
 
168
231
  ---
169
232
 
170
- ## Docker (self-host on VPS)
233
+ ## Docker
171
234
 
172
235
  ```bash
173
236
  cp .env.example .env
174
237
  # fill in your tokens
175
238
 
176
239
  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
240
  docker compose logs -f
184
241
  ```
185
242
 
243
+ Snapshots persist in a named Docker volume (`driftwatch-snapshots`) and survive container restarts.
244
+
186
245
  ---
187
246
 
188
247
  ## Snapshots
189
248
 
190
- Snapshots are stored in `.driftwatch/snapshots/` as JSON files named after each endpoint. They are **gitignored by default** — each environment (local, staging, prod) should build its own baseline on first run.
191
-
192
- Example snapshot:
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
- ```
249
+ Stored in `.driftwatch/snapshots/` as JSON files named after each endpoint (slugified). Gitignored by default — each environment builds its own baseline on first run.
250
+
251
+ Use `driftwatch reset "Endpoint Name"` to delete a snapshot and force re-baseline on the next check.
210
252
 
211
253
  ---
212
254
 
package/dist/checker.d.ts CHANGED
@@ -5,6 +5,8 @@ export interface CheckResult {
5
5
  endpoint: EndpointConfig;
6
6
  isFirstRun: boolean;
7
7
  hasDrift: boolean;
8
+ isDown: boolean;
9
+ downReason?: string;
8
10
  diff: DiffResult;
9
11
  }
10
12
  export declare function checkEndpoint(endpoint: EndpointConfig): Promise<CheckResult>;
@@ -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;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,cAAc,GAAG,OAAO,CAAC,WAAW,CAAC,CAgClF"}
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 response = await axios({
63
- method: endpoint.method,
64
- url: endpoint.url,
65
- headers: endpoint.headers ?? {},
66
- data: endpoint.body,
67
- timeout: 15000,
68
- maxContentLength: 5 * 1024 * 1024, // 5MB
69
- maxBodyLength: 1 * 1024 * 1024, // 1MB request body
70
- });
71
- const schema = extractSchema(response.data);
72
- const snapshot = readSnapshot(endpoint.name);
73
- if (!snapshot) {
74
- writeSnapshot(endpoint.name, endpoint.url, schema);
75
- return {
76
- endpoint,
77
- isFirstRun: true,
78
- hasDrift: false,
79
- diff: { added: [], removed: [], changed: [] },
80
- };
81
- }
82
- const diff = diffSchemas(snapshot.schema, schema);
83
- const hasDrift = diff.added.length > 0 || diff.removed.length > 0 || diff.changed.length > 0;
84
- if (hasDrift) {
85
- writeSnapshot(endpoint.name, endpoint.url, schema);
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
@@ -1 +1 @@
1
- {"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"checker.js","sourceRoot":"","sources":["../src/checker.ts"],"names":[],"mappings":"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"}
@@ -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,CAsBtD"}
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
@@ -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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"}
@@ -0,0 +1,7 @@
1
+ export declare function startDaemon(configPath?: string): void;
2
+ export declare function stopDaemon(): void;
3
+ export declare function isDaemonRunning(): {
4
+ running: boolean;
5
+ pid?: number;
6
+ };
7
+ //# sourceMappingURL=daemon.d.ts.map
@@ -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"}
@@ -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"}