wilfredwake 1.0.5 → 1.0.7

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/bin/cli.js CHANGED
@@ -14,6 +14,7 @@
14
14
 
15
15
  import { Command } from 'commander';
16
16
  import chalk from 'chalk';
17
+ import fs from 'fs';
17
18
  import { initCommand } from '../src/cli/commands/init.js';
18
19
  import { statusCommand } from '../src/cli/commands/status.js';
19
20
  import { wakeCommand } from '../src/cli/commands/wake.js';
@@ -30,7 +31,16 @@ program
30
31
  .description(
31
32
  chalk.cyan('🌅 CLI Tool for Multi-Developer Development Environment Wake & Status Management')
32
33
  )
33
- .version('1.0.0')
34
+ .version(
35
+ (() => {
36
+ try {
37
+ const pkg = JSON.parse(fs.readFileSync(new URL('../package.json', import.meta.url)));
38
+ return pkg.version || '0.0.0';
39
+ } catch (e) {
40
+ return '0.0.0';
41
+ }
42
+ })()
43
+ )
34
44
  .usage(chalk.yellow('[command] [options]'));
35
45
 
36
46
  // ═══════════════════════════════════════════════════════════════
@@ -59,7 +69,7 @@ program
59
69
  program
60
70
  .command('status [service]')
61
71
  .description(chalk.green('Check status of all services or a specific service'))
62
- .option('-e, --env <environment>', 'Environment (dev, staging, prod)', 'dev')
72
+ .option('-e, --env <environment>', 'Environment (dev, staging, prod)')
63
73
  .option('-f, --format <format>', 'Output format (table, json)', 'table')
64
74
  .action(statusCommand);
65
75
 
@@ -72,7 +82,7 @@ program
72
82
  program
73
83
  .command('wake [target]')
74
84
  .description(chalk.blue('Wake services on demand (all, <service>, or <group>)'))
75
- .option('-e, --env <environment>', 'Environment (dev, staging, prod)', 'dev')
85
+ .option('-e, --env <environment>', 'Environment (dev, staging, prod)')
76
86
  .option('--no-wait', 'Don\'t wait for services to be ready')
77
87
  .option('--timeout <seconds>', 'Timeout for service readiness', '300')
78
88
  .action(wakeCommand);
@@ -85,7 +95,7 @@ program
85
95
  program
86
96
  .command('health [service]')
87
97
  .description(chalk.cyan('Check health status of services'))
88
- .option('-e, --env <environment>', 'Environment (dev, staging, prod)', 'dev')
98
+ .option('-e, --env <environment>', 'Environment (dev, staging, prod)')
89
99
  .action(healthCommand);
90
100
 
91
101
  // ═══════════════════════════════════════════════════════════════
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "wilfredwake",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "description": "CLI Tool for Multi-Developer Development Environment Wake & Status Management",
5
5
  "type": "module",
6
6
  "main": "index.js",
@@ -126,14 +126,14 @@ function _displayTableStatus(services, environment) {
126
126
  const lastWoken = service.lastWakeTime
127
127
  ? new Date(service.lastWakeTime).toLocaleString()
128
128
  : 'Never';
129
-
130
129
  const cells = [
131
130
  chalk.cyan(service.name.padEnd(20)),
132
131
  statusColor(service.status.toUpperCase().padEnd(20)),
133
132
  chalk.yellow(lastWoken.padEnd(20)),
134
- chalk.gray(service.url.substring(0, 20).padEnd(20)),
133
+ chalk.gray((service.url || '').substring(0, 20).padEnd(20)),
135
134
  ];
136
135
  console.log(format.tableRow(cells));
136
+ console.log(''); // Extra spacing between rows for clarity
137
137
  });
138
138
 
139
139
  console.log(''); // Spacing
@@ -158,23 +158,16 @@ function _displaySummary(services) {
158
158
 
159
159
  const stats = {
160
160
  total: services.length,
161
- ready: services.filter(s => s.status === 'ready').length,
162
- sleeping: services.filter(s => s.status === 'sleeping').length,
161
+ live: services.filter(s => s.status === 'live').length,
162
+ dead: services.filter(s => s.status === 'dead').length,
163
163
  waking: services.filter(s => s.status === 'waking').length,
164
164
  failed: services.filter(s => s.status === 'failed').length,
165
+ unknown: services.filter(s => s.status === 'unknown').length,
165
166
  };
166
167
 
167
168
  console.log(chalk.magentaBright.bold('Summary'));
168
169
  console.log(
169
- ` ${colors.status.ready('✓')} Ready: ${colors.status.ready(
170
- stats.ready
171
- )} | ${colors.status.sleeping('⚫')} Sleeping: ${colors.status.sleeping(
172
- stats.sleeping
173
- )} | ${colors.status.waking('⟳')} Waking: ${colors.status.waking(
174
- stats.waking
175
- )} | ${colors.status.failed('✗')} Failed: ${colors.status.failed(
176
- stats.failed
177
- )}`
170
+ ` ${colors.status.live('✓')} Live: ${colors.status.live(stats.live)} | ${colors.status.dead('⚫')} Dead: ${colors.status.dead(stats.dead)} | ${colors.status.waking('⟳')} Waking: ${colors.status.waking(stats.waking)} | ${colors.status.failed('✗')} Failed: ${colors.status.failed(stats.failed)} | ${colors.status.unknown('?')} Unknown: ${colors.status.unknown(stats.unknown)}`
178
171
  );
179
172
  console.log(` Total: ${stats.total} services\n`);
180
173
  }
@@ -82,13 +82,22 @@ export async function wakeCommand(target, options) {
82
82
  _displayWakeResults(results);
83
83
 
84
84
  // ═══════════════════════════════════════════════════════════════
85
- // DISPLAY FINAL STATUS
85
+ // DISPLAY FINAL STATUS AND START MONITORING
86
86
  // ═══════════════════════════════════════════════════════════════
87
87
  if (shouldWait) {
88
88
  _displayFinalStatus(results);
89
+
90
+ // Start 5-minute monitoring period
91
+ await _monitorServicesForDuration(
92
+ config.orchestratorUrl,
93
+ config.token,
94
+ env,
95
+ wakeTarget,
96
+ 5 * 60 * 1000 // 5 minutes in milliseconds
97
+ );
89
98
  }
90
99
 
91
- process.exit(results.success ? 0 : 1);
100
+ process.exit(0);
92
101
  } catch (error) {
93
102
  stopSpinner?.();
94
103
  throw error;
@@ -180,3 +189,167 @@ function _displayFinalStatus(results) {
180
189
 
181
190
  console.log('');
182
191
  }
192
+
193
+ /**
194
+ * Monitor services for a duration and display live status updates
195
+ * @private
196
+ * @param {string} orchestratorUrl - Orchestrator URL
197
+ * @param {string} token - Authorization token
198
+ * @param {string} env - Environment name
199
+ * @param {string} wakeTarget - Wake target (all, <service>, or <group>)
200
+ * @param {number} duration - Duration in milliseconds
201
+ */
202
+ async function _monitorServicesForDuration(
203
+ orchestratorUrl,
204
+ token,
205
+ env,
206
+ wakeTarget,
207
+ duration
208
+ ) {
209
+ const pollInterval = 10000; // Poll every 10 seconds
210
+ const startTime = Date.now();
211
+ const endTime = startTime + duration;
212
+ let pollCount = 0;
213
+
214
+ console.log(
215
+ chalk.magentaBright.bold(
216
+ `📡 Monitoring services for ${Math.round(duration / 1000)}s...\n`
217
+ )
218
+ );
219
+
220
+ while (Date.now() < endTime) {
221
+ pollCount++;
222
+ const elapsedSeconds = Math.round((Date.now() - startTime) / 1000);
223
+
224
+ try {
225
+ // Fetch current status from orchestrator
226
+ const response = await axios.get(
227
+ `${orchestratorUrl}/api/status`,
228
+ {
229
+ params: {
230
+ environment: env,
231
+ },
232
+ timeout: 5000,
233
+ headers: {
234
+ Authorization: token ? `Bearer ${token}` : undefined,
235
+ },
236
+ }
237
+ );
238
+
239
+ const services = response.data.services || [];
240
+
241
+ // Clear screen and display updated status
242
+ console.clear();
243
+ console.log(chalk.cyanBright.bold('📡 Live Service Monitoring\n'));
244
+ console.log(
245
+ chalk.gray(`Elapsed: ${chalk.yellow(elapsedSeconds)}s / ${Math.round(duration / 1000)}s | Poll #${pollCount}\n`)
246
+ );
247
+
248
+ // Display table with current status
249
+ _displayLiveMonitoringTable(services, env);
250
+
251
+ // Display summary stats
252
+ _displayLiveMonitoringSummary(services);
253
+ } catch (error) {
254
+ console.clear();
255
+ console.log(chalk.cyanBright.bold('📡 Live Service Monitoring\n'));
256
+ console.log(
257
+ chalk.gray(`Elapsed: ${chalk.yellow(elapsedSeconds)}s / ${Math.round(duration / 1000)}s | Poll #${pollCount}\n`)
258
+ );
259
+ logger.warn(`Status check failed: ${error.message}`);
260
+ }
261
+
262
+ // Wait before next poll (unless we're at the end)
263
+ if (Date.now() < endTime) {
264
+ await new Promise(resolve => setTimeout(resolve, pollInterval));
265
+ }
266
+ }
267
+
268
+ // Final summary
269
+ console.clear();
270
+ console.log(chalk.cyanBright.bold('📡 Monitoring Complete\n'));
271
+ console.log(chalk.yellow(`Total monitoring duration: ${Math.round(duration / 1000)}s\n`));
272
+
273
+ try {
274
+ // Fetch final status
275
+ const response = await axios.get(
276
+ `${orchestratorUrl}/api/status`,
277
+ {
278
+ params: {
279
+ environment: env,
280
+ },
281
+ timeout: 5000,
282
+ headers: {
283
+ Authorization: token ? `Bearer ${token}` : undefined,
284
+ },
285
+ }
286
+ );
287
+
288
+ const services = response.data.services || [];
289
+ _displayLiveMonitoringTable(services, env);
290
+ _displayLiveMonitoringSummary(services);
291
+ } catch (error) {
292
+ logger.warn(`Could not fetch final status: ${error.message}`);
293
+ }
294
+
295
+ console.log('');
296
+ }
297
+
298
+ /**
299
+ * Display live monitoring status table
300
+ * @private
301
+ * @param {Array} services - Services array
302
+ * @param {string} environment - Environment name
303
+ */
304
+ function _displayLiveMonitoringTable(services, environment) {
305
+ if (services.length === 0) {
306
+ logger.info('No services to display.');
307
+ return;
308
+ }
309
+
310
+ console.log(chalk.cyanBright.bold(`Services (${environment.toUpperCase()})\n`));
311
+
312
+ const headers = ['Service', 'Status', 'Last Woken', 'URL'];
313
+ console.log(format.tableHeader(headers));
314
+
315
+ services.forEach((service) => {
316
+ const statusColor = colors.status[service.status] || colors.status.unknown;
317
+ const lastWoken = service.lastWakeTime
318
+ ? new Date(service.lastWakeTime).toLocaleString()
319
+ : 'Never';
320
+ const cells = [
321
+ chalk.cyan(service.name.padEnd(20)),
322
+ statusColor(service.status.toUpperCase().padEnd(20)),
323
+ chalk.yellow(lastWoken.padEnd(20)),
324
+ chalk.gray((service.url || '').substring(0, 20).padEnd(20)),
325
+ ];
326
+ console.log(format.tableRow(cells));
327
+ console.log('');
328
+ });
329
+
330
+ console.log('');
331
+ }
332
+
333
+ /**
334
+ * Display live monitoring summary stats
335
+ * @private
336
+ * @param {Array} services - Services array
337
+ */
338
+ function _displayLiveMonitoringSummary(services) {
339
+ if (services.length === 0) return;
340
+
341
+ const stats = {
342
+ total: services.length,
343
+ live: services.filter(s => s.status === 'live').length,
344
+ dead: services.filter(s => s.status === 'dead').length,
345
+ waking: services.filter(s => s.status === 'waking').length,
346
+ failed: services.filter(s => s.status === 'failed').length,
347
+ unknown: services.filter(s => s.status === 'unknown').length,
348
+ };
349
+
350
+ console.log(chalk.magentaBright.bold('Summary'));
351
+ console.log(
352
+ ` ${colors.status.live('✓')} Live: ${colors.status.live(stats.live)} | ${colors.status.dead('⚫')} Dead: ${colors.status.dead(stats.dead)} | ${colors.status.waking('⟳')} Waking: ${colors.status.waking(stats.waking)} | ${colors.status.failed('✗')} Failed: ${colors.status.failed(stats.failed)} | ${colors.status.unknown('?')} Unknown: ${colors.status.unknown(stats.unknown)}`
353
+ );
354
+ console.log(` Total: ${stats.total} services\n`);
355
+ }
@@ -283,17 +283,13 @@ export class Orchestrator {
283
283
  // ═══════════════════════════════════════════════════════════════
284
284
  // DETERMINE STATE FROM STATUS CODE
285
285
  // ═══════════════════════════════════════════════════════════════
286
- let state = ServiceState.UNKNOWN;
287
-
288
- if (response.status < 300) {
289
- state = ServiceState.LIVE; // Changed from READY
290
- } else if (response.status === 503) {
291
- state = ServiceState.DEAD; // Changed from SLEEPING
292
- } else if (response.status >= 500) {
293
- state = ServiceState.FAILED;
294
- } else if (response.status >= 400) {
295
- state = ServiceState.UNKNOWN;
296
- }
286
+ // NEW LOGIC: If we get ANY response from the API (2xx, 3xx, 4xx, 5xx),
287
+ // mark the service as LIVE. This means the service is responsive.
288
+ // Only mark as FAILED if no response is received at all.
289
+ let state = ServiceState.LIVE;
290
+
291
+ // If we got a response, the service is responsive = LIVE
292
+ // No need to check status code, any response means service can be reached
297
293
 
298
294
  this._logTimestamp(
299
295
  service.name,
@@ -316,7 +312,7 @@ export class Orchestrator {
316
312
  );
317
313
 
318
314
  return {
319
- state: ServiceState.DEAD, // Changed: assume dead on error
315
+ state: ServiceState.DEAD, // Only dead if no response received
320
316
  statusCode: null,
321
317
  responseTime,
322
318
  error: error.message,