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.
- package/README.md +115 -0
- package/dashboard/watchdog.html +406 -0
- package/dist/api/routes.d.ts.map +1 -1
- package/dist/api/routes.js +45 -0
- package/dist/api/routes.js.map +1 -1
- package/dist/index.js +5 -5
- package/dist/index.js.map +1 -1
- package/dist/platform/index.d.ts.map +1 -1
- package/dist/platform/index.js +7 -1
- package/dist/platform/index.js.map +1 -1
- package/dist/platform/linux.d.ts +14 -0
- package/dist/platform/linux.d.ts.map +1 -0
- package/dist/platform/linux.js +235 -0
- package/dist/platform/linux.js.map +1 -0
- package/dist/platform/macos.d.ts +14 -0
- package/dist/platform/macos.d.ts.map +1 -0
- package/dist/platform/macos.js +255 -0
- package/dist/platform/macos.js.map +1 -0
- package/dist/plugins/plugin-loader.d.ts +1 -1
- package/dist/plugins/plugin-loader.d.ts.map +1 -1
- package/dist/plugins/plugin-loader.js +46 -1
- package/dist/plugins/plugin-loader.js.map +1 -1
- package/dist/plugins/process-guard.d.ts.map +1 -1
- package/dist/plugins/process-guard.js +0 -15
- package/dist/plugins/process-guard.js.map +1 -1
- package/package.json +1 -1
- package/src/api/routes.ts +56 -0
- package/src/index.ts +5 -5
- package/src/platform/index.ts +7 -1
- package/src/platform/linux.ts +255 -0
- package/src/platform/macos.ts +259 -0
- package/src/plugins/plugin-loader.ts +66 -1
- package/src/plugins/process-guard.ts +0 -15
- package/tests/plugins/cpu-monitor.test.ts +5 -5
- package/tests/plugins/disk-health.test.ts +1 -1
- package/tests/plugins/fixtures/broken-plugin.js +5 -0
- package/tests/plugins/fixtures/custom-plugin.js +36 -0
- package/tests/plugins/fixtures/named-plugin.js +31 -0
- package/tests/plugins/plugin-loader.test.ts +124 -7
- package/tests/plugins/process-guard.test.ts +1 -1
package/README.md
ADDED
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# Process Watchdog
|
|
2
|
+
|
|
3
|
+
[](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
|
package/dashboard/watchdog.html
CHANGED
|
@@ -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
|
}());
|
package/dist/api/routes.d.ts.map
CHANGED
|
@@ -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;
|
|
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"}
|
package/dist/api/routes.js
CHANGED
|
@@ -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
|
package/dist/api/routes.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"routes.js","sourceRoot":"","sources":["../../src/api/routes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;
|
|
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 });
|