process-watchdog 1.0.0 → 1.1.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.
Files changed (40) hide show
  1. package/README.md +115 -0
  2. package/dashboard/watchdog.html +406 -0
  3. package/dist/api/routes.d.ts.map +1 -1
  4. package/dist/api/routes.js +45 -0
  5. package/dist/api/routes.js.map +1 -1
  6. package/dist/index.js +5 -5
  7. package/dist/index.js.map +1 -1
  8. package/dist/platform/index.d.ts.map +1 -1
  9. package/dist/platform/index.js +7 -1
  10. package/dist/platform/index.js.map +1 -1
  11. package/dist/platform/linux.d.ts +14 -0
  12. package/dist/platform/linux.d.ts.map +1 -0
  13. package/dist/platform/linux.js +235 -0
  14. package/dist/platform/linux.js.map +1 -0
  15. package/dist/platform/macos.d.ts +14 -0
  16. package/dist/platform/macos.d.ts.map +1 -0
  17. package/dist/platform/macos.js +255 -0
  18. package/dist/platform/macos.js.map +1 -0
  19. package/dist/plugins/plugin-loader.d.ts +1 -1
  20. package/dist/plugins/plugin-loader.d.ts.map +1 -1
  21. package/dist/plugins/plugin-loader.js +46 -1
  22. package/dist/plugins/plugin-loader.js.map +1 -1
  23. package/dist/plugins/process-guard.d.ts.map +1 -1
  24. package/dist/plugins/process-guard.js +0 -15
  25. package/dist/plugins/process-guard.js.map +1 -1
  26. package/package.json +1 -1
  27. package/src/api/routes.ts +56 -0
  28. package/src/index.ts +5 -5
  29. package/src/platform/index.ts +7 -1
  30. package/src/platform/linux.ts +255 -0
  31. package/src/platform/macos.ts +259 -0
  32. package/src/plugins/plugin-loader.ts +66 -1
  33. package/src/plugins/process-guard.ts +0 -15
  34. package/tests/plugins/cpu-monitor.test.ts +5 -5
  35. package/tests/plugins/disk-health.test.ts +1 -1
  36. package/tests/plugins/fixtures/broken-plugin.js +5 -0
  37. package/tests/plugins/fixtures/custom-plugin.js +36 -0
  38. package/tests/plugins/fixtures/named-plugin.js +31 -0
  39. package/tests/plugins/plugin-loader.test.ts +124 -7
  40. package/tests/plugins/process-guard.test.ts +1 -1
package/README.md ADDED
@@ -0,0 +1,115 @@
1
+ # Process Watchdog
2
+
3
+ [![npm version](https://img.shields.io/npm/v/process-watchdog.svg)](https://www.npmjs.com/package/process-watchdog)
4
+
5
+ Modular PC health agent for the aidev.com.au ecosystem.
6
+
7
+ ---
8
+
9
+ ## Features
10
+
11
+ - 5 built-in plugins: `process-guard`, `memory-monitor`, `disk-health`, `startup-optimizer`, `cpu-monitor`
12
+ - REST API on port 3400 (`/api/v1`)
13
+ - `watchdog` CLI tool
14
+ - Windows service support via `node-windows`
15
+ - aidev.com.au dashboard integration (totalRecall, mah)
16
+
17
+ ---
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ npm install -g process-watchdog
23
+
24
+ watchdog status # one-shot health check
25
+ watchdog start # run continuously with scheduler
26
+ ```
27
+
28
+ ---
29
+
30
+ ## CLI Commands
31
+
32
+ | Command | Description |
33
+ |----------------------------|----------------------------------------------------|
34
+ | `watchdog start` | Load config, start scheduler, watch continuously |
35
+ | `watchdog stop` | Stop the running watchdog process |
36
+ | `watchdog status` | Run check() on each plugin and print a summary |
37
+ | `watchdog check [plugin]` | Detailed metrics for all or a specific plugin |
38
+ | `watchdog fix [plugin]` | Run fix() for all or a specific plugin |
39
+ | `watchdog api start` | Start the HTTP REST API server |
40
+ | `watchdog install-service` | Install watchdog as a Windows system service |
41
+ | `watchdog uninstall-service` | Uninstall the Windows system service |
42
+
43
+ ---
44
+
45
+ ## Plugins
46
+
47
+ | Plugin | Description | Default Interval | Auto-Fix |
48
+ |---------------------|----------------------------------------------|-----------------|----------|
49
+ | `process-guard` | Kills runaway node/cmd/bash processes | 5 min | Yes |
50
+ | `memory-monitor` | Alerts on high RAM usage | 2 min | No |
51
+ | `disk-health` | Checks disk usage and cleans temp files | 30 min | Yes |
52
+ | `startup-optimizer` | Audits startup programs | On demand | No |
53
+ | `cpu-monitor` | Alerts on sustained high CPU usage | 2 min | No |
54
+
55
+ ---
56
+
57
+ ## API Endpoints
58
+
59
+ All routes are prefixed with `/api/v1`.
60
+
61
+ | Method | Endpoint | Description |
62
+ |--------|------------------------|------------------------------------------------------|
63
+ | GET | `/health` | Overall service health and per-plugin status |
64
+ | GET | `/plugins` | List all plugins with config and last check time |
65
+ | GET | `/plugins/:name` | Plugin details and last 50 history entries |
66
+ | POST | `/plugins/:name/run` | Run `check` or `fix` on a plugin (`{ action }` body) |
67
+ | GET | `/history` | Check/fix history (query: `plugin`, `limit`) |
68
+ | GET | `/config` | Current active configuration |
69
+
70
+ ---
71
+
72
+ ## Configuration
73
+
74
+ **Default config** — `config/default.json` in the package.
75
+
76
+ **User override** — `~/.aidev/watchdog.json` (merged over defaults at startup).
77
+
78
+ Key options:
79
+
80
+ ```jsonc
81
+ {
82
+ "port": 3400,
83
+ "logLevel": "info",
84
+ "historyRetentionDays": 30,
85
+ "plugins": {
86
+ "process-guard": { "enabled": true, "interval": 300000, "autoFix": true },
87
+ "memory-monitor": { "enabled": true, "interval": 120000, "autoFix": false }
88
+ // ...
89
+ }
90
+ }
91
+ ```
92
+
93
+ History and the SQLite database are stored at `~/.aidev/watchdog.db`.
94
+
95
+ ---
96
+
97
+ ## Dashboard
98
+
99
+ Open `dashboard/watchdog.html` in a browser while the API server is running to view a live health overview compatible with aidev.com.au.
100
+
101
+ ---
102
+
103
+ ## Development
104
+
105
+ ```bash
106
+ npm run build # compile TypeScript → dist/
107
+ npm test # run tests with vitest
108
+ npm run dev # run from source with tsx (no build step)
109
+ ```
110
+
111
+ ---
112
+
113
+ ## License
114
+
115
+ MIT
@@ -372,6 +372,176 @@
372
372
  font-size: 13px;
373
373
  }
374
374
 
375
+ /* ── Config editor ── */
376
+ .config-plugin-block {
377
+ border: 1px solid var(--card-border);
378
+ border-radius: 8px;
379
+ margin-bottom: 14px;
380
+ overflow: hidden;
381
+ }
382
+
383
+ .config-plugin-block:last-of-type { margin-bottom: 0; }
384
+
385
+ .config-plugin-header {
386
+ background: rgba(79,195,247,0.06);
387
+ border-bottom: 1px solid var(--card-border);
388
+ padding: 10px 16px;
389
+ display: flex;
390
+ align-items: center;
391
+ gap: 12px;
392
+ }
393
+
394
+ .config-plugin-name {
395
+ font-weight: 700;
396
+ font-size: 13px;
397
+ flex: 1;
398
+ color: var(--accent);
399
+ font-family: 'Consolas', 'SF Mono', monospace;
400
+ }
401
+
402
+ .config-plugin-body {
403
+ padding: 12px 16px;
404
+ display: grid;
405
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
406
+ gap: 10px 20px;
407
+ }
408
+
409
+ .config-field {
410
+ display: flex;
411
+ flex-direction: column;
412
+ gap: 4px;
413
+ }
414
+
415
+ .config-label {
416
+ font-size: 11px;
417
+ font-weight: 600;
418
+ color: var(--text-muted);
419
+ text-transform: uppercase;
420
+ letter-spacing: 0.06em;
421
+ }
422
+
423
+ .config-input {
424
+ background: var(--bg);
425
+ border: 1px solid var(--card-border);
426
+ border-radius: 5px;
427
+ color: var(--text);
428
+ font-size: 13px;
429
+ padding: 5px 9px;
430
+ width: 100%;
431
+ transition: border-color 0.15s;
432
+ font-family: inherit;
433
+ }
434
+
435
+ .config-input:focus {
436
+ outline: none;
437
+ border-color: var(--accent);
438
+ }
439
+
440
+ .config-thresholds {
441
+ grid-column: 1 / -1;
442
+ }
443
+
444
+ .config-threshold-row {
445
+ display: flex;
446
+ align-items: center;
447
+ gap: 6px;
448
+ margin-bottom: 6px;
449
+ }
450
+
451
+ .config-threshold-row:last-child { margin-bottom: 0; }
452
+
453
+ .config-threshold-key {
454
+ flex: 1;
455
+ min-width: 0;
456
+ }
457
+
458
+ .config-threshold-val {
459
+ width: 100px;
460
+ flex-shrink: 0;
461
+ }
462
+
463
+ /* Toggle switch */
464
+ .toggle-wrap {
465
+ display: flex;
466
+ align-items: center;
467
+ gap: 8px;
468
+ padding-top: 20px;
469
+ }
470
+
471
+ .toggle {
472
+ position: relative;
473
+ width: 36px;
474
+ height: 20px;
475
+ flex-shrink: 0;
476
+ }
477
+
478
+ .toggle input { opacity: 0; width: 0; height: 0; position: absolute; }
479
+
480
+ .toggle-slider {
481
+ position: absolute;
482
+ inset: 0;
483
+ background: var(--card-border);
484
+ border-radius: 20px;
485
+ cursor: pointer;
486
+ transition: background 0.2s;
487
+ }
488
+
489
+ .toggle-slider::before {
490
+ content: '';
491
+ position: absolute;
492
+ width: 14px;
493
+ height: 14px;
494
+ left: 3px;
495
+ top: 3px;
496
+ background: var(--text-muted);
497
+ border-radius: 50%;
498
+ transition: transform 0.2s, background 0.2s;
499
+ }
500
+
501
+ .toggle input:checked + .toggle-slider { background: rgba(79,195,247,0.25); }
502
+ .toggle input:checked + .toggle-slider::before {
503
+ transform: translateX(16px);
504
+ background: var(--accent);
505
+ }
506
+
507
+ /* Config action bar */
508
+ .config-actions {
509
+ display: flex;
510
+ align-items: center;
511
+ gap: 12px;
512
+ margin-top: 16px;
513
+ }
514
+
515
+ .btn-save {
516
+ background: rgba(79,195,247,0.15);
517
+ border-color: var(--accent);
518
+ color: var(--accent);
519
+ padding: 8px 20px;
520
+ font-size: 13px;
521
+ }
522
+
523
+ .config-feedback {
524
+ font-size: 12px;
525
+ font-weight: 600;
526
+ padding: 4px 10px;
527
+ border-radius: 5px;
528
+ display: none;
529
+ }
530
+
531
+ .config-feedback.success {
532
+ display: inline-block;
533
+ background: rgba(102,187,106,0.15);
534
+ color: var(--healthy);
535
+ border: 1px solid var(--healthy);
536
+ }
537
+
538
+ .config-feedback.error {
539
+ display: inline-block;
540
+ background: rgba(239,83,80,0.15);
541
+ color: var(--critical);
542
+ border: 1px solid var(--critical);
543
+ }
544
+
375
545
  /* ── Footer ── */
376
546
  footer {
377
547
  text-align: center;
@@ -440,6 +610,18 @@
440
610
  </div>
441
611
  </section>
442
612
 
613
+ <section id="config-section">
614
+ <h2>Configuration</h2>
615
+ <div class="card" id="config-card">
616
+ <div class="history-empty" id="config-loading">Loading configuration…</div>
617
+ <div id="config-plugins" style="display:none;"></div>
618
+ <div class="config-actions" id="config-actions" style="display:none;">
619
+ <button class="btn btn-save" id="config-save-btn" type="button">Save Changes</button>
620
+ <span class="config-feedback" id="config-feedback"></span>
621
+ </div>
622
+ </div>
623
+ </section>
624
+
443
625
  </main>
444
626
 
445
627
  <footer>
@@ -843,11 +1025,235 @@
843
1025
  }
844
1026
  }
845
1027
 
1028
+ // ── Config editor ─────────────────────────────────────────────────────────
1029
+
1030
+ // Store the raw config so Save knows what to build from
1031
+ var _loadedConfig = null;
1032
+
1033
+ function buildConfigEditor(config) {
1034
+ _loadedConfig = config;
1035
+ var container = document.getElementById('config-plugins');
1036
+ while (container.firstChild) container.removeChild(container.firstChild);
1037
+
1038
+ var plugins = config.plugins || {};
1039
+
1040
+ Object.keys(plugins).forEach(function (pluginName) {
1041
+ var pcfg = plugins[pluginName];
1042
+
1043
+ var block = el('div', 'config-plugin-block');
1044
+
1045
+ // ── Header: plugin name + enabled toggle ──
1046
+ var header = el('div', 'config-plugin-header');
1047
+
1048
+ var nameEl = el('span', 'config-plugin-name');
1049
+ setText(nameEl, pluginName);
1050
+ header.appendChild(nameEl);
1051
+
1052
+ var enabledLabel = el('span', 'config-label');
1053
+ setText(enabledLabel, 'Enabled');
1054
+ enabledLabel.style.marginRight = '6px';
1055
+
1056
+ var toggleWrap = el('label', 'toggle');
1057
+ var toggleInput = el('input');
1058
+ toggleInput.type = 'checkbox';
1059
+ toggleInput.id = 'cfg-enabled-' + pluginName;
1060
+ toggleInput.checked = !!pcfg.enabled;
1061
+ var toggleSlider = el('span', 'toggle-slider');
1062
+ toggleWrap.appendChild(toggleInput);
1063
+ toggleWrap.appendChild(toggleSlider);
1064
+
1065
+ header.appendChild(enabledLabel);
1066
+ header.appendChild(toggleWrap);
1067
+ block.appendChild(header);
1068
+
1069
+ // ── Body: interval + autoFix + thresholds ──
1070
+ var body = el('div', 'config-plugin-body');
1071
+
1072
+ // Interval (ms → seconds)
1073
+ if (pcfg.interval !== undefined) {
1074
+ var intervalField = el('div', 'config-field');
1075
+ var intervalLabel = el('label', 'config-label');
1076
+ setText(intervalLabel, 'Interval (seconds)');
1077
+ intervalLabel.htmlFor = 'cfg-interval-' + pluginName;
1078
+ var intervalInput = el('input', 'config-input');
1079
+ intervalInput.type = 'number';
1080
+ intervalInput.id = 'cfg-interval-' + pluginName;
1081
+ intervalInput.min = '0';
1082
+ intervalInput.value = String(Math.round(pcfg.interval / 1000));
1083
+ intervalField.appendChild(intervalLabel);
1084
+ intervalField.appendChild(intervalInput);
1085
+ body.appendChild(intervalField);
1086
+ }
1087
+
1088
+ // AutoFix toggle
1089
+ if (pcfg.autoFix !== undefined) {
1090
+ var autoFixField = el('div', 'config-field');
1091
+ var autoFixLabel = el('label', 'config-label');
1092
+ setText(autoFixLabel, 'Auto Fix');
1093
+ autoFixLabel.htmlFor = 'cfg-autofix-' + pluginName;
1094
+ var autoFixToggleWrap = el('div', 'toggle-wrap');
1095
+ var autoFixToggle = el('label', 'toggle');
1096
+ var autoFixInput = el('input');
1097
+ autoFixInput.type = 'checkbox';
1098
+ autoFixInput.id = 'cfg-autofix-' + pluginName;
1099
+ autoFixInput.checked = !!pcfg.autoFix;
1100
+ var autoFixSlider = el('span', 'toggle-slider');
1101
+ autoFixToggle.appendChild(autoFixInput);
1102
+ autoFixToggle.appendChild(autoFixSlider);
1103
+ autoFixToggleWrap.appendChild(autoFixToggle);
1104
+ autoFixField.appendChild(autoFixLabel);
1105
+ autoFixField.appendChild(autoFixToggleWrap);
1106
+ body.appendChild(autoFixField);
1107
+ }
1108
+
1109
+ // Thresholds
1110
+ var thresholds = pcfg.thresholds || {};
1111
+ var threshKeys = Object.keys(thresholds);
1112
+ if (threshKeys.length > 0) {
1113
+ var threshField = el('div', 'config-field config-thresholds');
1114
+ var threshHeading = el('span', 'config-label');
1115
+ setText(threshHeading, 'Thresholds');
1116
+ threshField.appendChild(threshHeading);
1117
+
1118
+ threshKeys.forEach(function (key) {
1119
+ var row = el('div', 'config-threshold-row');
1120
+
1121
+ var keyInput = el('input', 'config-input config-threshold-key');
1122
+ keyInput.type = 'text';
1123
+ keyInput.value = key;
1124
+ keyInput.readOnly = true;
1125
+ keyInput.style.color = 'var(--text-muted)';
1126
+
1127
+ var valInput = el('input', 'config-input config-threshold-val');
1128
+ valInput.type = 'number';
1129
+ valInput.id = 'cfg-thresh-' + pluginName + '-' + key;
1130
+ valInput.value = String(thresholds[key]);
1131
+ valInput.dataset.plugin = pluginName;
1132
+ valInput.dataset.key = key;
1133
+
1134
+ row.appendChild(keyInput);
1135
+ row.appendChild(valInput);
1136
+ threshField.appendChild(row);
1137
+ });
1138
+
1139
+ body.appendChild(threshField);
1140
+ }
1141
+
1142
+ block.appendChild(body);
1143
+ container.appendChild(block);
1144
+ });
1145
+
1146
+ document.getElementById('config-loading').style.display = 'none';
1147
+ container.style.display = 'block';
1148
+ document.getElementById('config-actions').style.display = 'flex';
1149
+ }
1150
+
1151
+ function collectConfigPayload() {
1152
+ if (!_loadedConfig) return null;
1153
+ var plugins = _loadedConfig.plugins || {};
1154
+ var payload = { plugins: {} };
1155
+
1156
+ Object.keys(plugins).forEach(function (pluginName) {
1157
+ var pcfg = plugins[pluginName];
1158
+ var out = {};
1159
+
1160
+ // enabled
1161
+ var enabledEl = document.getElementById('cfg-enabled-' + pluginName);
1162
+ if (enabledEl) out.enabled = enabledEl.checked;
1163
+
1164
+ // interval (seconds → ms)
1165
+ var intervalEl = document.getElementById('cfg-interval-' + pluginName);
1166
+ if (intervalEl) {
1167
+ var secs = parseFloat(intervalEl.value);
1168
+ out.interval = isNaN(secs) ? pcfg.interval : Math.round(secs * 1000);
1169
+ }
1170
+
1171
+ // autoFix
1172
+ var autoFixEl = document.getElementById('cfg-autofix-' + pluginName);
1173
+ if (autoFixEl) out.autoFix = autoFixEl.checked;
1174
+
1175
+ // thresholds
1176
+ var thresholds = pcfg.thresholds || {};
1177
+ if (Object.keys(thresholds).length > 0) {
1178
+ out.thresholds = {};
1179
+ Object.keys(thresholds).forEach(function (key) {
1180
+ var valEl = document.getElementById('cfg-thresh-' + pluginName + '-' + key);
1181
+ if (valEl) {
1182
+ var num = parseFloat(valEl.value);
1183
+ out.thresholds[key] = isNaN(num) ? thresholds[key] : num;
1184
+ } else {
1185
+ out.thresholds[key] = thresholds[key];
1186
+ }
1187
+ });
1188
+ }
1189
+
1190
+ payload.plugins[pluginName] = out;
1191
+ });
1192
+
1193
+ return payload;
1194
+ }
1195
+
1196
+ function showConfigFeedback(type, message) {
1197
+ var fb = document.getElementById('config-feedback');
1198
+ fb.className = 'config-feedback ' + type;
1199
+ setText(fb, message);
1200
+ clearTimeout(fb._hideTimer);
1201
+ fb._hideTimer = setTimeout(function () {
1202
+ fb.className = 'config-feedback';
1203
+ setText(fb, '');
1204
+ }, 4000);
1205
+ }
1206
+
1207
+ async function fetchConfig() {
1208
+ try {
1209
+ var res = await fetch(API_BASE + '/config', { cache: 'no-store' });
1210
+ if (!res.ok) throw new Error('HTTP ' + res.status);
1211
+ var data = await res.json();
1212
+ buildConfigEditor(data);
1213
+ } catch (err) {
1214
+ console.warn('[watchdog] fetchConfig error:', err.message);
1215
+ var loadingEl = document.getElementById('config-loading');
1216
+ setText(loadingEl, 'Unable to load configuration.');
1217
+ }
1218
+ }
1219
+
1220
+ async function saveConfig() {
1221
+ var saveBtn = document.getElementById('config-save-btn');
1222
+ saveBtn.disabled = true;
1223
+ var payload = collectConfigPayload();
1224
+ if (!payload) {
1225
+ showConfigFeedback('error', 'Nothing to save.');
1226
+ saveBtn.disabled = false;
1227
+ return;
1228
+ }
1229
+ try {
1230
+ var res = await fetch(API_BASE + '/config', {
1231
+ method: 'PUT',
1232
+ headers: { 'Content-Type': 'application/json' },
1233
+ body: JSON.stringify(payload),
1234
+ });
1235
+ var data = await res.json();
1236
+ if (res.ok) {
1237
+ showConfigFeedback('success', 'Configuration saved.');
1238
+ buildConfigEditor(data);
1239
+ } else {
1240
+ showConfigFeedback('error', data.error || 'Save failed (HTTP ' + res.status + ').');
1241
+ }
1242
+ } catch (err) {
1243
+ showConfigFeedback('error', 'Save error: ' + err.message);
1244
+ } finally {
1245
+ saveBtn.disabled = false;
1246
+ }
1247
+ }
1248
+
1249
+ document.getElementById('config-save-btn').addEventListener('click', saveConfig);
1250
+
846
1251
  // ── Boot ─────────────────────────────────────────────────────────────────
847
1252
 
848
1253
  buildGaugeGrid();
849
1254
  fetchHealth();
850
1255
  fetchHistory();
1256
+ fetchConfig();
851
1257
  setInterval(function () { fetchHealth(); fetchHistory(); }, 15000);
852
1258
 
853
1259
  }());
@@ -1 +1 @@
1
- {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AACjC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAgB,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAc9C,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,cAAc,EAAE,EACzB,MAAM,EAAE,cAAc,GACrB,MAAM,CAqGR"}
1
+ {"version":3,"file":"routes.d.ts","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAIjC,OAAO,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAgB,MAAM,gCAAgC,CAAC;AAC9E,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAc9C,wBAAgB,YAAY,CAC1B,KAAK,EAAE,YAAY,EACnB,OAAO,EAAE,cAAc,EAAE,EACzB,MAAM,EAAE,cAAc,GACrB,MAAM,CA0JR"}
@@ -1,4 +1,7 @@
1
1
  import { Router } from 'express';
2
+ import { writeFile, mkdir } from 'fs/promises';
3
+ import { join } from 'path';
4
+ import os from 'os';
2
5
  const STATUS_ORDER = ['healthy', 'warning', 'critical'];
3
6
  function worstStatus(statuses) {
4
7
  let worst = 'healthy';
@@ -100,6 +103,48 @@ export function createRoutes(store, plugins, config) {
100
103
  router.get('/config', (_req, res) => {
101
104
  res.json(config);
102
105
  });
106
+ // PUT /config — deep-merge partial config and persist to ~/.aidev/watchdog.json
107
+ router.put('/config', async (req, res) => {
108
+ const body = req.body;
109
+ if (!body || typeof body !== 'object' || Array.isArray(body)) {
110
+ res.status(400).json({ error: 'Request body must be a JSON object' });
111
+ return;
112
+ }
113
+ try {
114
+ // Deep-merge the incoming partial config into the live config object
115
+ function isPlainObject(v) {
116
+ return typeof v === 'object' && v !== null && !Array.isArray(v);
117
+ }
118
+ function deepMerge(target, source) {
119
+ const result = { ...target };
120
+ for (const key of Object.keys(source)) {
121
+ const src = source[key];
122
+ const tgt = result[key];
123
+ if (isPlainObject(src) && isPlainObject(tgt)) {
124
+ result[key] = deepMerge(tgt, src);
125
+ }
126
+ else {
127
+ result[key] = src;
128
+ }
129
+ }
130
+ return result;
131
+ }
132
+ const merged = deepMerge(config, body);
133
+ // Apply the merged values back onto the live config object so GET /config
134
+ // reflects the change immediately (without restart)
135
+ Object.assign(config, merged);
136
+ // Persist user overrides to ~/.aidev/watchdog.json
137
+ const aidevDir = join(os.homedir(), '.aidev');
138
+ await mkdir(aidevDir, { recursive: true });
139
+ const userConfigPath = join(aidevDir, 'watchdog.json');
140
+ await writeFile(userConfigPath, JSON.stringify(body, null, 2), 'utf-8');
141
+ res.json(config);
142
+ }
143
+ catch (err) {
144
+ const message = err instanceof Error ? err.message : String(err);
145
+ res.status(500).json({ error: message });
146
+ }
147
+ });
103
148
  return router;
104
149
  }
105
150
  //# sourceMappingURL=routes.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"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"}
package/dist/index.js CHANGED
@@ -19,7 +19,7 @@ program
19
19
  .description('Load config, start scheduler, and watch continuously')
20
20
  .action(async () => {
21
21
  const config = await loadConfig();
22
- const plugins = loadPlugins(config);
22
+ const plugins = await loadPlugins(config);
23
23
  const home = process.env.HOME || process.env.USERPROFILE || '.';
24
24
  const aidevDir = join(home, '.aidev');
25
25
  mkdirSync(aidevDir, { recursive: true });
@@ -47,7 +47,7 @@ program
47
47
  .description('Run check() on each plugin and print a summary line')
48
48
  .action(async () => {
49
49
  const config = await loadConfig();
50
- const plugins = loadPlugins(config);
50
+ const plugins = await loadPlugins(config);
51
51
  for (const plugin of plugins) {
52
52
  try {
53
53
  const result = await plugin.check();
@@ -71,7 +71,7 @@ program
71
71
  .description('Run check() for all or a specific plugin and print detailed metrics')
72
72
  .action(async (pluginArg) => {
73
73
  const config = await loadConfig();
74
- const plugins = loadPlugins(config);
74
+ const plugins = await loadPlugins(config);
75
75
  const targets = pluginArg
76
76
  ? plugins.filter((p) => p.name === pluginArg)
77
77
  : plugins;
@@ -109,7 +109,7 @@ program
109
109
  .description('Run fix() for all or a specific plugin and print actions and resources freed')
110
110
  .action(async (pluginArg) => {
111
111
  const config = await loadConfig();
112
- const plugins = loadPlugins(config);
112
+ const plugins = await loadPlugins(config);
113
113
  const targets = pluginArg
114
114
  ? plugins.filter((p) => p.name === pluginArg)
115
115
  : plugins;
@@ -151,7 +151,7 @@ apiCmd
151
151
  .description('Start the HTTP API server')
152
152
  .action(async () => {
153
153
  const config = await loadConfig();
154
- const plugins = loadPlugins(config);
154
+ const plugins = await loadPlugins(config);
155
155
  const home = process.env.HOME || process.env.USERPROFILE || '.';
156
156
  const aidevDir = join(home, '.aidev');
157
157
  mkdirSync(aidevDir, { recursive: true });