@trading-boy/cli 1.11.0 → 2.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/README.md +50 -22
- package/dist/api-client.d.ts +4 -7
- package/dist/api-client.js +8 -13
- package/dist/cli.bundle.js +1977 -33976
- package/dist/credentials.js +1 -1
- package/dist/index.d.ts +0 -28
- package/dist/index.js +0 -24
- package/dist/logger.d.ts +8 -0
- package/dist/logger.js +12 -0
- package/dist/utils.js +3 -3
- package/package.json +20 -5
- package/dist/cli.d.ts +0 -5
- package/dist/cli.js +0 -157
- package/dist/commands/agent-cmd.d.ts +0 -9
- package/dist/commands/agent-cmd.js +0 -572
- package/dist/commands/audit.d.ts +0 -18
- package/dist/commands/audit.js +0 -73
- package/dist/commands/behavioral.d.ts +0 -73
- package/dist/commands/behavioral.js +0 -349
- package/dist/commands/benchmark-cmd.d.ts +0 -3
- package/dist/commands/benchmark-cmd.js +0 -191
- package/dist/commands/billing.d.ts +0 -12
- package/dist/commands/billing.js +0 -142
- package/dist/commands/catalysts.d.ts +0 -17
- package/dist/commands/catalysts.js +0 -151
- package/dist/commands/coaching-cmd.d.ts +0 -16
- package/dist/commands/coaching-cmd.js +0 -222
- package/dist/commands/config-cmd.d.ts +0 -30
- package/dist/commands/config-cmd.js +0 -515
- package/dist/commands/connect-chatgpt.d.ts +0 -5
- package/dist/commands/connect-chatgpt.js +0 -293
- package/dist/commands/connect-claude.d.ts +0 -5
- package/dist/commands/connect-claude.js +0 -280
- package/dist/commands/context.d.ts +0 -41
- package/dist/commands/context.js +0 -405
- package/dist/commands/cron-cmd.d.ts +0 -3
- package/dist/commands/cron-cmd.js +0 -305
- package/dist/commands/decisions.d.ts +0 -57
- package/dist/commands/decisions.js +0 -364
- package/dist/commands/edge-cmd.d.ts +0 -78
- package/dist/commands/edge-cmd.js +0 -183
- package/dist/commands/edge-guard-cmd.d.ts +0 -36
- package/dist/commands/edge-guard-cmd.js +0 -169
- package/dist/commands/events.d.ts +0 -3
- package/dist/commands/events.js +0 -117
- package/dist/commands/infra.d.ts +0 -24
- package/dist/commands/infra.js +0 -137
- package/dist/commands/journal.d.ts +0 -3
- package/dist/commands/journal.js +0 -302
- package/dist/commands/login.d.ts +0 -18
- package/dist/commands/login.js +0 -127
- package/dist/commands/logout.d.ts +0 -8
- package/dist/commands/logout.js +0 -108
- package/dist/commands/narratives.d.ts +0 -3
- package/dist/commands/narratives.js +0 -259
- package/dist/commands/onboarding.d.ts +0 -7
- package/dist/commands/onboarding.js +0 -298
- package/dist/commands/query.d.ts +0 -32
- package/dist/commands/query.js +0 -135
- package/dist/commands/replay-cmd.d.ts +0 -43
- package/dist/commands/replay-cmd.js +0 -184
- package/dist/commands/review.d.ts +0 -3
- package/dist/commands/review.js +0 -443
- package/dist/commands/risk.d.ts +0 -47
- package/dist/commands/risk.js +0 -158
- package/dist/commands/social.d.ts +0 -43
- package/dist/commands/social.js +0 -318
- package/dist/commands/soul-wizard.d.ts +0 -29
- package/dist/commands/soul-wizard.js +0 -155
- package/dist/commands/strategy-cmd.d.ts +0 -44
- package/dist/commands/strategy-cmd.js +0 -340
- package/dist/commands/subscribe.d.ts +0 -78
- package/dist/commands/subscribe.js +0 -552
- package/dist/commands/suggestions-cmd.d.ts +0 -24
- package/dist/commands/suggestions-cmd.js +0 -148
- package/dist/commands/thesis-cmd.d.ts +0 -3
- package/dist/commands/thesis-cmd.js +0 -129
- package/dist/commands/trader.d.ts +0 -30
- package/dist/commands/trader.js +0 -971
- package/dist/commands/watch.d.ts +0 -16
- package/dist/commands/watch.js +0 -104
- package/dist/commands/whoami.d.ts +0 -14
- package/dist/commands/whoami.js +0 -105
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
import { Option } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { apiRequest, ApiError } from '../api-client.js';
|
|
4
|
-
import { ensureRemote } from '../utils.js';
|
|
5
|
-
// ─── Formatters ───
|
|
6
|
-
export function formatAssessmentOutput(data) {
|
|
7
|
-
const lines = [];
|
|
8
|
-
const a = data.assessment;
|
|
9
|
-
lines.push('');
|
|
10
|
-
lines.push(chalk.bold.cyan(` Edge Guard — ${data.traderId}`));
|
|
11
|
-
lines.push(chalk.gray(' ' + '─'.repeat(50)));
|
|
12
|
-
lines.push(` ${chalk.gray('Status:')} ${colorStatus(a.status)}`);
|
|
13
|
-
lines.push(` ${chalk.gray('Escalation:')} ${colorEscalation(a.escalation)}`);
|
|
14
|
-
lines.push(` ${chalk.gray('Friction Active:')} ${a.frictionActive ? chalk.red('YES') : chalk.green('NO')}`);
|
|
15
|
-
lines.push('');
|
|
16
|
-
lines.push(` ${chalk.gray('Portfolio EV:')} ${colorPnl(a.portfolioEV)}`);
|
|
17
|
-
lines.push(` ${chalk.gray('90% CI:')} [${formatUsd(a.portfolioEVLower)}, ${formatUsd(a.portfolioEVUpper)}]`);
|
|
18
|
-
lines.push(` ${chalk.gray('Negative Cells:')} ${a.negativeCells}/${a.totalCells}`);
|
|
19
|
-
if (a.message) {
|
|
20
|
-
lines.push('');
|
|
21
|
-
lines.push(chalk.bold(' Message'));
|
|
22
|
-
// Wrap message at ~70 chars, indented
|
|
23
|
-
const words = a.message.split(' ');
|
|
24
|
-
let line = ' ';
|
|
25
|
-
for (const word of words) {
|
|
26
|
-
if (line.length + word.length > 72) {
|
|
27
|
-
lines.push(line);
|
|
28
|
-
line = ' ';
|
|
29
|
-
}
|
|
30
|
-
line += word + ' ';
|
|
31
|
-
}
|
|
32
|
-
if (line.trim())
|
|
33
|
-
lines.push(line);
|
|
34
|
-
}
|
|
35
|
-
if (a.cellEstimates.length > 0) {
|
|
36
|
-
lines.push('');
|
|
37
|
-
lines.push(chalk.bold(' Cell Estimates'));
|
|
38
|
-
lines.push(` ${chalk.gray('Setup'.padEnd(18))}${'Regime'.padEnd(16)}${'EV'.padStart(10)}${'Win%'.padStart(8)}`);
|
|
39
|
-
lines.push(chalk.gray(' ' + '─'.repeat(52)));
|
|
40
|
-
// Show top negative cells first
|
|
41
|
-
const sorted = [...a.cellEstimates].sort((x, y) => x.evPoint - y.evPoint);
|
|
42
|
-
const shown = sorted.slice(0, 15);
|
|
43
|
-
for (const c of shown) {
|
|
44
|
-
const evStr = formatUsd(c.evPoint);
|
|
45
|
-
const wrStr = (c.winRate * 100).toFixed(1) + '%';
|
|
46
|
-
lines.push(` ${c.setupType.padEnd(18)}${c.regime.padEnd(16)}${(c.evPoint >= 0 ? chalk.green(evStr) : chalk.red(evStr)).padStart(10)}${wrStr.padStart(8)}`);
|
|
47
|
-
}
|
|
48
|
-
if (a.cellEstimates.length > 15) {
|
|
49
|
-
lines.push(chalk.dim(` ... and ${a.cellEstimates.length - 15} more cells`));
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
lines.push('');
|
|
53
|
-
return lines.join('\n');
|
|
54
|
-
}
|
|
55
|
-
export function formatFrictionOutput(data) {
|
|
56
|
-
const lines = [];
|
|
57
|
-
lines.push('');
|
|
58
|
-
lines.push(chalk.bold.cyan(` Entry Friction — ${data.traderId}`));
|
|
59
|
-
lines.push(chalk.gray(' ' + '─'.repeat(50)));
|
|
60
|
-
lines.push(` ${chalk.gray('Escalation:')} ${colorEscalation(data.escalation)}`);
|
|
61
|
-
lines.push(` ${chalk.gray('Requires Ack:')} ${data.requiresAcknowledgement ? chalk.red('YES') : chalk.green('NO')}`);
|
|
62
|
-
lines.push(` ${chalk.gray('Acknowledged:')} ${data.acknowledged ? chalk.green('YES') : chalk.dim('NO')}`);
|
|
63
|
-
if (data.message) {
|
|
64
|
-
lines.push('');
|
|
65
|
-
lines.push(` ${data.message}`);
|
|
66
|
-
}
|
|
67
|
-
if (data.requiresAcknowledgement && !data.acknowledged) {
|
|
68
|
-
lines.push('');
|
|
69
|
-
lines.push(chalk.yellow(` Run: trading-boy edge-guard acknowledge ${data.traderId}`));
|
|
70
|
-
}
|
|
71
|
-
lines.push('');
|
|
72
|
-
return lines.join('\n');
|
|
73
|
-
}
|
|
74
|
-
function colorStatus(status) {
|
|
75
|
-
switch (status) {
|
|
76
|
-
case 'POSITIVE_EDGE': return chalk.green(status);
|
|
77
|
-
case 'UNCERTAIN': return chalk.yellow(status);
|
|
78
|
-
case 'NEGATIVE_EDGE_WARNING': return chalk.red(status);
|
|
79
|
-
case 'NEGATIVE_EDGE_CONFIRMED': return chalk.bgRed.white(` ${status} `);
|
|
80
|
-
default: return status;
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
function colorEscalation(level) {
|
|
84
|
-
switch (level) {
|
|
85
|
-
case 'NORMAL': return chalk.green(level);
|
|
86
|
-
case 'INCREASED_COACHING': return chalk.yellow(level);
|
|
87
|
-
case 'EXPLICIT_WARNING': return chalk.red(level);
|
|
88
|
-
case 'ENTRY_FRICTION': return chalk.bgRed.white(` ${level} `);
|
|
89
|
-
default: return level;
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
function colorPnl(usd) {
|
|
93
|
-
const str = formatUsd(usd);
|
|
94
|
-
return usd >= 0 ? chalk.green(str) : chalk.red(str);
|
|
95
|
-
}
|
|
96
|
-
function formatUsd(val) {
|
|
97
|
-
const sign = val >= 0 ? '+' : '';
|
|
98
|
-
return `${sign}$${val.toFixed(2)}`;
|
|
99
|
-
}
|
|
100
|
-
// ─── Command Registration ───
|
|
101
|
-
export function registerEdgeGuardCommand(program) {
|
|
102
|
-
// Parent command group (no positional arg — subcommands handle their own traderId)
|
|
103
|
-
const cmd = program
|
|
104
|
-
.command('edge-guard')
|
|
105
|
-
.description('Check negative edge status and entry friction')
|
|
106
|
-
.action(() => {
|
|
107
|
-
// Show help when called without a subcommand
|
|
108
|
-
cmd.help();
|
|
109
|
-
});
|
|
110
|
-
// Subcommand: check <traderId> (default assessment)
|
|
111
|
-
cmd
|
|
112
|
-
.command('check <traderId>')
|
|
113
|
-
.description('Assess negative edge status for a trader')
|
|
114
|
-
.option('--friction', 'Check entry friction level only')
|
|
115
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
116
|
-
.action(async (traderId, options) => {
|
|
117
|
-
try {
|
|
118
|
-
if (!(await ensureRemote()))
|
|
119
|
-
return;
|
|
120
|
-
if (options.friction) {
|
|
121
|
-
const data = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderId)}/edge-guard/friction`);
|
|
122
|
-
if (options.format === 'json') {
|
|
123
|
-
console.log(JSON.stringify(data, null, 2));
|
|
124
|
-
}
|
|
125
|
-
else {
|
|
126
|
-
console.log(formatFrictionOutput(data));
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
const data = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderId)}/edge-guard`);
|
|
131
|
-
if (options.format === 'json') {
|
|
132
|
-
console.log(JSON.stringify(data, null, 2));
|
|
133
|
-
}
|
|
134
|
-
else {
|
|
135
|
-
console.log(formatAssessmentOutput(data));
|
|
136
|
-
}
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
catch (error) {
|
|
140
|
-
const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
|
|
141
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
142
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
// Subcommand: acknowledge <traderId>
|
|
146
|
-
cmd
|
|
147
|
-
.command('acknowledge <traderId>')
|
|
148
|
-
.description('Acknowledge negative edge warning to proceed with trade')
|
|
149
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
150
|
-
.action(async (traderId, ackOptions) => {
|
|
151
|
-
try {
|
|
152
|
-
if (!(await ensureRemote()))
|
|
153
|
-
return;
|
|
154
|
-
const data = await apiRequest(`/api/v1/traders/${encodeURIComponent(traderId)}/edge-guard/acknowledge`, { method: 'POST' });
|
|
155
|
-
if (ackOptions.format === 'json') {
|
|
156
|
-
console.log(JSON.stringify(data, null, 2));
|
|
157
|
-
}
|
|
158
|
-
else {
|
|
159
|
-
console.log(chalk.green(`\n ✓ Edge warning acknowledged for ${data.traderId}\n`));
|
|
160
|
-
}
|
|
161
|
-
}
|
|
162
|
-
catch (error) {
|
|
163
|
-
const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
|
|
164
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
165
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
166
|
-
}
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
//# sourceMappingURL=edge-guard-cmd.js.map
|
package/dist/commands/events.js
DELETED
|
@@ -1,117 +0,0 @@
|
|
|
1
|
-
import { Option } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { createLogger, EventType, Severity } from '@trading-boy/core';
|
|
4
|
-
import { apiRequest, ApiError } from '../api-client.js';
|
|
5
|
-
import { padRight, truncate, formatDate } from '../utils.js';
|
|
6
|
-
const logger = createLogger('cli-events');
|
|
7
|
-
// ─── Formatters ───
|
|
8
|
-
function colorSeverity(severity) {
|
|
9
|
-
switch (severity) {
|
|
10
|
-
case 'CRITICAL': return chalk.bgRed.white.bold(` ${severity} `);
|
|
11
|
-
case 'HIGH': return chalk.red(severity);
|
|
12
|
-
case 'MEDIUM': return chalk.yellow(severity);
|
|
13
|
-
case 'LOW': return chalk.green(severity);
|
|
14
|
-
default: return chalk.dim(severity);
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
function formatEventsList(events) {
|
|
18
|
-
const lines = [];
|
|
19
|
-
lines.push('');
|
|
20
|
-
lines.push(chalk.bold.cyan(' Events'));
|
|
21
|
-
lines.push(chalk.gray(' ' + '─'.repeat(80)));
|
|
22
|
-
if (events.length === 0) {
|
|
23
|
-
lines.push(` ${chalk.dim('No events found.')}`);
|
|
24
|
-
lines.push('');
|
|
25
|
-
return lines.join('\n');
|
|
26
|
-
}
|
|
27
|
-
lines.push(' ' + padRight('Date', 14) + padRight('Type', 22) + padRight('Severity', 12) + 'Title');
|
|
28
|
-
lines.push(' ' + '─'.repeat(80));
|
|
29
|
-
for (const e of events) {
|
|
30
|
-
lines.push(' ' +
|
|
31
|
-
padRight(formatDate(e.scheduledDate), 14) +
|
|
32
|
-
padRight(e.type, 22) +
|
|
33
|
-
padRight(colorSeverity(e.severity), 12) +
|
|
34
|
-
truncate(e.title, 30));
|
|
35
|
-
}
|
|
36
|
-
lines.push('');
|
|
37
|
-
lines.push(` ${chalk.gray(`${events.length} event(s)`)}`);
|
|
38
|
-
lines.push('');
|
|
39
|
-
return lines.join('\n');
|
|
40
|
-
}
|
|
41
|
-
// ─── Command Registration ───
|
|
42
|
-
export function registerEventsCommand(program) {
|
|
43
|
-
const events = program
|
|
44
|
-
.command('events')
|
|
45
|
-
.description('Event management commands')
|
|
46
|
-
.action(() => { events.help(); });
|
|
47
|
-
// events add
|
|
48
|
-
events
|
|
49
|
-
.command('add')
|
|
50
|
-
.description('Add a new event')
|
|
51
|
-
.requiredOption('--type <type>', `Event type (${Object.values(EventType).join(', ')})`)
|
|
52
|
-
.requiredOption('--title <title>', 'Event title')
|
|
53
|
-
.option('--token <symbols...>', 'Token symbol(s) affected')
|
|
54
|
-
.option('--date <date>', 'Scheduled date (ISO 8601)')
|
|
55
|
-
.option('--description <desc>', 'Event description')
|
|
56
|
-
.option('--severity <severity>', `Severity (${Object.values(Severity).join(', ')})`, 'MEDIUM')
|
|
57
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
58
|
-
.action(async (options) => {
|
|
59
|
-
try {
|
|
60
|
-
const body = {
|
|
61
|
-
type: options.type,
|
|
62
|
-
title: options.title,
|
|
63
|
-
severity: options.severity,
|
|
64
|
-
};
|
|
65
|
-
if (options.token)
|
|
66
|
-
body.tokenSymbols = options.token;
|
|
67
|
-
if (options.date)
|
|
68
|
-
body.scheduledDate = options.date;
|
|
69
|
-
if (options.description)
|
|
70
|
-
body.description = options.description;
|
|
71
|
-
const result = await apiRequest('/api/v1/events', { method: 'POST', body });
|
|
72
|
-
if (options.format === 'json') {
|
|
73
|
-
console.log(JSON.stringify(result, null, 2));
|
|
74
|
-
}
|
|
75
|
-
else {
|
|
76
|
-
console.log(chalk.green(`\n ✓ Event created: ${result.title} (${result.id})\n`));
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
catch (error) {
|
|
80
|
-
const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
|
|
81
|
-
logger.error({ error: message }, 'Failed to create event');
|
|
82
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
83
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
84
|
-
}
|
|
85
|
-
});
|
|
86
|
-
// events list
|
|
87
|
-
events
|
|
88
|
-
.command('list')
|
|
89
|
-
.description('List events')
|
|
90
|
-
.option('--type <type>', 'Filter by event type')
|
|
91
|
-
.option('--days <n>', 'Days to look ahead', parseInt, 30)
|
|
92
|
-
.option('--limit <n>', 'Maximum results', parseInt, 50)
|
|
93
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
94
|
-
.action(async (options) => {
|
|
95
|
-
try {
|
|
96
|
-
const params = new URLSearchParams();
|
|
97
|
-
params.set('days', String(options.days));
|
|
98
|
-
if (options.type)
|
|
99
|
-
params.set('type', options.type);
|
|
100
|
-
params.set('limit', String(options.limit));
|
|
101
|
-
const data = await apiRequest(`/api/v1/events?${params.toString()}`);
|
|
102
|
-
if (options.format === 'json') {
|
|
103
|
-
console.log(JSON.stringify(data, null, 2));
|
|
104
|
-
}
|
|
105
|
-
else {
|
|
106
|
-
console.log(formatEventsList(data.events));
|
|
107
|
-
}
|
|
108
|
-
}
|
|
109
|
-
catch (error) {
|
|
110
|
-
const message = error instanceof ApiError ? error.message : (error instanceof Error ? error.message : String(error));
|
|
111
|
-
logger.error({ error: message }, 'Failed to list events');
|
|
112
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
113
|
-
process.exitCode = error instanceof ApiError ? 2 : 1;
|
|
114
|
-
}
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
//# sourceMappingURL=events.js.map
|
package/dist/commands/infra.d.ts
DELETED
|
@@ -1,24 +0,0 @@
|
|
|
1
|
-
import { Command } from 'commander';
|
|
2
|
-
interface ServiceHealthResult {
|
|
3
|
-
name: string;
|
|
4
|
-
healthy: boolean;
|
|
5
|
-
message: string;
|
|
6
|
-
details?: string;
|
|
7
|
-
}
|
|
8
|
-
export interface InfraStatusResult {
|
|
9
|
-
services: ServiceHealthResult[];
|
|
10
|
-
overallHealthy: boolean;
|
|
11
|
-
}
|
|
12
|
-
/**
|
|
13
|
-
* Check infrastructure health via the API /health endpoint.
|
|
14
|
-
* Falls back to a basic connectivity check if the API is unreachable.
|
|
15
|
-
*/
|
|
16
|
-
export declare function checkInfraStatus(options?: {
|
|
17
|
-
remote?: boolean;
|
|
18
|
-
}): Promise<InfraStatusResult>;
|
|
19
|
-
export declare function formatInfraStatus(result: InfraStatusResult, options?: {
|
|
20
|
-
remote?: boolean;
|
|
21
|
-
}): string;
|
|
22
|
-
export declare function registerInfraCommand(program: Command): void;
|
|
23
|
-
export {};
|
|
24
|
-
//# sourceMappingURL=infra.d.ts.map
|
package/dist/commands/infra.js
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
import { Option } from 'commander';
|
|
2
|
-
import chalk from 'chalk';
|
|
3
|
-
import { createLogger } from '@trading-boy/core';
|
|
4
|
-
import { padRight } from '../utils.js';
|
|
5
|
-
import { getApiBase, isRemoteMode } from '../api-client.js';
|
|
6
|
-
// ─── Logger ───
|
|
7
|
-
const logger = createLogger('cli-infra');
|
|
8
|
-
// ─── Health Check Logic ───
|
|
9
|
-
/**
|
|
10
|
-
* Check infrastructure health via the API /health endpoint.
|
|
11
|
-
* Falls back to a basic connectivity check if the API is unreachable.
|
|
12
|
-
*/
|
|
13
|
-
export async function checkInfraStatus(options) {
|
|
14
|
-
const apiBase = getApiBase();
|
|
15
|
-
try {
|
|
16
|
-
// Call the API health endpoint directly (no auth needed)
|
|
17
|
-
const response = await fetch(`${apiBase}/health`);
|
|
18
|
-
const data = (await response.json());
|
|
19
|
-
const services = [
|
|
20
|
-
{
|
|
21
|
-
name: 'API Server',
|
|
22
|
-
healthy: response.ok,
|
|
23
|
-
message: response.ok ? 'Connected' : `Status: ${response.status}`,
|
|
24
|
-
details: apiBase,
|
|
25
|
-
},
|
|
26
|
-
];
|
|
27
|
-
// In remote mode, skip individual DB statuses — they are managed infrastructure
|
|
28
|
-
if (!options?.remote && data.services) {
|
|
29
|
-
if (data.services.neo4j) {
|
|
30
|
-
services.push({
|
|
31
|
-
name: 'Neo4j',
|
|
32
|
-
healthy: data.services.neo4j.status === 'ok',
|
|
33
|
-
message: data.services.neo4j.status === 'ok' ? 'Connected' : (data.services.neo4j.error ?? 'Unhealthy'),
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
if (data.services.timescale) {
|
|
37
|
-
services.push({
|
|
38
|
-
name: 'TimescaleDB',
|
|
39
|
-
healthy: data.services.timescale.status === 'ok',
|
|
40
|
-
message: data.services.timescale.status === 'ok' ? 'Connected' : (data.services.timescale.error ?? 'Unhealthy'),
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
if (data.services.redis) {
|
|
44
|
-
services.push({
|
|
45
|
-
name: 'Redis',
|
|
46
|
-
healthy: data.services.redis.status === 'ok',
|
|
47
|
-
message: data.services.redis.status === 'ok' ? 'Connected' : (data.services.redis.error ?? 'Unhealthy'),
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
// In remote mode, use API response status to determine health
|
|
52
|
-
// (services array only contains API Server, but backend may be degraded)
|
|
53
|
-
const overallHealthy = options?.remote
|
|
54
|
-
? response.ok && data.status === 'ok'
|
|
55
|
-
: services.every((s) => s.healthy);
|
|
56
|
-
return { services, overallHealthy };
|
|
57
|
-
}
|
|
58
|
-
catch (error) {
|
|
59
|
-
// API is unreachable
|
|
60
|
-
return {
|
|
61
|
-
services: [
|
|
62
|
-
{
|
|
63
|
-
name: 'API Server',
|
|
64
|
-
healthy: false,
|
|
65
|
-
message: error instanceof Error ? error.message : String(error),
|
|
66
|
-
details: apiBase,
|
|
67
|
-
},
|
|
68
|
-
],
|
|
69
|
-
overallHealthy: false,
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
// ─── Formatter ───
|
|
74
|
-
function formatStatusIndicator(healthy) {
|
|
75
|
-
return healthy ? chalk.green('\u2713 UP') : chalk.red('\u2717 DOWN');
|
|
76
|
-
}
|
|
77
|
-
export function formatInfraStatus(result, options) {
|
|
78
|
-
const lines = [];
|
|
79
|
-
lines.push('');
|
|
80
|
-
lines.push(chalk.bold.cyan(' Infrastructure Status'));
|
|
81
|
-
lines.push(chalk.gray(' ' + '\u2500'.repeat(60)));
|
|
82
|
-
lines.push('');
|
|
83
|
-
for (const service of result.services) {
|
|
84
|
-
const status = formatStatusIndicator(service.healthy);
|
|
85
|
-
lines.push(` ${status} ${chalk.bold(padRight(service.name, 16))} ${chalk.gray(service.message)}`);
|
|
86
|
-
if (service.details) {
|
|
87
|
-
lines.push(` ${chalk.dim(service.details)}`);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
lines.push('');
|
|
91
|
-
if (options?.remote) {
|
|
92
|
-
lines.push(chalk.dim(' Backend services are managed infrastructure. Contact support if issues persist.'));
|
|
93
|
-
lines.push('');
|
|
94
|
-
}
|
|
95
|
-
if (result.overallHealthy) {
|
|
96
|
-
lines.push(` ${chalk.green.bold('All services healthy.')}`);
|
|
97
|
-
}
|
|
98
|
-
else {
|
|
99
|
-
const downCount = result.services.filter((s) => !s.healthy).length;
|
|
100
|
-
lines.push(` ${chalk.red.bold(`${downCount} service(s) down.`)}`);
|
|
101
|
-
}
|
|
102
|
-
lines.push('');
|
|
103
|
-
return lines.join('\n');
|
|
104
|
-
}
|
|
105
|
-
// ─── Command Registration ───
|
|
106
|
-
export function registerInfraCommand(program) {
|
|
107
|
-
const infra = program
|
|
108
|
-
.command('infra')
|
|
109
|
-
.description('Infrastructure management commands');
|
|
110
|
-
infra
|
|
111
|
-
.command('status')
|
|
112
|
-
.description('Check health of API server and underlying infrastructure')
|
|
113
|
-
.addOption(new Option('--format <format>', 'Output format').choices(['text', 'json']).default('text'))
|
|
114
|
-
.action(async (options) => {
|
|
115
|
-
try {
|
|
116
|
-
const remote = await isRemoteMode();
|
|
117
|
-
console.log(chalk.gray(' Checking infrastructure health...'));
|
|
118
|
-
const result = await checkInfraStatus({ remote });
|
|
119
|
-
if (options.format === 'json') {
|
|
120
|
-
console.log(JSON.stringify(result, null, 2));
|
|
121
|
-
}
|
|
122
|
-
else {
|
|
123
|
-
console.log(formatInfraStatus(result, { remote }));
|
|
124
|
-
}
|
|
125
|
-
if (!result.overallHealthy) {
|
|
126
|
-
process.exitCode = 1;
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
catch (error) {
|
|
130
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
131
|
-
logger.error({ error: message }, 'Infra status check failed');
|
|
132
|
-
console.error(chalk.red(`Error: ${message}`));
|
|
133
|
-
process.exitCode = 1;
|
|
134
|
-
}
|
|
135
|
-
});
|
|
136
|
-
}
|
|
137
|
-
//# sourceMappingURL=infra.js.map
|