@ktmcp-cli/alertersystem 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENT.md +40 -0
- package/LICENSE +21 -0
- package/README.md +133 -0
- package/bin/alertersystem.js +2 -0
- package/package.json +27 -0
- package/src/api.js +138 -0
- package/src/config.js +19 -0
- package/src/index.js +405 -0
package/AGENT.md
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Alerter System CLI - AI Agent Guide
|
|
2
|
+
|
|
3
|
+
This CLI provides programmatic access to the Alerter System Monitoring API.
|
|
4
|
+
|
|
5
|
+
## Quick Start for AI Agents
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
alertersystem config set --token YOUR_TOKEN
|
|
9
|
+
alertersystem alerts list
|
|
10
|
+
alertersystem monitors list
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Available Commands
|
|
14
|
+
|
|
15
|
+
### config
|
|
16
|
+
- `alertersystem config set --token <token>` - Set bearer token
|
|
17
|
+
- `alertersystem config get <key>` - Get config value
|
|
18
|
+
- `alertersystem config list` - Show all config
|
|
19
|
+
|
|
20
|
+
### alerts
|
|
21
|
+
- `alertersystem alerts list [--status open|acknowledged|resolved]` - List alerts
|
|
22
|
+
- `alertersystem alerts get <alert-id>` - Get alert details
|
|
23
|
+
- `alertersystem alerts acknowledge <alert-id>` - Acknowledge alert
|
|
24
|
+
- `alertersystem alerts resolve <alert-id>` - Resolve alert
|
|
25
|
+
|
|
26
|
+
### monitors
|
|
27
|
+
- `alertersystem monitors list` - List all monitors
|
|
28
|
+
- `alertersystem monitors get <monitor-id>` - Get monitor details
|
|
29
|
+
- `alertersystem monitors create --name <name> --url <url> [--interval <seconds>] [--type http|https|tcp|ping]` - Create monitor
|
|
30
|
+
- `alertersystem monitors delete <monitor-id>` - Delete monitor
|
|
31
|
+
|
|
32
|
+
### notifications
|
|
33
|
+
- `alertersystem notifications list` - List all notifications
|
|
34
|
+
- `alertersystem notifications get <notification-id>` - Get notification details
|
|
35
|
+
|
|
36
|
+
## Tips for Agents
|
|
37
|
+
|
|
38
|
+
- All commands support `--json` for machine-readable output
|
|
39
|
+
- Filter critical alerts: `alertersystem alerts list --json | jq '.[] | select(.severity == "critical")'`
|
|
40
|
+
- Acknowledge all open alerts: `alertersystem alerts list --status open --json | jq -r '.[].id' | xargs -I{} alertersystem alerts acknowledge {}`
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 KTMCP
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
> "Six months ago, everyone was talking about MCPs. And I was like, screw MCPs. Every MCP would be better as a CLI."
|
|
2
|
+
>
|
|
3
|
+
> — [Peter Steinberger](https://twitter.com/steipete), Founder of OpenClaw
|
|
4
|
+
> [Watch on YouTube (~2:39:00)](https://www.youtube.com/@lexfridman) | [Lex Fridman Podcast #491](https://lexfridman.com/peter-steinberger/)
|
|
5
|
+
|
|
6
|
+
# Alerter System CLI
|
|
7
|
+
|
|
8
|
+
Production-ready CLI for the [Alerter System](https://alertersystem.com) Monitoring API. Manage alerts, monitors, and notifications directly from your terminal.
|
|
9
|
+
|
|
10
|
+
> **Disclaimer**: This is an unofficial CLI tool and is not affiliated with, endorsed by, or supported by Alerter System.
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g @ktmcp-cli/alertersystem
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Configuration
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
alertersystem config set --token YOUR_BEARER_TOKEN
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
Get your API token at [alertersystem.com](https://alertersystem.com).
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### Configuration
|
|
29
|
+
|
|
30
|
+
```bash
|
|
31
|
+
# Set bearer token
|
|
32
|
+
alertersystem config set --token YOUR_BEARER_TOKEN
|
|
33
|
+
|
|
34
|
+
# Show configuration
|
|
35
|
+
alertersystem config list
|
|
36
|
+
|
|
37
|
+
# Get a specific config value
|
|
38
|
+
alertersystem config get token
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Alerts
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
# List all alerts
|
|
45
|
+
alertersystem alerts list
|
|
46
|
+
|
|
47
|
+
# Filter by status
|
|
48
|
+
alertersystem alerts list --status open
|
|
49
|
+
alertersystem alerts list --status acknowledged
|
|
50
|
+
alertersystem alerts list --status resolved
|
|
51
|
+
|
|
52
|
+
# Get a specific alert
|
|
53
|
+
alertersystem alerts get ALERT_ID
|
|
54
|
+
|
|
55
|
+
# Acknowledge an alert
|
|
56
|
+
alertersystem alerts acknowledge ALERT_ID
|
|
57
|
+
|
|
58
|
+
# Resolve an alert
|
|
59
|
+
alertersystem alerts resolve ALERT_ID
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Monitors
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
# List all monitors
|
|
66
|
+
alertersystem monitors list
|
|
67
|
+
|
|
68
|
+
# Get a specific monitor
|
|
69
|
+
alertersystem monitors get MONITOR_ID
|
|
70
|
+
|
|
71
|
+
# Create a new monitor
|
|
72
|
+
alertersystem monitors create \
|
|
73
|
+
--name "My Website" \
|
|
74
|
+
--url https://example.com \
|
|
75
|
+
--interval 60 \
|
|
76
|
+
--type https
|
|
77
|
+
|
|
78
|
+
# Delete a monitor
|
|
79
|
+
alertersystem monitors delete MONITOR_ID
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
### Notifications
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
# List all notifications
|
|
86
|
+
alertersystem notifications list
|
|
87
|
+
|
|
88
|
+
# Get a specific notification
|
|
89
|
+
alertersystem notifications get NOTIFICATION_ID
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### JSON Output
|
|
93
|
+
|
|
94
|
+
All commands support `--json` for machine-readable output:
|
|
95
|
+
|
|
96
|
+
```bash
|
|
97
|
+
# List open alerts as JSON
|
|
98
|
+
alertersystem alerts list --status open --json
|
|
99
|
+
|
|
100
|
+
# Pipe to jq
|
|
101
|
+
alertersystem alerts list --json | jq '.[] | select(.severity == "critical") | {id, name}'
|
|
102
|
+
|
|
103
|
+
# Get monitor status
|
|
104
|
+
alertersystem monitors list --json | jq '.[] | {name, status, uptime}'
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Examples
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
# Morning ops check: see all open alerts
|
|
111
|
+
alertersystem alerts list --status open
|
|
112
|
+
|
|
113
|
+
# Acknowledge all critical alerts
|
|
114
|
+
alertersystem alerts list --json | jq -r '.[] | select(.severity == "critical") | .id' | \
|
|
115
|
+
xargs -I{} alertersystem alerts acknowledge {}
|
|
116
|
+
|
|
117
|
+
# Create a monitor for your API
|
|
118
|
+
alertersystem monitors create --name "Production API" --url https://api.example.com/health --interval 30
|
|
119
|
+
|
|
120
|
+
# Check monitor uptime
|
|
121
|
+
alertersystem monitors list --json | jq '.[] | {name, uptime: .uptime}'
|
|
122
|
+
|
|
123
|
+
# Count unread notifications
|
|
124
|
+
alertersystem notifications list --json | jq '[.[] | select(.read == false)] | length'
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## License
|
|
128
|
+
|
|
129
|
+
MIT
|
|
130
|
+
|
|
131
|
+
---
|
|
132
|
+
|
|
133
|
+
Part of the [KTMCP CLI](https://killthemcp.com) project — replacing MCPs with simple, composable CLIs.
|
package/package.json
ADDED
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@ktmcp-cli/alertersystem",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Production-ready CLI for Alerter System Monitoring API - Kill The MCP",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "src/index.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"alertersystem": "bin/alertersystem.js"
|
|
9
|
+
},
|
|
10
|
+
"keywords": ["alertersystem", "monitoring", "alerts", "cli", "api", "ktmcp"],
|
|
11
|
+
"author": "KTMCP",
|
|
12
|
+
"license": "MIT",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"commander": "^12.0.0",
|
|
15
|
+
"axios": "^1.6.7",
|
|
16
|
+
"chalk": "^5.3.0",
|
|
17
|
+
"ora": "^8.0.1",
|
|
18
|
+
"conf": "^12.0.0"
|
|
19
|
+
},
|
|
20
|
+
"engines": { "node": ">=18.0.0" },
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "git+https://github.com/ktmcp-cli/alertersystem.git"
|
|
24
|
+
},
|
|
25
|
+
"homepage": "https://killthemcp.com/alertersystem-cli",
|
|
26
|
+
"bugs": { "url": "https://github.com/ktmcp-cli/alertersystem/issues" }
|
|
27
|
+
}
|
package/src/api.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import { getConfig } from './config.js';
|
|
3
|
+
|
|
4
|
+
const BASE_URL = 'https://api.alertersystem.com';
|
|
5
|
+
|
|
6
|
+
function getToken() {
|
|
7
|
+
const token = getConfig('token');
|
|
8
|
+
if (!token) {
|
|
9
|
+
throw new Error('Bearer token not configured. Run: alertersystem config set token YOUR_TOKEN');
|
|
10
|
+
}
|
|
11
|
+
return token;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
function handleApiError(error) {
|
|
15
|
+
if (error.response) {
|
|
16
|
+
const status = error.response.status;
|
|
17
|
+
const data = error.response.data;
|
|
18
|
+
if (status === 401 || status === 403) {
|
|
19
|
+
throw new Error('Authentication failed. Check your token: alertersystem config set token YOUR_TOKEN');
|
|
20
|
+
} else if (status === 429) {
|
|
21
|
+
throw new Error('Rate limit exceeded. Please wait before retrying.');
|
|
22
|
+
} else if (status === 404) {
|
|
23
|
+
throw new Error('Resource not found.');
|
|
24
|
+
} else {
|
|
25
|
+
const message = data?.error || data?.message || JSON.stringify(data);
|
|
26
|
+
throw new Error(`API Error (${status}): ${message}`);
|
|
27
|
+
}
|
|
28
|
+
} else if (error.request) {
|
|
29
|
+
throw new Error('No response from Alerter System API. Check your internet connection.');
|
|
30
|
+
} else {
|
|
31
|
+
throw error;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function getHeaders() {
|
|
36
|
+
return {
|
|
37
|
+
'Authorization': `Bearer ${getToken()}`,
|
|
38
|
+
'Content-Type': 'application/json',
|
|
39
|
+
'Accept': 'application/json'
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
async function apiGet(path, params = {}) {
|
|
44
|
+
try {
|
|
45
|
+
const response = await axios.get(`${BASE_URL}${path}`, {
|
|
46
|
+
headers: getHeaders(),
|
|
47
|
+
params
|
|
48
|
+
});
|
|
49
|
+
return response.data;
|
|
50
|
+
} catch (error) {
|
|
51
|
+
handleApiError(error);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function apiPost(path, data = {}) {
|
|
56
|
+
try {
|
|
57
|
+
const response = await axios.post(`${BASE_URL}${path}`, data, {
|
|
58
|
+
headers: getHeaders()
|
|
59
|
+
});
|
|
60
|
+
return response.data;
|
|
61
|
+
} catch (error) {
|
|
62
|
+
handleApiError(error);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function apiDelete(path) {
|
|
67
|
+
try {
|
|
68
|
+
const response = await axios.delete(`${BASE_URL}${path}`, {
|
|
69
|
+
headers: getHeaders()
|
|
70
|
+
});
|
|
71
|
+
return response.data;
|
|
72
|
+
} catch (error) {
|
|
73
|
+
handleApiError(error);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
async function apiPatch(path, data = {}) {
|
|
78
|
+
try {
|
|
79
|
+
const response = await axios.patch(`${BASE_URL}${path}`, data, {
|
|
80
|
+
headers: getHeaders()
|
|
81
|
+
});
|
|
82
|
+
return response.data;
|
|
83
|
+
} catch (error) {
|
|
84
|
+
handleApiError(error);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// ============================================================
|
|
89
|
+
// ALERTS
|
|
90
|
+
// ============================================================
|
|
91
|
+
|
|
92
|
+
export async function listAlerts(params = {}) {
|
|
93
|
+
return await apiGet('/alerts', params);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
export async function getAlert(alertId) {
|
|
97
|
+
return await apiGet(`/alerts/${alertId}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export async function acknowledgeAlert(alertId) {
|
|
101
|
+
return await apiPatch(`/alerts/${alertId}/acknowledge`, {});
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export async function resolveAlert(alertId) {
|
|
105
|
+
return await apiPatch(`/alerts/${alertId}/resolve`, {});
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
// ============================================================
|
|
109
|
+
// MONITORS
|
|
110
|
+
// ============================================================
|
|
111
|
+
|
|
112
|
+
export async function listMonitors(params = {}) {
|
|
113
|
+
return await apiGet('/monitors', params);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
export async function getMonitor(monitorId) {
|
|
117
|
+
return await apiGet(`/monitors/${monitorId}`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export async function createMonitor({ name, url, interval, type }) {
|
|
121
|
+
return await apiPost('/monitors', { name, url, interval, type });
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
export async function deleteMonitor(monitorId) {
|
|
125
|
+
return await apiDelete(`/monitors/${monitorId}`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// ============================================================
|
|
129
|
+
// NOTIFICATIONS
|
|
130
|
+
// ============================================================
|
|
131
|
+
|
|
132
|
+
export async function listNotifications(params = {}) {
|
|
133
|
+
return await apiGet('/notifications', params);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export async function getNotification(notificationId) {
|
|
137
|
+
return await apiGet(`/notifications/${notificationId}`);
|
|
138
|
+
}
|
package/src/config.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import Conf from 'conf';
|
|
2
|
+
|
|
3
|
+
const config = new Conf({ projectName: '@ktmcp-cli/alertersystem' });
|
|
4
|
+
|
|
5
|
+
export function getConfig(key) {
|
|
6
|
+
return config.get(key);
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function setConfig(key, value) {
|
|
10
|
+
config.set(key, value);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export function isConfigured() {
|
|
14
|
+
return !!config.get('token');
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function getAllConfig() {
|
|
18
|
+
return config.store;
|
|
19
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
import ora from 'ora';
|
|
4
|
+
import { getConfig, setConfig, isConfigured, getAllConfig } from './config.js';
|
|
5
|
+
import {
|
|
6
|
+
listAlerts,
|
|
7
|
+
getAlert,
|
|
8
|
+
acknowledgeAlert,
|
|
9
|
+
resolveAlert,
|
|
10
|
+
listMonitors,
|
|
11
|
+
getMonitor,
|
|
12
|
+
createMonitor,
|
|
13
|
+
deleteMonitor,
|
|
14
|
+
listNotifications,
|
|
15
|
+
getNotification
|
|
16
|
+
} from './api.js';
|
|
17
|
+
|
|
18
|
+
const program = new Command();
|
|
19
|
+
|
|
20
|
+
// ============================================================
|
|
21
|
+
// Helpers
|
|
22
|
+
// ============================================================
|
|
23
|
+
|
|
24
|
+
function printSuccess(message) {
|
|
25
|
+
console.log(chalk.green('✓') + ' ' + message);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function printError(message) {
|
|
29
|
+
console.error(chalk.red('✗') + ' ' + message);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function printTable(data, columns) {
|
|
33
|
+
if (!data || data.length === 0) {
|
|
34
|
+
console.log(chalk.yellow('No results found.'));
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
const widths = {};
|
|
38
|
+
columns.forEach(col => {
|
|
39
|
+
widths[col.key] = col.label.length;
|
|
40
|
+
data.forEach(row => {
|
|
41
|
+
const val = String(col.format ? col.format(row[col.key], row) : (row[col.key] ?? ''));
|
|
42
|
+
if (val.length > widths[col.key]) widths[col.key] = val.length;
|
|
43
|
+
});
|
|
44
|
+
widths[col.key] = Math.min(widths[col.key], 40);
|
|
45
|
+
});
|
|
46
|
+
const header = columns.map(col => col.label.padEnd(widths[col.key])).join(' ');
|
|
47
|
+
console.log(chalk.bold(chalk.cyan(header)));
|
|
48
|
+
console.log(chalk.dim('─'.repeat(header.length)));
|
|
49
|
+
data.forEach(row => {
|
|
50
|
+
const line = columns.map(col => {
|
|
51
|
+
const val = String(col.format ? col.format(row[col.key], row) : (row[col.key] ?? ''));
|
|
52
|
+
return val.substring(0, widths[col.key]).padEnd(widths[col.key]);
|
|
53
|
+
}).join(' ');
|
|
54
|
+
console.log(line);
|
|
55
|
+
});
|
|
56
|
+
console.log(chalk.dim(`\n${data.length} result(s)`));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function printJson(data) {
|
|
60
|
+
console.log(JSON.stringify(data, null, 2));
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function withSpinner(message, fn) {
|
|
64
|
+
const spinner = ora(message).start();
|
|
65
|
+
try {
|
|
66
|
+
const result = await fn();
|
|
67
|
+
spinner.stop();
|
|
68
|
+
return result;
|
|
69
|
+
} catch (error) {
|
|
70
|
+
spinner.stop();
|
|
71
|
+
throw error;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function requireAuth() {
|
|
76
|
+
if (!isConfigured()) {
|
|
77
|
+
printError('Alerter System token not configured.');
|
|
78
|
+
console.log('\nRun the following to configure:');
|
|
79
|
+
console.log(chalk.cyan(' alertersystem config set token YOUR_BEARER_TOKEN'));
|
|
80
|
+
process.exit(1);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function alertStatusColor(status) {
|
|
85
|
+
switch ((status || '').toLowerCase()) {
|
|
86
|
+
case 'open': case 'firing': case 'critical': return chalk.red(status);
|
|
87
|
+
case 'acknowledged': return chalk.yellow(status);
|
|
88
|
+
case 'resolved': case 'closed': return chalk.green(status);
|
|
89
|
+
default: return status;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function monitorStatusColor(status) {
|
|
94
|
+
switch ((status || '').toLowerCase()) {
|
|
95
|
+
case 'up': case 'ok': case 'operational': return chalk.green(status);
|
|
96
|
+
case 'down': case 'error': case 'critical': return chalk.red(status);
|
|
97
|
+
case 'degraded': case 'warning': return chalk.yellow(status);
|
|
98
|
+
default: return status;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
// ============================================================
|
|
103
|
+
// Program metadata
|
|
104
|
+
// ============================================================
|
|
105
|
+
|
|
106
|
+
program
|
|
107
|
+
.name('alertersystem')
|
|
108
|
+
.description(chalk.bold('Alerter System CLI') + ' - System monitoring from your terminal')
|
|
109
|
+
.version('1.0.0');
|
|
110
|
+
|
|
111
|
+
// ============================================================
|
|
112
|
+
// CONFIG
|
|
113
|
+
// ============================================================
|
|
114
|
+
|
|
115
|
+
const configCmd = program.command('config').description('Manage CLI configuration');
|
|
116
|
+
|
|
117
|
+
configCmd
|
|
118
|
+
.command('set')
|
|
119
|
+
.description('Set configuration values')
|
|
120
|
+
.option('--token <token>', 'Alerter System Bearer token')
|
|
121
|
+
.action((options) => {
|
|
122
|
+
if (options.token) {
|
|
123
|
+
setConfig('token', options.token);
|
|
124
|
+
printSuccess('Token set');
|
|
125
|
+
} else {
|
|
126
|
+
printError('No options provided. Use --token');
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
configCmd
|
|
131
|
+
.command('get')
|
|
132
|
+
.description('Get a configuration value')
|
|
133
|
+
.argument('<key>', 'Config key to retrieve')
|
|
134
|
+
.action((key) => {
|
|
135
|
+
const value = getConfig(key);
|
|
136
|
+
if (value === undefined || value === null || value === '') {
|
|
137
|
+
console.log(chalk.yellow(`${key}: not set`));
|
|
138
|
+
} else {
|
|
139
|
+
const masked = key.toLowerCase().includes('token') ? '*'.repeat(8) : value;
|
|
140
|
+
console.log(`${key}: ${chalk.green(masked)}`);
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
configCmd
|
|
145
|
+
.command('list')
|
|
146
|
+
.description('List all configuration values')
|
|
147
|
+
.action(() => {
|
|
148
|
+
const all = getAllConfig();
|
|
149
|
+
console.log(chalk.bold('\nAlerter System CLI Configuration\n'));
|
|
150
|
+
console.log('Token:', all.token ? chalk.green('*'.repeat(8)) : chalk.red('not set'));
|
|
151
|
+
console.log('');
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// ============================================================
|
|
155
|
+
// ALERTS
|
|
156
|
+
// ============================================================
|
|
157
|
+
|
|
158
|
+
const alertsCmd = program.command('alerts').description('Manage system alerts');
|
|
159
|
+
|
|
160
|
+
alertsCmd
|
|
161
|
+
.command('list')
|
|
162
|
+
.description('List all alerts')
|
|
163
|
+
.option('--status <status>', 'Filter by status (open|acknowledged|resolved)')
|
|
164
|
+
.option('--json', 'Output as JSON')
|
|
165
|
+
.action(async (options) => {
|
|
166
|
+
requireAuth();
|
|
167
|
+
try {
|
|
168
|
+
const params = options.status ? { status: options.status } : {};
|
|
169
|
+
const data = await withSpinner('Fetching alerts...', () => listAlerts(params));
|
|
170
|
+
if (options.json) { printJson(data); return; }
|
|
171
|
+
const list = Array.isArray(data) ? data : (data?.alerts || data?.data || []);
|
|
172
|
+
printTable(list, [
|
|
173
|
+
{ key: 'id', label: 'ID' },
|
|
174
|
+
{ key: 'name', label: 'Name' },
|
|
175
|
+
{ key: 'severity', label: 'Severity', format: (v) => v ? chalk.bold(v) : 'N/A' },
|
|
176
|
+
{ key: 'status', label: 'Status', format: (v) => alertStatusColor(v) },
|
|
177
|
+
{ key: 'monitor', label: 'Monitor', format: (v, row) => v || row.monitor_name || 'N/A' },
|
|
178
|
+
{ key: 'created_at', label: 'Created' }
|
|
179
|
+
]);
|
|
180
|
+
} catch (error) {
|
|
181
|
+
printError(error.message);
|
|
182
|
+
process.exit(1);
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
alertsCmd
|
|
187
|
+
.command('get <alert-id>')
|
|
188
|
+
.description('Get details of a specific alert')
|
|
189
|
+
.option('--json', 'Output as JSON')
|
|
190
|
+
.action(async (alertId, options) => {
|
|
191
|
+
requireAuth();
|
|
192
|
+
try {
|
|
193
|
+
const alert = await withSpinner('Fetching alert...', () => getAlert(alertId));
|
|
194
|
+
if (options.json) { printJson(alert); return; }
|
|
195
|
+
console.log(chalk.bold('\nAlert Details\n'));
|
|
196
|
+
console.log('ID: ', chalk.cyan(alert.id || alertId));
|
|
197
|
+
console.log('Name: ', chalk.bold(alert.name || 'N/A'));
|
|
198
|
+
console.log('Status: ', alertStatusColor(alert.status));
|
|
199
|
+
console.log('Severity: ', alert.severity || 'N/A');
|
|
200
|
+
console.log('Monitor: ', alert.monitor || alert.monitor_name || 'N/A');
|
|
201
|
+
console.log('Message: ', alert.message || alert.description || 'N/A');
|
|
202
|
+
console.log('Created: ', alert.created_at || 'N/A');
|
|
203
|
+
if (alert.resolved_at) console.log('Resolved: ', alert.resolved_at);
|
|
204
|
+
console.log('');
|
|
205
|
+
} catch (error) {
|
|
206
|
+
printError(error.message);
|
|
207
|
+
process.exit(1);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
alertsCmd
|
|
212
|
+
.command('acknowledge <alert-id>')
|
|
213
|
+
.description('Acknowledge an alert')
|
|
214
|
+
.option('--json', 'Output as JSON')
|
|
215
|
+
.action(async (alertId, options) => {
|
|
216
|
+
requireAuth();
|
|
217
|
+
try {
|
|
218
|
+
const result = await withSpinner('Acknowledging alert...', () => acknowledgeAlert(alertId));
|
|
219
|
+
if (options.json) { printJson(result); return; }
|
|
220
|
+
printSuccess(`Alert ${chalk.cyan(alertId)} acknowledged`);
|
|
221
|
+
} catch (error) {
|
|
222
|
+
printError(error.message);
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
alertsCmd
|
|
228
|
+
.command('resolve <alert-id>')
|
|
229
|
+
.description('Resolve an alert')
|
|
230
|
+
.option('--json', 'Output as JSON')
|
|
231
|
+
.action(async (alertId, options) => {
|
|
232
|
+
requireAuth();
|
|
233
|
+
try {
|
|
234
|
+
const result = await withSpinner('Resolving alert...', () => resolveAlert(alertId));
|
|
235
|
+
if (options.json) { printJson(result); return; }
|
|
236
|
+
printSuccess(`Alert ${chalk.cyan(alertId)} resolved`);
|
|
237
|
+
} catch (error) {
|
|
238
|
+
printError(error.message);
|
|
239
|
+
process.exit(1);
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
|
|
243
|
+
// ============================================================
|
|
244
|
+
// MONITORS
|
|
245
|
+
// ============================================================
|
|
246
|
+
|
|
247
|
+
const monitorsCmd = program.command('monitors').description('Manage uptime monitors');
|
|
248
|
+
|
|
249
|
+
monitorsCmd
|
|
250
|
+
.command('list')
|
|
251
|
+
.description('List all monitors')
|
|
252
|
+
.option('--json', 'Output as JSON')
|
|
253
|
+
.action(async (options) => {
|
|
254
|
+
requireAuth();
|
|
255
|
+
try {
|
|
256
|
+
const data = await withSpinner('Fetching monitors...', () => listMonitors());
|
|
257
|
+
if (options.json) { printJson(data); return; }
|
|
258
|
+
const list = Array.isArray(data) ? data : (data?.monitors || data?.data || []);
|
|
259
|
+
printTable(list, [
|
|
260
|
+
{ key: 'id', label: 'ID' },
|
|
261
|
+
{ key: 'name', label: 'Name' },
|
|
262
|
+
{ key: 'url', label: 'URL' },
|
|
263
|
+
{ key: 'type', label: 'Type' },
|
|
264
|
+
{ key: 'status', label: 'Status', format: (v) => monitorStatusColor(v) },
|
|
265
|
+
{ key: 'interval', label: 'Interval', format: (v) => v ? `${v}s` : 'N/A' }
|
|
266
|
+
]);
|
|
267
|
+
} catch (error) {
|
|
268
|
+
printError(error.message);
|
|
269
|
+
process.exit(1);
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
|
|
273
|
+
monitorsCmd
|
|
274
|
+
.command('get <monitor-id>')
|
|
275
|
+
.description('Get details of a specific monitor')
|
|
276
|
+
.option('--json', 'Output as JSON')
|
|
277
|
+
.action(async (monitorId, options) => {
|
|
278
|
+
requireAuth();
|
|
279
|
+
try {
|
|
280
|
+
const monitor = await withSpinner('Fetching monitor...', () => getMonitor(monitorId));
|
|
281
|
+
if (options.json) { printJson(monitor); return; }
|
|
282
|
+
console.log(chalk.bold('\nMonitor Details\n'));
|
|
283
|
+
console.log('ID: ', chalk.cyan(monitor.id || monitorId));
|
|
284
|
+
console.log('Name: ', chalk.bold(monitor.name || 'N/A'));
|
|
285
|
+
console.log('URL: ', monitor.url || 'N/A');
|
|
286
|
+
console.log('Type: ', monitor.type || 'N/A');
|
|
287
|
+
console.log('Status: ', monitorStatusColor(monitor.status));
|
|
288
|
+
console.log('Interval: ', monitor.interval ? `${monitor.interval}s` : 'N/A');
|
|
289
|
+
console.log('Created: ', monitor.created_at || 'N/A');
|
|
290
|
+
if (monitor.last_checked) console.log('Last Check: ', monitor.last_checked);
|
|
291
|
+
if (monitor.uptime) console.log('Uptime: ', `${monitor.uptime}%`);
|
|
292
|
+
console.log('');
|
|
293
|
+
} catch (error) {
|
|
294
|
+
printError(error.message);
|
|
295
|
+
process.exit(1);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
monitorsCmd
|
|
300
|
+
.command('create')
|
|
301
|
+
.description('Create a new uptime monitor')
|
|
302
|
+
.requiredOption('--name <name>', 'Monitor name')
|
|
303
|
+
.requiredOption('--url <url>', 'URL to monitor')
|
|
304
|
+
.option('--interval <seconds>', 'Check interval in seconds', '60')
|
|
305
|
+
.option('--type <type>', 'Monitor type (http|https|tcp|ping)', 'https')
|
|
306
|
+
.option('--json', 'Output as JSON')
|
|
307
|
+
.action(async (options) => {
|
|
308
|
+
requireAuth();
|
|
309
|
+
try {
|
|
310
|
+
const monitor = await withSpinner('Creating monitor...', () =>
|
|
311
|
+
createMonitor({
|
|
312
|
+
name: options.name,
|
|
313
|
+
url: options.url,
|
|
314
|
+
interval: parseInt(options.interval),
|
|
315
|
+
type: options.type
|
|
316
|
+
})
|
|
317
|
+
);
|
|
318
|
+
if (options.json) { printJson(monitor); return; }
|
|
319
|
+
printSuccess(`Monitor created: ${chalk.bold(options.name)}`);
|
|
320
|
+
console.log('Monitor ID:', chalk.cyan(monitor.id || 'N/A'));
|
|
321
|
+
console.log('URL: ', options.url);
|
|
322
|
+
console.log('Interval: ', `${options.interval}s`);
|
|
323
|
+
console.log('');
|
|
324
|
+
} catch (error) {
|
|
325
|
+
printError(error.message);
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
monitorsCmd
|
|
331
|
+
.command('delete <monitor-id>')
|
|
332
|
+
.description('Delete a monitor')
|
|
333
|
+
.option('--json', 'Output as JSON')
|
|
334
|
+
.action(async (monitorId, options) => {
|
|
335
|
+
requireAuth();
|
|
336
|
+
try {
|
|
337
|
+
const result = await withSpinner('Deleting monitor...', () => deleteMonitor(monitorId));
|
|
338
|
+
if (options.json) { printJson(result || { deleted: true }); return; }
|
|
339
|
+
printSuccess(`Monitor ${chalk.cyan(monitorId)} deleted`);
|
|
340
|
+
} catch (error) {
|
|
341
|
+
printError(error.message);
|
|
342
|
+
process.exit(1);
|
|
343
|
+
}
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
// ============================================================
|
|
347
|
+
// NOTIFICATIONS
|
|
348
|
+
// ============================================================
|
|
349
|
+
|
|
350
|
+
const notificationsCmd = program.command('notifications').description('View notifications');
|
|
351
|
+
|
|
352
|
+
notificationsCmd
|
|
353
|
+
.command('list')
|
|
354
|
+
.description('List all notifications')
|
|
355
|
+
.option('--json', 'Output as JSON')
|
|
356
|
+
.action(async (options) => {
|
|
357
|
+
requireAuth();
|
|
358
|
+
try {
|
|
359
|
+
const data = await withSpinner('Fetching notifications...', () => listNotifications());
|
|
360
|
+
if (options.json) { printJson(data); return; }
|
|
361
|
+
const list = Array.isArray(data) ? data : (data?.notifications || data?.data || []);
|
|
362
|
+
printTable(list, [
|
|
363
|
+
{ key: 'id', label: 'ID' },
|
|
364
|
+
{ key: 'type', label: 'Type' },
|
|
365
|
+
{ key: 'message', label: 'Message' },
|
|
366
|
+
{ key: 'read', label: 'Read', format: (v) => v ? chalk.dim('Yes') : chalk.bold('No') },
|
|
367
|
+
{ key: 'created_at', label: 'Created' }
|
|
368
|
+
]);
|
|
369
|
+
} catch (error) {
|
|
370
|
+
printError(error.message);
|
|
371
|
+
process.exit(1);
|
|
372
|
+
}
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
notificationsCmd
|
|
376
|
+
.command('get <notification-id>')
|
|
377
|
+
.description('Get details of a specific notification')
|
|
378
|
+
.option('--json', 'Output as JSON')
|
|
379
|
+
.action(async (notificationId, options) => {
|
|
380
|
+
requireAuth();
|
|
381
|
+
try {
|
|
382
|
+
const notification = await withSpinner('Fetching notification...', () => getNotification(notificationId));
|
|
383
|
+
if (options.json) { printJson(notification); return; }
|
|
384
|
+
console.log(chalk.bold('\nNotification Details\n'));
|
|
385
|
+
console.log('ID: ', chalk.cyan(notification.id || notificationId));
|
|
386
|
+
console.log('Type: ', notification.type || 'N/A');
|
|
387
|
+
console.log('Message: ', notification.message || 'N/A');
|
|
388
|
+
console.log('Read: ', notification.read ? chalk.dim('Yes') : chalk.bold('No'));
|
|
389
|
+
console.log('Created: ', notification.created_at || 'N/A');
|
|
390
|
+
console.log('');
|
|
391
|
+
} catch (error) {
|
|
392
|
+
printError(error.message);
|
|
393
|
+
process.exit(1);
|
|
394
|
+
}
|
|
395
|
+
});
|
|
396
|
+
|
|
397
|
+
// ============================================================
|
|
398
|
+
// Parse
|
|
399
|
+
// ============================================================
|
|
400
|
+
|
|
401
|
+
program.parse(process.argv);
|
|
402
|
+
|
|
403
|
+
if (process.argv.length <= 2) {
|
|
404
|
+
program.help();
|
|
405
|
+
}
|