@prajwolkc/stk 0.1.1 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +112 -5
- package/dist/commands/doctor.d.ts +2 -0
- package/dist/commands/doctor.js +220 -0
- package/dist/commands/health.js +29 -4
- package/dist/commands/logs.js +202 -97
- package/dist/index.js +3 -1
- package/dist/lib/output.d.ts +7 -0
- package/dist/lib/output.js +14 -0
- package/dist/lib/plugins.d.ts +40 -0
- package/dist/lib/plugins.js +65 -0
- package/dist/mcp/server.d.ts +9 -0
- package/dist/mcp/server.js +385 -0
- package/dist/services/aws.js +39 -10
- package/dist/services/database.js +25 -6
- package/dist/services/registry.d.ts +1 -0
- package/dist/services/registry.js +16 -2
- package/package.json +10 -4
package/README.md
CHANGED
|
@@ -4,10 +4,13 @@ One CLI to deploy, monitor, and debug your entire stack.
|
|
|
4
4
|
|
|
5
5
|
Stop opening 5 dashboards. `stk` checks your services, watches your deploys, syncs your env vars, tails your logs, and manages your issues — all from one command.
|
|
6
6
|
|
|
7
|
+
<!-- TODO: Add demo GIF here -->
|
|
8
|
+
<!--  -->
|
|
9
|
+
|
|
7
10
|
## Install
|
|
8
11
|
|
|
9
12
|
```bash
|
|
10
|
-
npm install -g stk
|
|
13
|
+
npm install -g @prajwolkc/stk
|
|
11
14
|
```
|
|
12
15
|
|
|
13
16
|
## Quick Start
|
|
@@ -16,6 +19,7 @@ npm install -g stk-cli
|
|
|
16
19
|
cd my-project
|
|
17
20
|
stk init # auto-detect your services
|
|
18
21
|
stk init --template saas # or use a starter template
|
|
22
|
+
stk doctor # diagnose any misconfig
|
|
19
23
|
stk health # check everything
|
|
20
24
|
stk status # one-line summary of your whole stack
|
|
21
25
|
```
|
|
@@ -27,10 +31,12 @@ stk status # one-line summary of your whole stack
|
|
|
27
31
|
| `stk init` | Initialize config (auto-detect or `--template saas\|api\|fullstack\|static\|fly\|aws`) |
|
|
28
32
|
| `stk status` | One-line summary: git, services, deploys, issues |
|
|
29
33
|
| `stk health` | Health check all configured services |
|
|
30
|
-
| `stk
|
|
34
|
+
| `stk doctor` | Diagnose misconfig, missing env vars, and suggest fixes |
|
|
35
|
+
| `stk deploy` | Git push + watch deploy providers |
|
|
31
36
|
| `stk env pull` | Pull env vars from Vercel + Railway into `.env.pulled` |
|
|
32
37
|
| `stk env diff` | Show what's in your local `.env` |
|
|
33
|
-
| `stk logs` | Tail Railway
|
|
38
|
+
| `stk logs` | Tail logs from Railway, Vercel, Fly, or Render |
|
|
39
|
+
| `stk logs -p vercel` | Logs from a specific provider |
|
|
34
40
|
| `stk todo ls` | List open GitHub issues |
|
|
35
41
|
| `stk todo add "title"` | Create a GitHub issue |
|
|
36
42
|
| `stk todo close 42` | Close an issue |
|
|
@@ -40,6 +46,7 @@ stk status # one-line summary of your whole stack
|
|
|
40
46
|
**Deploy providers:** Railway, Vercel, Fly.io, Render, AWS
|
|
41
47
|
**Databases:** PostgreSQL, MongoDB, Redis, Supabase
|
|
42
48
|
**Storage & billing:** Cloudflare R2, Stripe
|
|
49
|
+
**Custom:** Add your own via plugins
|
|
43
50
|
|
|
44
51
|
## Configuration
|
|
45
52
|
|
|
@@ -82,15 +89,57 @@ stk init --list-templates
|
|
|
82
89
|
| `fly` | Fly.io + PostgreSQL + Redis |
|
|
83
90
|
| `aws` | AWS + PostgreSQL + Redis |
|
|
84
91
|
|
|
85
|
-
##
|
|
92
|
+
## Doctor
|
|
93
|
+
|
|
94
|
+
`stk doctor` scans your config and environment to catch issues before they bite:
|
|
95
|
+
|
|
96
|
+
```
|
|
97
|
+
$ stk doctor
|
|
98
|
+
|
|
99
|
+
my-saas — Doctor
|
|
100
|
+
─────────────────────────────────────────
|
|
101
|
+
✓ railway Configured correctly
|
|
102
|
+
✗ vercel Missing required: VERCEL_TOKEN
|
|
103
|
+
See https://vercel.com/account/tokens
|
|
104
|
+
! database Missing optional: RAILWAY_PROJECT_ID
|
|
105
|
+
Some features need these for full functionality
|
|
106
|
+
✓ stripe Configured correctly
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Plugins
|
|
110
|
+
|
|
111
|
+
Add custom services without forking. Create `.stk/plugins/my-service.mjs`:
|
|
112
|
+
|
|
113
|
+
```js
|
|
114
|
+
export default {
|
|
115
|
+
name: "my-plugin",
|
|
116
|
+
services: {
|
|
117
|
+
myservice: {
|
|
118
|
+
name: "My Service",
|
|
119
|
+
envVars: ["MY_SERVICE_TOKEN"],
|
|
120
|
+
healthCheck: async () => {
|
|
121
|
+
const token = process.env.MY_SERVICE_TOKEN;
|
|
122
|
+
if (!token) {
|
|
123
|
+
return { name: "My Service", status: "skipped", detail: "MY_SERVICE_TOKEN not set" };
|
|
124
|
+
}
|
|
125
|
+
// Your check logic here
|
|
126
|
+
return { name: "My Service", status: "healthy", detail: "connected" };
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
```
|
|
86
132
|
|
|
87
|
-
|
|
133
|
+
Plugins are automatically loaded by `stk health` and `stk health --all`.
|
|
134
|
+
|
|
135
|
+
## Environment Variables
|
|
88
136
|
|
|
89
137
|
```bash
|
|
90
138
|
# Deploy providers
|
|
91
139
|
RAILWAY_API_TOKEN=
|
|
92
140
|
VERCEL_TOKEN=
|
|
93
141
|
FLY_API_TOKEN=
|
|
142
|
+
FLY_APP_NAME= # needed for stk logs -p fly
|
|
94
143
|
RENDER_API_KEY=
|
|
95
144
|
AWS_ACCESS_KEY_ID= / AWS_SECRET_ACCESS_KEY=
|
|
96
145
|
|
|
@@ -109,6 +158,64 @@ GITHUB_TOKEN=
|
|
|
109
158
|
GITHUB_REPO=owner/repo # or auto-detected from git remote
|
|
110
159
|
```
|
|
111
160
|
|
|
161
|
+
## Claude Code / MCP Integration
|
|
162
|
+
|
|
163
|
+
`stk` ships with a built-in MCP server so Claude Code can use your infrastructure as native tools.
|
|
164
|
+
|
|
165
|
+
### Setup
|
|
166
|
+
|
|
167
|
+
1. Install stk globally:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
npm install -g @prajwolkc/stk
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
2. Add to your project's `.mcp.json` (create it in your project root):
|
|
174
|
+
|
|
175
|
+
```json
|
|
176
|
+
{
|
|
177
|
+
"mcpServers": {
|
|
178
|
+
"stk": {
|
|
179
|
+
"command": "stk-mcp",
|
|
180
|
+
"args": []
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
3. Restart Claude Code. Approve the stk MCP server when prompted.
|
|
187
|
+
|
|
188
|
+
### What Claude can do
|
|
189
|
+
|
|
190
|
+
| Tool | Description |
|
|
191
|
+
|------|-------------|
|
|
192
|
+
| `stk_health` | Check if all services are up before writing code |
|
|
193
|
+
| `stk_status` | Full overview: git, services, deploys, issues |
|
|
194
|
+
| `stk_doctor` | Diagnose misconfig and missing env vars |
|
|
195
|
+
| `stk_logs` | Read production logs to understand bugs |
|
|
196
|
+
| `stk_todo_list` | See what needs to be worked on |
|
|
197
|
+
| `stk_todo_add` | Create GitHub issues |
|
|
198
|
+
| `stk_deploy` | Push code and trigger deploys |
|
|
199
|
+
| `stk_config` | Read the project's stack config |
|
|
200
|
+
|
|
201
|
+
### Example prompts
|
|
202
|
+
|
|
203
|
+
- *"Check if all my services are healthy"*
|
|
204
|
+
- *"What errors are in my production logs?"*
|
|
205
|
+
- *"What should I work on next?"*
|
|
206
|
+
- *"Deploy this and verify it worked"*
|
|
207
|
+
|
|
208
|
+
## Development
|
|
209
|
+
|
|
210
|
+
```bash
|
|
211
|
+
git clone https://github.com/Harden43/stk.git
|
|
212
|
+
cd stk
|
|
213
|
+
npm install
|
|
214
|
+
npm run dev -- health --all # run in dev mode
|
|
215
|
+
npm test # run tests
|
|
216
|
+
npm run build # compile TypeScript
|
|
217
|
+
```
|
|
218
|
+
|
|
112
219
|
## License
|
|
113
220
|
|
|
114
221
|
MIT
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import { Command } from "commander";
|
|
2
|
+
import chalk from "chalk";
|
|
3
|
+
import { existsSync } from "fs";
|
|
4
|
+
import { loadConfig, enabledServices, CONFIG_FILE } from "../lib/config.js";
|
|
5
|
+
const ENV_REQUIREMENTS = {
|
|
6
|
+
railway: {
|
|
7
|
+
required: ["RAILWAY_API_TOKEN"],
|
|
8
|
+
optional: ["RAILWAY_PROJECT_ID", "RAILWAY_ENVIRONMENT_ID", "RAILWAY_SERVICE_ID"],
|
|
9
|
+
docs: "https://docs.railway.com/guides/public-api",
|
|
10
|
+
},
|
|
11
|
+
vercel: {
|
|
12
|
+
required: ["VERCEL_TOKEN"],
|
|
13
|
+
optional: ["VERCEL_PROJECT_ID"],
|
|
14
|
+
docs: "https://vercel.com/account/tokens",
|
|
15
|
+
},
|
|
16
|
+
fly: {
|
|
17
|
+
required: ["FLY_API_TOKEN"],
|
|
18
|
+
optional: ["FLY_APP_NAME"],
|
|
19
|
+
docs: "https://fly.io/docs/flyctl/tokens/",
|
|
20
|
+
},
|
|
21
|
+
render: {
|
|
22
|
+
required: ["RENDER_API_KEY"],
|
|
23
|
+
optional: [],
|
|
24
|
+
docs: "https://render.com/docs/api",
|
|
25
|
+
},
|
|
26
|
+
aws: {
|
|
27
|
+
required: ["AWS_ACCESS_KEY_ID", "AWS_SECRET_ACCESS_KEY"],
|
|
28
|
+
optional: ["AWS_REGION"],
|
|
29
|
+
},
|
|
30
|
+
database: {
|
|
31
|
+
required: ["DATABASE_URL"],
|
|
32
|
+
optional: [],
|
|
33
|
+
},
|
|
34
|
+
mongodb: {
|
|
35
|
+
required: ["MONGODB_URL"],
|
|
36
|
+
optional: [],
|
|
37
|
+
},
|
|
38
|
+
redis: {
|
|
39
|
+
required: ["REDIS_URL"],
|
|
40
|
+
optional: [],
|
|
41
|
+
},
|
|
42
|
+
supabase: {
|
|
43
|
+
required: ["SUPABASE_URL"],
|
|
44
|
+
optional: ["SUPABASE_SERVICE_KEY", "SUPABASE_ANON_KEY"],
|
|
45
|
+
docs: "https://supabase.com/docs/guides/api",
|
|
46
|
+
},
|
|
47
|
+
r2: {
|
|
48
|
+
required: ["CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_TOKEN"],
|
|
49
|
+
optional: [],
|
|
50
|
+
docs: "https://developers.cloudflare.com/r2/api/",
|
|
51
|
+
},
|
|
52
|
+
stripe: {
|
|
53
|
+
required: ["STRIPE_SECRET_KEY"],
|
|
54
|
+
optional: [],
|
|
55
|
+
docs: "https://dashboard.stripe.com/apikeys",
|
|
56
|
+
},
|
|
57
|
+
};
|
|
58
|
+
export const doctorCommand = new Command("doctor")
|
|
59
|
+
.description("Diagnose configuration issues and suggest fixes")
|
|
60
|
+
.action(async () => {
|
|
61
|
+
const issues = [];
|
|
62
|
+
// 1. Check config file
|
|
63
|
+
const hasConfig = existsSync(CONFIG_FILE);
|
|
64
|
+
if (!hasConfig) {
|
|
65
|
+
issues.push({
|
|
66
|
+
level: "warn",
|
|
67
|
+
service: "config",
|
|
68
|
+
message: `No ${CONFIG_FILE} found — using auto-detection`,
|
|
69
|
+
fix: `Run ${chalk.white("stk init")} to create one`,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const config = loadConfig();
|
|
73
|
+
const enabled = enabledServices(config);
|
|
74
|
+
// 2. Check each enabled service for env vars
|
|
75
|
+
for (const svc of enabled) {
|
|
76
|
+
const reqs = ENV_REQUIREMENTS[svc];
|
|
77
|
+
if (!reqs)
|
|
78
|
+
continue;
|
|
79
|
+
const missingRequired = reqs.required.filter((v) => !process.env[v]);
|
|
80
|
+
const missingOptional = reqs.optional.filter((v) => !process.env[v]);
|
|
81
|
+
if (missingRequired.length > 0) {
|
|
82
|
+
issues.push({
|
|
83
|
+
level: "error",
|
|
84
|
+
service: svc,
|
|
85
|
+
message: `Missing required: ${missingRequired.join(", ")}`,
|
|
86
|
+
fix: reqs.docs ? `See ${reqs.docs}` : `Set ${missingRequired[0]} in your .env`,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
if (missingOptional.length > 0) {
|
|
90
|
+
issues.push({
|
|
91
|
+
level: "warn",
|
|
92
|
+
service: svc,
|
|
93
|
+
message: `Missing optional: ${missingOptional.join(", ")}`,
|
|
94
|
+
fix: `Some features (logs, env sync, deploy) need these for full functionality`,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
if (missingRequired.length === 0) {
|
|
98
|
+
issues.push({
|
|
99
|
+
level: "info",
|
|
100
|
+
service: svc,
|
|
101
|
+
message: "Configured correctly",
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
// 3. Check for partial config (env vars set but service not enabled)
|
|
106
|
+
for (const [svc, reqs] of Object.entries(ENV_REQUIREMENTS)) {
|
|
107
|
+
if (enabled.includes(svc))
|
|
108
|
+
continue;
|
|
109
|
+
const hasAny = reqs.required.some((v) => process.env[v]);
|
|
110
|
+
if (hasAny) {
|
|
111
|
+
issues.push({
|
|
112
|
+
level: "warn",
|
|
113
|
+
service: svc,
|
|
114
|
+
message: `Env vars found but service not enabled in config`,
|
|
115
|
+
fix: `Add "${svc}": true to services in ${CONFIG_FILE}`,
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
// 4. Check for common misconfigurations
|
|
120
|
+
if (process.env.DATABASE_URL) {
|
|
121
|
+
try {
|
|
122
|
+
new URL(process.env.DATABASE_URL);
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
issues.push({
|
|
126
|
+
level: "error",
|
|
127
|
+
service: "database",
|
|
128
|
+
message: "DATABASE_URL is not a valid URL",
|
|
129
|
+
fix: "Format: postgresql://user:pass@host:5432/dbname",
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
if (process.env.REDIS_URL) {
|
|
134
|
+
try {
|
|
135
|
+
new URL(process.env.REDIS_URL);
|
|
136
|
+
}
|
|
137
|
+
catch {
|
|
138
|
+
issues.push({
|
|
139
|
+
level: "error",
|
|
140
|
+
service: "redis",
|
|
141
|
+
message: "REDIS_URL is not a valid URL",
|
|
142
|
+
fix: "Format: redis://default:pass@host:6379",
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
if (process.env.STRIPE_SECRET_KEY && !process.env.STRIPE_SECRET_KEY.startsWith("sk_")) {
|
|
147
|
+
issues.push({
|
|
148
|
+
level: "error",
|
|
149
|
+
service: "stripe",
|
|
150
|
+
message: "STRIPE_SECRET_KEY should start with sk_test_ or sk_live_",
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
// 5. Check deploy config
|
|
154
|
+
if (hasConfig && config.deploy?.providers) {
|
|
155
|
+
for (const p of config.deploy.providers) {
|
|
156
|
+
if (!enabled.includes(p)) {
|
|
157
|
+
issues.push({
|
|
158
|
+
level: "error",
|
|
159
|
+
service: "deploy",
|
|
160
|
+
message: `Deploy provider "${p}" is not enabled in services`,
|
|
161
|
+
fix: `Add "${p}": true to services, or remove from deploy.providers`,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
// 6. Check GitHub config for todo
|
|
167
|
+
if (!process.env.GITHUB_TOKEN && !config.github?.repo) {
|
|
168
|
+
issues.push({
|
|
169
|
+
level: "info",
|
|
170
|
+
service: "github",
|
|
171
|
+
message: "GITHUB_TOKEN not set — stk todo will have limited access",
|
|
172
|
+
fix: "Set GITHUB_TOKEN for creating/closing issues",
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
// Print results
|
|
176
|
+
const ICONS = {
|
|
177
|
+
error: chalk.red("✗"),
|
|
178
|
+
warn: chalk.yellow("!"),
|
|
179
|
+
info: chalk.green("✓"),
|
|
180
|
+
};
|
|
181
|
+
const COLORS = {
|
|
182
|
+
error: chalk.red,
|
|
183
|
+
warn: chalk.yellow,
|
|
184
|
+
info: chalk.green,
|
|
185
|
+
};
|
|
186
|
+
console.log();
|
|
187
|
+
console.log(chalk.bold(` ${config.name} — Doctor`));
|
|
188
|
+
console.log(chalk.dim(" ─────────────────────────────────────────"));
|
|
189
|
+
const grouped = new Map();
|
|
190
|
+
for (const issue of issues) {
|
|
191
|
+
const list = grouped.get(issue.service) ?? [];
|
|
192
|
+
list.push(issue);
|
|
193
|
+
grouped.set(issue.service, list);
|
|
194
|
+
}
|
|
195
|
+
for (const [service, serviceIssues] of grouped) {
|
|
196
|
+
for (const issue of serviceIssues) {
|
|
197
|
+
const icon = ICONS[issue.level];
|
|
198
|
+
const svc = COLORS[issue.level](service.padEnd(12));
|
|
199
|
+
console.log(` ${icon} ${svc} ${issue.message}`);
|
|
200
|
+
if (issue.fix) {
|
|
201
|
+
console.log(` ${" ".repeat(12)} ${chalk.dim(issue.fix)}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
const errors = issues.filter((i) => i.level === "error");
|
|
206
|
+
const warns = issues.filter((i) => i.level === "warn");
|
|
207
|
+
const ok = issues.filter((i) => i.level === "info");
|
|
208
|
+
console.log();
|
|
209
|
+
if (errors.length > 0) {
|
|
210
|
+
console.log(chalk.red(` ${errors.length} error${errors.length > 1 ? "s" : ""} found`));
|
|
211
|
+
process.exitCode = 1;
|
|
212
|
+
}
|
|
213
|
+
else if (warns.length > 0) {
|
|
214
|
+
console.log(chalk.yellow(` ${warns.length} warning${warns.length > 1 ? "s" : ""}, ${ok.length} ok`));
|
|
215
|
+
}
|
|
216
|
+
else {
|
|
217
|
+
console.log(chalk.green(` All ${ok.length} checks passed`));
|
|
218
|
+
}
|
|
219
|
+
console.log();
|
|
220
|
+
});
|
package/dist/commands/health.js
CHANGED
|
@@ -2,7 +2,8 @@ import { Command } from "commander";
|
|
|
2
2
|
import chalk from "chalk";
|
|
3
3
|
import ora from "ora";
|
|
4
4
|
import { loadConfig, enabledServices } from "../lib/config.js";
|
|
5
|
-
import { getChecker, allCheckerNames } from "../services/registry.js";
|
|
5
|
+
import { getChecker, allCheckerNames, loadPluginCheckers } from "../services/registry.js";
|
|
6
|
+
import { jsonOutput } from "../lib/output.js";
|
|
6
7
|
const STATUS_ICON = {
|
|
7
8
|
healthy: chalk.green("✓"),
|
|
8
9
|
degraded: chalk.yellow("~"),
|
|
@@ -19,20 +20,25 @@ export const healthCommand = new Command("health")
|
|
|
19
20
|
.description("Check the health of all connected services")
|
|
20
21
|
.option("-v, --verbose", "Show latency and extra detail")
|
|
21
22
|
.option("-a, --all", "Check all known services, not just configured ones")
|
|
23
|
+
.option("-j, --json", "Output as JSON")
|
|
22
24
|
.action(async (opts) => {
|
|
23
25
|
const config = loadConfig();
|
|
24
|
-
|
|
26
|
+
await loadPluginCheckers();
|
|
25
27
|
const serviceList = opts.all
|
|
26
28
|
? allCheckerNames()
|
|
27
29
|
: enabledServices(config);
|
|
28
30
|
if (serviceList.length === 0) {
|
|
29
|
-
|
|
31
|
+
if (opts.json) {
|
|
32
|
+
jsonOutput({ services: [], summary: "no services configured" });
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
30
35
|
console.log();
|
|
31
36
|
console.log(chalk.yellow(" No services configured."));
|
|
32
37
|
console.log(chalk.dim(` Run ${chalk.white("stk init")} to set up your project, or ${chalk.white("stk health --all")} to check everything.`));
|
|
33
38
|
console.log();
|
|
34
39
|
return;
|
|
35
40
|
}
|
|
41
|
+
const spinner = opts.json ? null : ora("Checking services...").start();
|
|
36
42
|
const checks = serviceList.map((name) => {
|
|
37
43
|
const checker = getChecker(name);
|
|
38
44
|
if (!checker) {
|
|
@@ -45,7 +51,26 @@ export const healthCommand = new Command("health")
|
|
|
45
51
|
return checker();
|
|
46
52
|
});
|
|
47
53
|
const results = await Promise.all(checks);
|
|
48
|
-
spinner
|
|
54
|
+
spinner?.stop();
|
|
55
|
+
// JSON output
|
|
56
|
+
if (opts.json) {
|
|
57
|
+
const down = results.filter((r) => r.status === "down");
|
|
58
|
+
jsonOutput({
|
|
59
|
+
project: config.name,
|
|
60
|
+
services: results,
|
|
61
|
+
summary: {
|
|
62
|
+
healthy: results.filter((r) => r.status === "healthy").length,
|
|
63
|
+
down: down.length,
|
|
64
|
+
skipped: results.filter((r) => r.status === "skipped").length,
|
|
65
|
+
total: results.length,
|
|
66
|
+
},
|
|
67
|
+
ok: down.length === 0,
|
|
68
|
+
});
|
|
69
|
+
if (down.length > 0)
|
|
70
|
+
process.exitCode = 1;
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
// Human output
|
|
49
74
|
console.log();
|
|
50
75
|
console.log(chalk.bold(` ${config.name} — Service Health`));
|
|
51
76
|
console.log(chalk.dim(" ─────────────────────────────────────────"));
|