@uptimebolt/mcp-server 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/LICENSE +21 -0
- package/README.md +186 -0
- package/dist/api-client.d.ts +11 -0
- package/dist/api-client.d.ts.map +1 -0
- package/dist/api-client.js +80 -0
- package/dist/api-client.js.map +1 -0
- package/dist/formatters.d.ts +17 -0
- package/dist/formatters.d.ts.map +1 -0
- package/dist/formatters.js +300 -0
- package/dist/formatters.js.map +1 -0
- package/dist/http-server.d.ts +3 -0
- package/dist/http-server.d.ts.map +1 -0
- package/dist/http-server.js +148 -0
- package/dist/http-server.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/dist/index.js.map +1 -0
- package/dist/logger.d.ts +4 -0
- package/dist/logger.d.ts.map +1 -0
- package/dist/logger.js +77 -0
- package/dist/logger.js.map +1 -0
- package/dist/server.d.ts +3 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +34 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/get-deployments.d.ts +20 -0
- package/dist/tools/get-deployments.d.ts.map +1 -0
- package/dist/tools/get-deployments.js +27 -0
- package/dist/tools/get-deployments.js.map +1 -0
- package/dist/tools/get-executive-summary.d.ts +19 -0
- package/dist/tools/get-executive-summary.d.ts.map +1 -0
- package/dist/tools/get-executive-summary.js +19 -0
- package/dist/tools/get-executive-summary.js.map +1 -0
- package/dist/tools/get-incidents.d.ts +22 -0
- package/dist/tools/get-incidents.d.ts.map +1 -0
- package/dist/tools/get-incidents.js +56 -0
- package/dist/tools/get-incidents.js.map +1 -0
- package/dist/tools/get-monitor-health.d.ts +20 -0
- package/dist/tools/get-monitor-health.d.ts.map +1 -0
- package/dist/tools/get-monitor-health.js +48 -0
- package/dist/tools/get-monitor-health.js.map +1 -0
- package/dist/tools/get-monitor-metrics.d.ts +19 -0
- package/dist/tools/get-monitor-metrics.d.ts.map +1 -0
- package/dist/tools/get-monitor-metrics.js +42 -0
- package/dist/tools/get-monitor-metrics.js.map +1 -0
- package/dist/tools/get-monitors.d.ts +19 -0
- package/dist/tools/get-monitors.d.ts.map +1 -0
- package/dist/tools/get-monitors.js +44 -0
- package/dist/tools/get-monitors.js.map +1 -0
- package/dist/tools/get-predictions.d.ts +20 -0
- package/dist/tools/get-predictions.d.ts.map +1 -0
- package/dist/tools/get-predictions.js +36 -0
- package/dist/tools/get-predictions.js.map +1 -0
- package/dist/tools/get-service-status.d.ts +19 -0
- package/dist/tools/get-service-status.d.ts.map +1 -0
- package/dist/tools/get-service-status.js +47 -0
- package/dist/tools/get-service-status.js.map +1 -0
- package/dist/tools/is-safe-to-deploy.d.ts +19 -0
- package/dist/tools/is-safe-to-deploy.d.ts.map +1 -0
- package/dist/tools/is-safe-to-deploy.js +148 -0
- package/dist/tools/is-safe-to-deploy.js.map +1 -0
- package/dist/tools/run-root-cause-analysis.d.ts +21 -0
- package/dist/tools/run-root-cause-analysis.d.ts.map +1 -0
- package/dist/tools/run-root-cause-analysis.js +40 -0
- package/dist/tools/run-root-cause-analysis.js.map +1 -0
- package/dist/tools.d.ts +288 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +146 -0
- package/dist/tools.js.map +1 -0
- package/package.json +70 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 CLM Cloud Solutions S.L.
|
|
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,186 @@
|
|
|
1
|
+
# UptimeBolt MCP Server
|
|
2
|
+
|
|
3
|
+
AI-powered infrastructure monitoring tools for [Claude](https://claude.ai), [Claude Code](https://claude.ai/code), [Cursor](https://cursor.com), and any MCP-compatible client.
|
|
4
|
+
|
|
5
|
+
[UptimeBolt](https://uptimebolt.com) is an AI-first monitoring platform that groups monitors into logical business services, predicts cascade failures before they happen, and automatically identifies which deploy caused each incident — including the commit, files, and lines of code responsible.
|
|
6
|
+
|
|
7
|
+
Built by [CLM Cloud Solutions](https://clmcloudsolutions.es) in Madrid, Spain.
|
|
8
|
+
|
|
9
|
+
## Why UptimeBolt MCP Server?
|
|
10
|
+
|
|
11
|
+
Ask your infrastructure questions in natural language. Instead of navigating dashboards, let your AI assistant query real-time monitoring data directly:
|
|
12
|
+
|
|
13
|
+
- *"Is it safe to deploy right now?"* — get a data-driven answer based on health scores, active incidents, and predictions
|
|
14
|
+
- *"What caused the last incident?"* — AI-powered root cause analysis with deploy correlation
|
|
15
|
+
- *"Give me an executive summary of the last 24 hours"* — ready for your standup or status report
|
|
16
|
+
- *"Show me monitors that are degraded"* — instant filtered view across your infrastructure
|
|
17
|
+
|
|
18
|
+
## Features
|
|
19
|
+
|
|
20
|
+
- **10 monitoring tools** — service status, incidents, predictions, RCA, deploy safety, and more
|
|
21
|
+
- **Dual transport** — stdio (local) + HTTP (remote/CI-CD)
|
|
22
|
+
- **API key authentication** — secure per-request access to your UptimeBolt data
|
|
23
|
+
- **Works everywhere** — Claude Desktop, Claude Code, Cursor, Cline, and any MCP client
|
|
24
|
+
- **CI/CD ready** — deploy safety checks directly in your pipeline
|
|
25
|
+
|
|
26
|
+
## Quick Start
|
|
27
|
+
|
|
28
|
+
### Claude Desktop (stdio)
|
|
29
|
+
|
|
30
|
+
Add to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json`):
|
|
31
|
+
|
|
32
|
+
```json
|
|
33
|
+
{
|
|
34
|
+
"mcpServers": {
|
|
35
|
+
"uptimebolt": {
|
|
36
|
+
"command": "npx",
|
|
37
|
+
"args": ["-y", "@uptimebolt/mcp-server"],
|
|
38
|
+
"env": {
|
|
39
|
+
"UPTIMEBOLT_API_KEY": "your-api-key",
|
|
40
|
+
"UPTIMEBOLT_API_URL": "https://api.uptimebolt.io"
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Claude Code / Cursor
|
|
48
|
+
|
|
49
|
+
Add to your project's `.mcp.json`:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"mcpServers": {
|
|
54
|
+
"uptimebolt": {
|
|
55
|
+
"command": "npx",
|
|
56
|
+
"args": ["-y", "@uptimebolt/mcp-server"],
|
|
57
|
+
"env": {
|
|
58
|
+
"UPTIMEBOLT_API_KEY": "your-api-key",
|
|
59
|
+
"UPTIMEBOLT_API_URL": "https://api.uptimebolt.io"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### Docker (HTTP)
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
docker run -p 3100:3100 \
|
|
70
|
+
-e UPTIMEBOLT_API_URL=https://api.uptimebolt.io \
|
|
71
|
+
uptimebolt/mcp-server:latest
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
Then connect via `mcp-remote`:
|
|
75
|
+
|
|
76
|
+
```json
|
|
77
|
+
{
|
|
78
|
+
"mcpServers": {
|
|
79
|
+
"uptimebolt": {
|
|
80
|
+
"command": "npx",
|
|
81
|
+
"args": ["-y", "mcp-remote", "http://localhost:3100/mcp", "--header", "x-api-key:your-api-key"]
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### npm (programmatic)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm install @uptimebolt/mcp-server
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
```typescript
|
|
94
|
+
import { TOOLS, HANDLERS } from "@uptimebolt/mcp-server";
|
|
95
|
+
|
|
96
|
+
// TOOLS — MCP tool definitions (10 tools)
|
|
97
|
+
// HANDLERS — tool handler functions: (args, context?) => Promise<result>
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## Available Tools
|
|
101
|
+
|
|
102
|
+
| Tool | Description |
|
|
103
|
+
|------|-------------|
|
|
104
|
+
| `get_service_status` | Health status of business services with health score (0-100), monitor breakdown, and active incidents |
|
|
105
|
+
| `get_monitors` | List all monitors with operational status, response time, and uptime percentage |
|
|
106
|
+
| `get_monitor_health` | Detailed health for a specific monitor including response time trends and active predictions |
|
|
107
|
+
| `get_monitor_metrics` | Response time stats (avg, p95, p99), uptime percentage, and error breakdown |
|
|
108
|
+
| `get_incidents` | Active and resolved incidents with optional AI root cause analysis details |
|
|
109
|
+
| `get_predictions` | AI predictions for upcoming issues with confidence levels and predicted impact |
|
|
110
|
+
| `get_deployments` | Recent deployments with automatic incident correlation (GitHub/GitLab) |
|
|
111
|
+
| `run_root_cause_analysis` | AI-powered RCA using multi-model analysis (Claude, GPT) with deploy correlation |
|
|
112
|
+
| `is_safe_to_deploy` | CI/CD deploy safety check based on health scores, predictions, and active incidents |
|
|
113
|
+
| `get_executive_summary` | Infrastructure health summary for standups, weekly reports, or status updates |
|
|
114
|
+
|
|
115
|
+
## Authentication
|
|
116
|
+
|
|
117
|
+
Get your API key at [app.uptimebolt.io/settings/api-keys](https://app.uptimebolt.io/settings/api-keys).
|
|
118
|
+
|
|
119
|
+
- **stdio mode**: Set `UPTIMEBOLT_API_KEY` environment variable
|
|
120
|
+
- **HTTP mode**: Pass `x-api-key` header with each request (no startup key required)
|
|
121
|
+
|
|
122
|
+
## CI/CD Integration
|
|
123
|
+
|
|
124
|
+
Use `is_safe_to_deploy` as a gate in your deployment pipeline:
|
|
125
|
+
|
|
126
|
+
```bash
|
|
127
|
+
curl -X POST http://localhost:3100/mcp \
|
|
128
|
+
-H "Content-Type: application/json" \
|
|
129
|
+
-H "Accept: application/json, text/event-stream" \
|
|
130
|
+
-H "x-api-key: your-api-key" \
|
|
131
|
+
-d '{
|
|
132
|
+
"jsonrpc": "2.0",
|
|
133
|
+
"id": 1,
|
|
134
|
+
"method": "tools/call",
|
|
135
|
+
"params": {
|
|
136
|
+
"name": "is_safe_to_deploy",
|
|
137
|
+
"arguments": { "service_name": "my-service" }
|
|
138
|
+
}
|
|
139
|
+
}'
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
The response includes a risk level (`low`, `medium`, `high`), active issues, and a recommendation (`proceed`, `proceed_with_caution`, `wait_and_monitor`).
|
|
143
|
+
|
|
144
|
+
## Configuration
|
|
145
|
+
|
|
146
|
+
| Variable | Description | Default |
|
|
147
|
+
|----------|-------------|---------|
|
|
148
|
+
| `UPTIMEBOLT_API_KEY` | Your UptimeBolt API key | (required for stdio) |
|
|
149
|
+
| `UPTIMEBOLT_API_URL` | UptimeBolt API base URL | `http://localhost:3200` |
|
|
150
|
+
| `MCP_HTTP_PORT` | HTTP server port | `3100` |
|
|
151
|
+
| `NODE_ENV` | Environment (`production` disables console logs) | `development` |
|
|
152
|
+
| `LOG_LEVEL` | Log level (error, warn, info, debug) | `debug` (dev) / `info` (prod) |
|
|
153
|
+
|
|
154
|
+
## Development
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
git clone https://github.com/clm-cloud-solutions/uptimebolt-mcp-server.git
|
|
158
|
+
cd uptimebolt-mcp-server
|
|
159
|
+
cp .env.example .env # configure your environment
|
|
160
|
+
npm install
|
|
161
|
+
npm run dev # stdio mode
|
|
162
|
+
npm run dev:http # HTTP mode
|
|
163
|
+
npm run build # compile TypeScript
|
|
164
|
+
npm run typecheck # type check without emitting
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
## About UptimeBolt
|
|
168
|
+
|
|
169
|
+
[UptimeBolt](https://uptimebolt.com) is an AI-first SaaS monitoring platform for DevOps teams and SREs. Key capabilities:
|
|
170
|
+
|
|
171
|
+
- **Cascade failure prediction** — dependency graph with what-if analysis, downtime cost estimation, and proactive mitigations
|
|
172
|
+
- **Deploy correlation + RCA 2.0** — automatically correlates GitHub/GitLab deploys with incidents, identifies the responsible commit and files
|
|
173
|
+
- **AI Copilot** — conversational assistant with real-time infrastructure context
|
|
174
|
+
- **8 monitor types** — HTTP, TCP, DNS, Database, Email, Synthetic, Push, Ping
|
|
175
|
+
|
|
176
|
+
## About CLM Cloud Solutions
|
|
177
|
+
|
|
178
|
+
[CLM Cloud Solutions S.L.](https://clmcloudsolutions.es) is a technology company based in Madrid, Spain, building SaaS products for engineering teams to operate with confidence, speed, and security.
|
|
179
|
+
|
|
180
|
+
- [LinkedIn](https://www.linkedin.com/company/clm-cloud-solutions/)
|
|
181
|
+
- [GitHub](https://github.com/clm-cloud-solutions)
|
|
182
|
+
- Contact: info@clmcloudsolutions.es
|
|
183
|
+
|
|
184
|
+
## License
|
|
185
|
+
|
|
186
|
+
MIT
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export declare class ApiError extends Error {
|
|
2
|
+
status: number;
|
|
3
|
+
body?: any | undefined;
|
|
4
|
+
constructor(message: string, status: number, body?: any | undefined);
|
|
5
|
+
}
|
|
6
|
+
export declare const apiClient: {
|
|
7
|
+
get<T = any>(path: string, params?: Record<string, string | number | boolean | undefined>, timeout?: number, authToken?: string): Promise<T>;
|
|
8
|
+
post<T = any>(path: string, body?: any, timeout?: number, authToken?: string): Promise<T>;
|
|
9
|
+
};
|
|
10
|
+
export declare function validateApiKey(): void;
|
|
11
|
+
//# sourceMappingURL=api-client.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.d.ts","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"AAIA,qBAAa,QAAS,SAAQ,KAAK;IAGxB,MAAM,EAAE,MAAM;IACd,IAAI,CAAC,EAAE,GAAG;gBAFjB,OAAO,EAAE,MAAM,EACR,MAAM,EAAE,MAAM,EACd,IAAI,CAAC,EAAE,GAAG,YAAA;CAKpB;AAsDD,eAAO,MAAM,SAAS;QAChB,CAAC,cAAc,MAAM,WAAW,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,SAAS,CAAC,YAAY,MAAM,cAAc,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;SAIvI,CAAC,cAAc,MAAM,SAAS,GAAG,YAAY,MAAM,cAAc,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC;CAG1F,CAAC;AAEF,wBAAgB,cAAc,IAAI,IAAI,CAUrC"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apiClient = exports.ApiError = void 0;
|
|
4
|
+
exports.validateApiKey = validateApiKey;
|
|
5
|
+
const BASE_URL = process.env.UPTIMEBOLT_API_URL || "http://localhost:3200";
|
|
6
|
+
const API_KEY = process.env.UPTIMEBOLT_API_KEY || "";
|
|
7
|
+
const API_PREFIX = "/api/v1";
|
|
8
|
+
class ApiError extends Error {
|
|
9
|
+
status;
|
|
10
|
+
body;
|
|
11
|
+
constructor(message, status, body) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.status = status;
|
|
14
|
+
this.body = body;
|
|
15
|
+
this.name = "ApiError";
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.ApiError = ApiError;
|
|
19
|
+
async function request(method, path, options) {
|
|
20
|
+
const url = new URL(`${API_PREFIX}${path}`, BASE_URL);
|
|
21
|
+
if (options?.params) {
|
|
22
|
+
for (const [key, value] of Object.entries(options.params)) {
|
|
23
|
+
if (value !== undefined && value !== null) {
|
|
24
|
+
url.searchParams.set(key, String(value));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
const controller = new AbortController();
|
|
29
|
+
const timeoutMs = options?.timeout || 30000;
|
|
30
|
+
const timer = setTimeout(() => controller.abort(), timeoutMs);
|
|
31
|
+
try {
|
|
32
|
+
const resp = await fetch(url.toString(), {
|
|
33
|
+
method,
|
|
34
|
+
headers: {
|
|
35
|
+
...(options?.authToken
|
|
36
|
+
? { Authorization: `Bearer ${options.authToken}` }
|
|
37
|
+
: { "x-api-key": API_KEY }),
|
|
38
|
+
"Content-Type": "application/json",
|
|
39
|
+
Accept: "application/json",
|
|
40
|
+
},
|
|
41
|
+
body: options?.body ? JSON.stringify(options.body) : undefined,
|
|
42
|
+
signal: controller.signal,
|
|
43
|
+
});
|
|
44
|
+
const json = await resp.json().catch(() => null);
|
|
45
|
+
if (!resp.ok) {
|
|
46
|
+
const message = json?.message || json?.error || `HTTP ${resp.status} ${resp.statusText}`;
|
|
47
|
+
throw new ApiError(message, resp.status, json);
|
|
48
|
+
}
|
|
49
|
+
return json?.data !== undefined ? json.data : json;
|
|
50
|
+
}
|
|
51
|
+
catch (err) {
|
|
52
|
+
if (err instanceof ApiError)
|
|
53
|
+
throw err;
|
|
54
|
+
if (err?.name === "AbortError") {
|
|
55
|
+
throw new ApiError(`Request timed out after ${timeoutMs}ms`, 408);
|
|
56
|
+
}
|
|
57
|
+
throw new ApiError(err.message, 0);
|
|
58
|
+
}
|
|
59
|
+
finally {
|
|
60
|
+
clearTimeout(timer);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
exports.apiClient = {
|
|
64
|
+
get(path, params, timeout, authToken) {
|
|
65
|
+
return request("GET", path, { params, timeout, authToken });
|
|
66
|
+
},
|
|
67
|
+
post(path, body, timeout, authToken) {
|
|
68
|
+
return request("POST", path, { body, timeout, authToken });
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
function validateApiKey() {
|
|
72
|
+
if (!API_KEY) {
|
|
73
|
+
process.stderr.write("Error: UPTIMEBOLT_API_KEY environment variable is required (stdio mode).\n" +
|
|
74
|
+
"Set it to your UptimeBolt API key.\n\n" +
|
|
75
|
+
"Usage:\n" +
|
|
76
|
+
" UPTIMEBOLT_API_KEY=your-key UPTIMEBOLT_API_URL=https://api.uptimebolt.io node dist/server.js\n\n");
|
|
77
|
+
process.exit(1);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
//# sourceMappingURL=api-client.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client.js","sourceRoot":"","sources":["../src/api-client.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
export declare function formatServiceStatus(data: any): string;
|
|
2
|
+
export declare function formatPredictions(predictions: any[]): string;
|
|
3
|
+
export declare function formatIncidents(incidents: any[], rcas?: Map<string, any>): string;
|
|
4
|
+
export declare function formatMonitorHealth(monitor: any, predictions?: any[]): string;
|
|
5
|
+
export declare function formatDeployments(deployments: any[]): string;
|
|
6
|
+
export declare function formatRca(rca: any): string;
|
|
7
|
+
export declare function formatSafetyCheck(result: {
|
|
8
|
+
safe: boolean;
|
|
9
|
+
riskLevel: string;
|
|
10
|
+
reason: string;
|
|
11
|
+
activeIssues: any[];
|
|
12
|
+
recommendation: string;
|
|
13
|
+
}): string;
|
|
14
|
+
export declare function formatExecutiveSummary(data: any): string;
|
|
15
|
+
export declare function formatMonitors(monitors: any[]): string;
|
|
16
|
+
export declare function formatMonitorMetrics(summary: any): string;
|
|
17
|
+
//# sourceMappingURL=formatters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"formatters.d.ts","sourceRoot":"","sources":["../src/formatters.ts"],"names":[],"mappings":"AA4CA,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAmCrD;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,GAAG,EAAE,GAAG,MAAM,CAQ5D;AAED,wBAAgB,eAAe,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE,IAAI,CAAC,EAAE,GAAG,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,MAAM,CAkBjF;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,GAAG,EAAE,WAAW,CAAC,EAAE,GAAG,EAAE,GAAG,MAAM,CAiB7E;AAED,wBAAgB,iBAAiB,CAAC,WAAW,EAAE,GAAG,EAAE,GAAG,MAAM,CAiB5D;AAED,wBAAgB,SAAS,CAAC,GAAG,EAAE,GAAG,GAAG,MAAM,CA2C1C;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,YAAY,EAAE,GAAG,EAAE,CAAC;IAAC,cAAc,EAAE,MAAM,CAAA;CAAE,GAAG,MAAM,CAenJ;AAED,wBAAgB,sBAAsB,CAAC,IAAI,EAAE,GAAG,GAAG,MAAM,CAsBxD;AAED,wBAAgB,cAAc,CAAC,QAAQ,EAAE,GAAG,EAAE,GAAG,MAAM,CAsCtD;AAED,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,GAAG,GAAG,MAAM,CA8CzD"}
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Formatters convert API JSON responses to concise text for LLM consumption
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
exports.formatServiceStatus = formatServiceStatus;
|
|
5
|
+
exports.formatPredictions = formatPredictions;
|
|
6
|
+
exports.formatIncidents = formatIncidents;
|
|
7
|
+
exports.formatMonitorHealth = formatMonitorHealth;
|
|
8
|
+
exports.formatDeployments = formatDeployments;
|
|
9
|
+
exports.formatRca = formatRca;
|
|
10
|
+
exports.formatSafetyCheck = formatSafetyCheck;
|
|
11
|
+
exports.formatExecutiveSummary = formatExecutiveSummary;
|
|
12
|
+
exports.formatMonitors = formatMonitors;
|
|
13
|
+
exports.formatMonitorMetrics = formatMonitorMetrics;
|
|
14
|
+
function statusIcon(status) {
|
|
15
|
+
const s = status?.toLowerCase();
|
|
16
|
+
if (s === "up" || s === "active" || s === "healthy" || s === "resolved")
|
|
17
|
+
return "[UP]";
|
|
18
|
+
if (s === "degraded" || s === "warning" || s === "caution")
|
|
19
|
+
return "[DEGRADED]";
|
|
20
|
+
if (s === "down" || s === "critical" || s === "detecting" || s === "investigating")
|
|
21
|
+
return "[DOWN]";
|
|
22
|
+
if (s === "paused")
|
|
23
|
+
return "[PAUSED]";
|
|
24
|
+
if (s === "maintenance")
|
|
25
|
+
return "[MAINTENANCE]";
|
|
26
|
+
return `[${(status || "unknown").toUpperCase()}]`;
|
|
27
|
+
}
|
|
28
|
+
function priorityLabel(p) {
|
|
29
|
+
return `[${(p || "unknown").toUpperCase()}]`;
|
|
30
|
+
}
|
|
31
|
+
function pct(n) {
|
|
32
|
+
return n != null ? `${Number(n).toFixed(1)}%` : "N/A";
|
|
33
|
+
}
|
|
34
|
+
function ms(n) {
|
|
35
|
+
return n != null ? `${Math.round(n)}ms` : "N/A";
|
|
36
|
+
}
|
|
37
|
+
function ago(date) {
|
|
38
|
+
if (!date)
|
|
39
|
+
return "N/A";
|
|
40
|
+
const diff = Date.now() - new Date(date).getTime();
|
|
41
|
+
const mins = Math.floor(diff / 60000);
|
|
42
|
+
if (mins < 60)
|
|
43
|
+
return `${mins}m ago`;
|
|
44
|
+
const hours = Math.floor(mins / 60);
|
|
45
|
+
if (hours < 24)
|
|
46
|
+
return `${hours}h ago`;
|
|
47
|
+
return `${Math.floor(hours / 24)}d ago`;
|
|
48
|
+
}
|
|
49
|
+
function truncateList(items, max, formatter) {
|
|
50
|
+
const shown = items.slice(0, max).map(formatter);
|
|
51
|
+
if (items.length > max) {
|
|
52
|
+
shown.push(`... and ${items.length - max} more`);
|
|
53
|
+
}
|
|
54
|
+
return shown.join("\n");
|
|
55
|
+
}
|
|
56
|
+
// --- Tool formatters ---
|
|
57
|
+
function formatServiceStatus(data) {
|
|
58
|
+
if (Array.isArray(data)) {
|
|
59
|
+
if (data.length === 0)
|
|
60
|
+
return "No services found.";
|
|
61
|
+
const totalMonitors = data.reduce((sum, s) => sum + (s.serviceMonitors?.length || 0), 0);
|
|
62
|
+
return `${data.length} services (${totalMonitors} monitors total):\n\n` + truncateList(data, 20, (s) => {
|
|
63
|
+
const health = s.currentHealthScore != null ? ` | Health: ${pct(s.currentHealthScore)}` : "";
|
|
64
|
+
const monitors = s.serviceMonitors || [];
|
|
65
|
+
const monitorCount = monitors.length > 0 ? ` | ${monitors.length} monitors` : "";
|
|
66
|
+
let line = `- ${s.name} (${s.environment || "unknown"})${health}${monitorCount} | ${s.criticality || "normal"} criticality\n ID: ${s.id}`;
|
|
67
|
+
if (monitors.length > 0) {
|
|
68
|
+
line += "\n" + monitors.map((sm) => {
|
|
69
|
+
const m = sm.monitor || sm;
|
|
70
|
+
return ` ${statusIcon(m.operationalStatus || m.status)} ${m.name} (${m.type})`;
|
|
71
|
+
}).join("\n");
|
|
72
|
+
}
|
|
73
|
+
return line;
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
const s = data;
|
|
77
|
+
const monitors = s.serviceMonitors || [];
|
|
78
|
+
let text = `Service: ${s.name}\n`;
|
|
79
|
+
text += `Environment: ${s.environment || "unknown"} | Criticality: ${s.criticality || "normal"}\n`;
|
|
80
|
+
if (s.healthScore != null)
|
|
81
|
+
text += `Health Score: ${pct(s.healthScore)}\n`;
|
|
82
|
+
if (s.description)
|
|
83
|
+
text += `Description: ${s.description}\n`;
|
|
84
|
+
if (monitors.length > 0) {
|
|
85
|
+
text += `\nMonitors (${monitors.length}):\n`;
|
|
86
|
+
text += truncateList(monitors, 15, (sm) => {
|
|
87
|
+
const m = sm.monitor || sm;
|
|
88
|
+
return ` ${statusIcon(m.operationalStatus || m.status)} ${m.name} (${m.type}) | Response: ${ms(m.responseTime)} | Uptime: ${pct(m.uptimePercentage)}`;
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
return text;
|
|
92
|
+
}
|
|
93
|
+
function formatPredictions(predictions) {
|
|
94
|
+
if (!predictions || predictions.length === 0)
|
|
95
|
+
return "No active predictions.";
|
|
96
|
+
return `${predictions.length} active predictions:\n\n` + truncateList(predictions, 10, (p) => {
|
|
97
|
+
const confidence = p.confidence != null ? `${Math.round((p.confidence > 1 ? p.confidence : p.confidence * 100))}%` : "N/A";
|
|
98
|
+
const monitor = p.monitor?.name || p.monitorId || "unknown";
|
|
99
|
+
return `- ${priorityLabel(p.severity || p.predictionType)} ${p.predictionType} | Monitor: ${monitor} | Confidence: ${confidence} | Window: ${p.timeWindow || "N/A"}`;
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
function formatIncidents(incidents, rcas) {
|
|
103
|
+
if (!incidents || incidents.length === 0)
|
|
104
|
+
return "No incidents found.";
|
|
105
|
+
return `${incidents.length} incidents:\n\n` + truncateList(incidents, 10, (inc) => {
|
|
106
|
+
const dur = inc.downtimeDuration != null ? `${Math.round(inc.downtimeDuration / 60)}min` : "ongoing";
|
|
107
|
+
let text = `- ${statusIcon(inc.status)} ${priorityLabel(inc.severity)} ${inc.title || "Untitled"}\n`;
|
|
108
|
+
text += ` ID: ${inc.id} | Monitor: ${inc.monitor?.name || inc.monitorId || "unknown"} | Duration: ${dur} | Started: ${ago(inc.startTime || inc.createdAt)}`;
|
|
109
|
+
if (inc.errorCode)
|
|
110
|
+
text += ` | Error: ${inc.errorCode}`;
|
|
111
|
+
const rca = rcas?.get(inc.id);
|
|
112
|
+
if (rca) {
|
|
113
|
+
text += `\n RCA: ${rca.rootCauseSummary || "N/A"} (${rca.rootCauseType || "unknown"}, confidence: ${pct(rca.confidenceScore)})`;
|
|
114
|
+
if (rca.cascadeDetected)
|
|
115
|
+
text += ` [CASCADE]`;
|
|
116
|
+
if (rca.correlatedDeploymentId)
|
|
117
|
+
text += ` [DEPLOY-RELATED]`;
|
|
118
|
+
}
|
|
119
|
+
return text;
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
function formatMonitorHealth(monitor, predictions) {
|
|
123
|
+
const m = monitor;
|
|
124
|
+
let text = `Monitor: ${m.name}\n`;
|
|
125
|
+
text += `ID: ${m.id}\n`;
|
|
126
|
+
text += `Type: ${m.type} | Status: ${statusIcon(m.operationalStatus || m.status)} ${m.operationalStatus || m.status}\n`;
|
|
127
|
+
text += `Response Time: ${ms(m.responseTime)} | Uptime: ${pct(m.uptimePercentage)}\n`;
|
|
128
|
+
if (m.target)
|
|
129
|
+
text += `Target: ${m.target}\n`;
|
|
130
|
+
if (predictions && predictions.length > 0) {
|
|
131
|
+
text += `\nActive Predictions (${predictions.length}):\n`;
|
|
132
|
+
text += truncateList(predictions, 5, (p) => {
|
|
133
|
+
const confidence = p.confidence != null ? `${Math.round((p.confidence > 1 ? p.confidence : p.confidence * 100))}%` : "N/A";
|
|
134
|
+
return ` - ${p.predictionType} | Confidence: ${confidence} | Window: ${p.timeWindow || "N/A"}`;
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
return text;
|
|
138
|
+
}
|
|
139
|
+
function formatDeployments(deployments) {
|
|
140
|
+
if (!deployments || deployments.length === 0)
|
|
141
|
+
return "No recent deployments.";
|
|
142
|
+
return `${deployments.length} deployments:\n\n` + truncateList(deployments, 15, (d) => {
|
|
143
|
+
const status = d.deploymentStatus || d.status || "unknown";
|
|
144
|
+
const msg = (d.commitMessage || "").substring(0, 80);
|
|
145
|
+
let text = `- ${statusIcon(status)} ${msg} (${d.branch || "N/A"})\n`;
|
|
146
|
+
text += ` Author: ${d.commitAuthor || "unknown"} | SHA: ${(d.commitSha || "").substring(0, 8)} | ${ago(d.deployedAt)}`;
|
|
147
|
+
if (d.filesChanged)
|
|
148
|
+
text += ` | ${d.filesChanged} files`;
|
|
149
|
+
const correlations = d.correlations || [];
|
|
150
|
+
if (correlations.length > 0) {
|
|
151
|
+
text += `\n Correlations: ${correlations.map((c) => `score=${c.correlationScore} (${c.confidence})`).join(", ")}`;
|
|
152
|
+
}
|
|
153
|
+
return text;
|
|
154
|
+
});
|
|
155
|
+
}
|
|
156
|
+
function formatRca(rca) {
|
|
157
|
+
let text = `Root Cause Analysis\n`;
|
|
158
|
+
text += `${"=".repeat(40)}\n\n`;
|
|
159
|
+
text += `RCA ID: ${rca.id || "N/A"}\n`;
|
|
160
|
+
text += `Summary: ${rca.rootCauseSummary || "N/A"}\n`;
|
|
161
|
+
text += `Type: ${rca.rootCauseType || "unknown"} | Confidence: ${pct(rca.confidenceScore)}\n`;
|
|
162
|
+
text += `Model: ${rca.aiModelUsed || "unknown"} | Duration: ${rca.analysisDurationMs ? `${Math.round(rca.analysisDurationMs / 1000)}s` : "N/A"}\n`;
|
|
163
|
+
if (rca.cascadeDetected) {
|
|
164
|
+
text += `\nCascade Detected: Yes | Origin: ${rca.cascadeOriginMonitorId || "unknown"}`;
|
|
165
|
+
text += ` | Affected: ${rca.affectedMonitorsCount || 0} monitors, ${rca.affectedServicesCount || 0} services\n`;
|
|
166
|
+
}
|
|
167
|
+
if (rca.correlatedDeploymentId) {
|
|
168
|
+
text += `\nDeploy Correlated: Yes | Score: ${rca.deployCorrelationScore || "N/A"}\n`;
|
|
169
|
+
}
|
|
170
|
+
const analysis = rca.detailedAnalysis;
|
|
171
|
+
if (analysis) {
|
|
172
|
+
if (analysis.timeline?.length) {
|
|
173
|
+
text += `\nTimeline:\n`;
|
|
174
|
+
text += truncateList(analysis.timeline, 8, (t) => ` ${t.time}: ${t.event}`);
|
|
175
|
+
}
|
|
176
|
+
if (analysis.deployAnalysis?.suspectedLines?.length) {
|
|
177
|
+
text += `\n\nSuspected Code Changes:\n`;
|
|
178
|
+
text += truncateList(analysis.deployAnalysis.suspectedLines, 5, (sl) => ` - ${sl.filename}: ${sl.change}\n Why: ${sl.explanation}\n Fix: ${sl.suggestedFix}`);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (rca.suggestedActions?.length) {
|
|
182
|
+
text += `\n\nSuggested Actions:\n`;
|
|
183
|
+
text += truncateList(rca.suggestedActions, 5, (a) => ` [${a.urgency}] ${a.action}`);
|
|
184
|
+
}
|
|
185
|
+
if (rca.preventionRecommendations?.length) {
|
|
186
|
+
text += `\n\nPrevention:\n`;
|
|
187
|
+
text += truncateList(rca.preventionRecommendations, 5, (r) => ` [${r.priority}] ${r.action}`);
|
|
188
|
+
}
|
|
189
|
+
return text;
|
|
190
|
+
}
|
|
191
|
+
function formatSafetyCheck(result) {
|
|
192
|
+
const icon = result.safe ? "[SAFE]" : result.riskLevel === "high" ? "[UNSAFE]" : "[CAUTION]";
|
|
193
|
+
let text = `${icon} Deploy Safety Check\n\n`;
|
|
194
|
+
text += `Risk Level: ${result.riskLevel}\n`;
|
|
195
|
+
text += `Recommendation: ${result.recommendation}\n\n`;
|
|
196
|
+
text += `${result.reason}\n`;
|
|
197
|
+
if (result.activeIssues.length > 0) {
|
|
198
|
+
text += `\nActive Issues:\n`;
|
|
199
|
+
text += truncateList(result.activeIssues, 10, (i) => ` - [${i.type}] ${i.message}${i.confidence ? ` (confidence: ${i.confidence}%)` : ""}`);
|
|
200
|
+
}
|
|
201
|
+
return text;
|
|
202
|
+
}
|
|
203
|
+
function formatExecutiveSummary(data) {
|
|
204
|
+
let text = data.summary || "No summary available.";
|
|
205
|
+
if (data.metrics) {
|
|
206
|
+
const m = data.metrics;
|
|
207
|
+
text += `\n\nMetrics:\n`;
|
|
208
|
+
text += ` Overall Health: ${pct(m.overallHealth)}\n`;
|
|
209
|
+
text += ` Monitors: ${m.totalMonitors || 0} total (${m.monitorsUp || 0} up, ${m.monitorsDegraded || 0} degraded, ${m.monitorsDown || 0} down)\n`;
|
|
210
|
+
text += ` Incidents: ${m.incidentsInPeriod || 0} | Predictions: ${m.predictionsActive || 0} | Deployments: ${m.deploymentsInPeriod || 0}`;
|
|
211
|
+
}
|
|
212
|
+
if (data.highlights?.length) {
|
|
213
|
+
text += `\n\nHighlights:\n`;
|
|
214
|
+
text += truncateList(data.highlights, 5, (h) => ` - [${h.type}] ${h.message}`);
|
|
215
|
+
}
|
|
216
|
+
if (data.suggestedQuestions?.length) {
|
|
217
|
+
text += `\n\nSuggested Questions:\n`;
|
|
218
|
+
text += data.suggestedQuestions.map((q) => ` - ${q}`).join("\n");
|
|
219
|
+
}
|
|
220
|
+
return text;
|
|
221
|
+
}
|
|
222
|
+
function formatMonitors(monitors) {
|
|
223
|
+
if (!monitors || monitors.length === 0)
|
|
224
|
+
return "No monitors found.";
|
|
225
|
+
// Effective status: use administrative status (paused/maintenance) when not active,
|
|
226
|
+
// otherwise use operationalStatus (up/down/degraded)
|
|
227
|
+
function effectiveStatus(m) {
|
|
228
|
+
const admin = (m.status || "active").toLowerCase();
|
|
229
|
+
if (admin === "paused" || admin === "maintenance")
|
|
230
|
+
return admin;
|
|
231
|
+
return (m.operationalStatus || "up").toLowerCase();
|
|
232
|
+
}
|
|
233
|
+
const statusCounts = {};
|
|
234
|
+
const typeCounts = {};
|
|
235
|
+
for (const m of monitors) {
|
|
236
|
+
const s = effectiveStatus(m);
|
|
237
|
+
statusCounts[s] = (statusCounts[s] || 0) + 1;
|
|
238
|
+
const t = (m.type || "unknown").toLowerCase();
|
|
239
|
+
typeCounts[t] = (typeCounts[t] || 0) + 1;
|
|
240
|
+
}
|
|
241
|
+
const statusParts = Object.entries(statusCounts).map(([s, c]) => `${c} ${s}`).join(", ");
|
|
242
|
+
const typeParts = Object.entries(typeCounts).map(([t, c]) => `${c} ${t}`).join(", ");
|
|
243
|
+
// Sort: down first, then degraded, then paused, then up
|
|
244
|
+
const order = { down: 0, critical: 0, degraded: 1, warning: 1, paused: 2, maintenance: 3, up: 4 };
|
|
245
|
+
const sorted = [...monitors].sort((a, b) => {
|
|
246
|
+
return (order[effectiveStatus(a)] ?? 5) - (order[effectiveStatus(b)] ?? 5);
|
|
247
|
+
});
|
|
248
|
+
let text = `${monitors.length} monitors (${statusParts}):\n`;
|
|
249
|
+
text += `By type: ${typeParts}\n\n`;
|
|
250
|
+
text += truncateList(sorted, 20, (m) => {
|
|
251
|
+
const es = effectiveStatus(m);
|
|
252
|
+
const icon = statusIcon(es === "paused" ? "paused" : es === "maintenance" ? "maintenance" : (m.operationalStatus || "unknown"));
|
|
253
|
+
return `- ${icon} ${m.name} (${m.type || "unknown"}) | Response: ${ms(m.responseTime)} | Uptime: ${pct(m.uptimePercentage)}\n ID: ${m.id}`;
|
|
254
|
+
});
|
|
255
|
+
return text;
|
|
256
|
+
}
|
|
257
|
+
function formatMonitorMetrics(summary) {
|
|
258
|
+
const m = summary.monitor || summary;
|
|
259
|
+
const cs = summary.currentStatus || {};
|
|
260
|
+
const rt = summary.responseTime || {};
|
|
261
|
+
const up = summary.uptime || {};
|
|
262
|
+
const checks = summary.checks || {};
|
|
263
|
+
const ssl = summary.sslCertificate || null;
|
|
264
|
+
let text = `Monitor: ${m.name || "Unknown"} (${m.type || "unknown"})\n`;
|
|
265
|
+
text += `Status: ${statusIcon(cs.operationalStatus || m.operationalStatus || m.status || "unknown")} | Target: ${m.target || "N/A"}\n`;
|
|
266
|
+
text += `ID: ${m.id || "N/A"}\n`;
|
|
267
|
+
text += `\nResponse Time:\n`;
|
|
268
|
+
text += ` Current: ${ms(rt.current ?? cs.lastResponseTime)} | Day avg: ${ms(rt.avgDay)} | Week avg: ${ms(rt.avgWeek)} | Month avg: ${ms(rt.avgMonth)} | Year avg: ${ms(rt.avgYear)}\n`;
|
|
269
|
+
text += `\nUptime:\n`;
|
|
270
|
+
text += ` Day: ${pct(up.day)} | Week: ${pct(up.week)} | Month: ${pct(up.month)} | Year: ${pct(up.year)}\n`;
|
|
271
|
+
if (checks.day || checks.week || checks.month || checks.year) {
|
|
272
|
+
text += `\nChecks:\n`;
|
|
273
|
+
if (checks.day) {
|
|
274
|
+
text += ` Last 24h: ${checks.day.total || 0} total | ${checks.day.up || 0} up | ${checks.day.down || 0} down\n`;
|
|
275
|
+
}
|
|
276
|
+
if (checks.week) {
|
|
277
|
+
text += ` Last 7d: ${checks.week.total || 0} total | ${checks.week.up || 0} up | ${checks.week.down || 0} down\n`;
|
|
278
|
+
}
|
|
279
|
+
if (checks.month) {
|
|
280
|
+
text += ` Last 30d: ${checks.month.total || 0} total | ${checks.month.up || 0} up | ${checks.month.down || 0} down\n`;
|
|
281
|
+
}
|
|
282
|
+
if (checks.year) {
|
|
283
|
+
text += ` Last 365d: ${checks.year.total || 0} total | ${checks.year.up || 0} up | ${checks.year.down || 0} down\n`;
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
if (ssl) {
|
|
287
|
+
const expiresAt = ssl.expiresAt || ssl.validUntil;
|
|
288
|
+
const daysRemaining = ssl.daysRemaining != null
|
|
289
|
+
? ssl.daysRemaining
|
|
290
|
+
: (expiresAt ? Math.floor((new Date(expiresAt).getTime() - Date.now()) / 86400000) : null);
|
|
291
|
+
text += `\nSSL Certificate:\n`;
|
|
292
|
+
text += ` Status: ${ssl.status || "unknown"} | Issuer: ${ssl.issuer || "N/A"}`;
|
|
293
|
+
if (expiresAt)
|
|
294
|
+
text += ` | Expires: ${expiresAt}`;
|
|
295
|
+
if (daysRemaining != null)
|
|
296
|
+
text += ` | Days remaining: ${daysRemaining}`;
|
|
297
|
+
}
|
|
298
|
+
return text;
|
|
299
|
+
}
|
|
300
|
+
//# sourceMappingURL=formatters.js.map
|