@output.ai/cli 0.7.4 → 0.7.5

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.
@@ -9,21 +9,27 @@ const ANSI = {
9
9
  RESET: '\x1b[0m',
10
10
  DIM: '\x1b[2m',
11
11
  BOLD: '\x1b[1m',
12
- CYAN: '\x1b[36m'
12
+ CYAN: '\x1b[36m',
13
+ RED: '\x1b[31m',
14
+ YELLOW: '\x1b[33m',
15
+ BG_RED: '\x1b[41m',
16
+ WHITE: '\x1b[37m'
13
17
  };
14
18
  const STATUS_ICONS = {
15
19
  [SERVICE_HEALTH.HEALTHY]: '●',
16
20
  [SERVICE_HEALTH.UNHEALTHY]: '○',
17
21
  [SERVICE_HEALTH.STARTING]: '◐',
18
22
  [SERVICE_HEALTH.NONE]: '●',
19
- [SERVICE_STATE.RUNNING]: '●'
23
+ [SERVICE_STATE.RUNNING]: '●',
24
+ [SERVICE_STATE.EXITED]: '✗'
20
25
  };
21
26
  const STATUS_COLORS = {
22
27
  [SERVICE_HEALTH.HEALTHY]: '\x1b[32m',
23
28
  [SERVICE_HEALTH.UNHEALTHY]: '\x1b[31m',
24
29
  [SERVICE_HEALTH.STARTING]: '\x1b[33m',
25
30
  [SERVICE_HEALTH.NONE]: '\x1b[34m',
26
- [SERVICE_STATE.RUNNING]: '\x1b[34m'
31
+ [SERVICE_STATE.RUNNING]: '\x1b[34m',
32
+ [SERVICE_STATE.EXITED]: '\x1b[31m'
27
33
  };
28
34
  const formatService = (service) => {
29
35
  const healthKey = service.health === SERVICE_HEALTH.NONE ? service.state : service.health;
@@ -35,6 +41,27 @@ const formatService = (service) => {
35
41
  const statusPadded = status.padEnd(10);
36
42
  return ` ${color}${icon}${ANSI.RESET} ${name} ${ANSI.DIM}${statusPadded}${ANSI.RESET} ${ANSI.DIM}${ports}${ANSI.RESET}`;
37
43
  };
44
+ const getFailedServicesWarning = (services) => {
45
+ const failedServices = services.filter(s => s.state === SERVICE_STATE.EXITED);
46
+ if (failedServices.length === 0) {
47
+ return [];
48
+ }
49
+ const failedNames = failedServices.map(s => s.name);
50
+ const hasWorkerFailed = failedNames.some(name => name.toLowerCase().includes('worker'));
51
+ const warningLines = [
52
+ '',
53
+ `${ANSI.BG_RED}${ANSI.WHITE}${ANSI.BOLD} ⚠️ SERVICE FAILURE DETECTED ${ANSI.RESET}`,
54
+ '',
55
+ `${ANSI.RED}${ANSI.BOLD}Failed services:${ANSI.RESET} ${failedNames.join(', ')}`
56
+ ];
57
+ if (hasWorkerFailed) {
58
+ warningLines.push('', `${ANSI.YELLOW}${ANSI.BOLD}⚡ The worker is not running!${ANSI.RESET}`, `${ANSI.YELLOW} Workflows will fail until the worker is restarted.${ANSI.RESET}`, '', `${ANSI.DIM}Check the logs with: docker compose logs worker${ANSI.RESET}`);
59
+ }
60
+ else {
61
+ warningLines.push('', `${ANSI.DIM}Check the logs with: docker compose logs <service-name>${ANSI.RESET}`);
62
+ }
63
+ return warningLines;
64
+ };
38
65
  const poll = async (fn, intervalMs) => {
39
66
  for (;;) {
40
67
  await fn();
@@ -113,10 +140,12 @@ export default class Dev extends Command {
113
140
  const outputServiceStatus = async () => {
114
141
  try {
115
142
  const services = await getServiceStatus(dockerComposePath);
143
+ const failureWarning = getFailedServicesWarning(services);
116
144
  const lines = [
117
145
  `${ANSI.BOLD}📊 Service Status${ANSI.RESET}`,
118
146
  '',
119
147
  ...services.map(formatService),
148
+ ...failureWarning,
120
149
  '',
121
150
  `${ANSI.CYAN}🌐 Temporal UI:${ANSI.RESET} ${ANSI.BOLD}http://localhost:8080${ANSI.RESET}`,
122
151
  '',
@@ -76,7 +76,7 @@ export function parseServiceStatus(jsonOutput) {
76
76
  });
77
77
  }
78
78
  export async function getServiceStatus(dockerComposePath) {
79
- const result = execFileSync('docker', ['compose', '-f', dockerComposePath, 'ps', '--format', 'json'], { encoding: 'utf-8', cwd: process.cwd() });
79
+ const result = execFileSync('docker', ['compose', '-f', dockerComposePath, 'ps', '--all', '--format', 'json'], { encoding: 'utf-8', cwd: process.cwd() });
80
80
  return parseServiceStatus(result);
81
81
  }
82
82
  const STATUS_ICONS = {
@@ -73,7 +73,7 @@ describe('docker service', () => {
73
73
  const mockOutput = '{"Service":"redis","State":"running","Health":"healthy","Publishers":[]}';
74
74
  vi.mocked(execFileSync).mockReturnValue(mockOutput);
75
75
  await getServiceStatus('/path/to/docker-compose.yml');
76
- expect(execFileSync).toHaveBeenCalledWith('docker', ['compose', '-f', '/path/to/docker-compose.yml', 'ps', '--format', 'json'], expect.objectContaining({ encoding: 'utf-8' }));
76
+ expect(execFileSync).toHaveBeenCalledWith('docker', ['compose', '-f', '/path/to/docker-compose.yml', 'ps', '--all', '--format', 'json'], expect.objectContaining({ encoding: 'utf-8' }));
77
77
  });
78
78
  it('should return parsed service status', async () => {
79
79
  const mockOutput = '{"Service":"redis","State":"running","Health":"healthy","Publishers":[{"PublishedPort":6379,"TargetPort":6379}]}';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@output.ai/cli",
3
- "version": "0.7.4",
3
+ "version": "0.7.5",
4
4
  "description": "CLI for Output.ai workflow generation",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",