@constellation-network/node-pilot 0.1.0 → 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 CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @constellation-network/node-pilot
21
21
  $ cpilot COMMAND
22
22
  running command...
23
23
  $ cpilot (--version|-v)
24
- @constellation-network/node-pilot/0.1.0 darwin-arm64 node-v22.15.0
24
+ @constellation-network/node-pilot/0.2.1 darwin-arm64 node-v22.15.0
25
25
  $ cpilot --help [COMMAND]
26
26
  USAGE
27
27
  $ cpilot COMMAND
@@ -37,7 +37,7 @@ If no command is entered, node-pilot will automatically perform a series of chec
37
37
 
38
38
  # Commands
39
39
  <!-- commands -->
40
- * [`cpilot clean LAYER`](#cpilot-clean-layer)
40
+ * [`cpilot clean [LAYER]`](#cpilot-clean-layer)
41
41
  * [`cpilot config`](#cpilot-config)
42
42
  * [`cpilot config get [NAME]`](#cpilot-config-get-name)
43
43
  * [`cpilot config set NAME VALUE`](#cpilot-config-set-name-value)
@@ -48,21 +48,21 @@ If no command is entered, node-pilot will automatically perform a series of chec
48
48
  * [`cpilot shutdown`](#cpilot-shutdown)
49
49
  * [`cpilot status`](#cpilot-status)
50
50
 
51
- ## `cpilot clean LAYER`
51
+ ## `cpilot clean [LAYER]`
52
52
 
53
53
  Remove data and/or logs from a validator node
54
54
 
55
55
  ```
56
56
  USAGE
57
- $ cpilot clean LAYER [-a] [-d] [-l]
57
+ $ cpilot clean [LAYER] [-d] [-j] [-l]
58
58
 
59
59
  ARGUMENTS
60
60
  LAYER network layer to clean. e.g. gl0
61
61
 
62
62
  FLAGS
63
- -a, --all remove all data and logs
64
- -d, --data remove data
65
- -l, --logs remove logs
63
+ -d, --data remove only data
64
+ -j, --jars remove only jars
65
+ -l, --logs remove only logs
66
66
 
67
67
  DESCRIPTION
68
68
  Remove data and/or logs from a validator node
@@ -71,7 +71,7 @@ EXAMPLES
71
71
  $ cpilot clean
72
72
  ```
73
73
 
74
- _See code: [src/commands/clean.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.1.0/src/commands/clean.ts)_
74
+ _See code: [src/commands/clean.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.2.1/src/commands/clean.ts)_
75
75
 
76
76
  ## `cpilot config`
77
77
 
@@ -88,7 +88,7 @@ EXAMPLES
88
88
  $ cpilot config
89
89
  ```
90
90
 
91
- _See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.1.0/src/commands/config.ts)_
91
+ _See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.2.1/src/commands/config.ts)_
92
92
 
93
93
  ## `cpilot config get [NAME]`
94
94
 
@@ -112,7 +112,7 @@ EXAMPLES
112
112
  $ cpilot config get gl0:CL_PUBLIC_HTTP_PORT
113
113
  ```
114
114
 
115
- _See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.1.0/src/commands/config/get.ts)_
115
+ _See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.2.1/src/commands/config/get.ts)_
116
116
 
117
117
  ## `cpilot config set NAME VALUE`
118
118
 
@@ -135,7 +135,7 @@ EXAMPLES
135
135
  $ cpilot config set gl0:CL_PUBLIC_HTTP_PORT 9000
136
136
  ```
137
137
 
138
- _See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.1.0/src/commands/config/set.ts)_
138
+ _See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.2.1/src/commands/config/set.ts)_
139
139
 
140
140
  ## `cpilot help [COMMAND]`
141
141
 
@@ -172,7 +172,7 @@ EXAMPLES
172
172
  $ cpilot info
173
173
  ```
174
174
 
175
- _See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.1.0/src/commands/info.ts)_
175
+ _See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.2.1/src/commands/info.ts)_
176
176
 
177
177
  ## `cpilot logs LAYER`
178
178
 
@@ -180,12 +180,14 @@ view validator node runtime logs
180
180
 
181
181
  ```
182
182
  USAGE
183
- $ cpilot logs LAYER [-f] [-n <value>]
183
+ $ cpilot logs LAYER [-a app|hc] [-f] [-n <value>]
184
184
 
185
185
  ARGUMENTS
186
186
  LAYER network layer to view. e.g. gl0
187
187
 
188
188
  FLAGS
189
+ -a, --area=<option> [default: hc] area to view logs for. e.g. "app" or "hc" for health-check
190
+ <options: app|hc>
189
191
  -f, --follow continuously wait for additional data to be appended
190
192
  -n, --numOfLines=<value> number of lines at the end of the log to display
191
193
 
@@ -196,7 +198,7 @@ EXAMPLES
196
198
  $ cpilot logs
197
199
  ```
198
200
 
199
- _See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.1.0/src/commands/logs.ts)_
201
+ _See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.2.1/src/commands/logs.ts)_
200
202
 
201
203
  ## `cpilot restart [LAYER]`
202
204
 
@@ -224,7 +226,7 @@ EXAMPLES
224
226
  $ cpilot restart
225
227
  ```
226
228
 
227
- _See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.1.0/src/commands/restart.ts)_
229
+ _See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.2.1/src/commands/restart.ts)_
228
230
 
229
231
  ## `cpilot shutdown`
230
232
 
@@ -244,7 +246,7 @@ EXAMPLES
244
246
  $ cpilot shutdown
245
247
  ```
246
248
 
247
- _See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.1.0/src/commands/shutdown.ts)_
249
+ _See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.2.1/src/commands/shutdown.ts)_
248
250
 
249
251
  ## `cpilot status`
250
252
 
@@ -258,5 +260,5 @@ DESCRIPTION
258
260
  Display node status and configuration settings
259
261
  ```
260
262
 
261
- _See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.1.0/src/commands/status.ts)_
263
+ _See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.2.1/src/commands/status.ts)_
262
264
  <!-- commandsstop -->
@@ -1,5 +1,6 @@
1
1
  export declare const checkNodePilot: {
2
2
  checkDiscordRegistration(): Promise<void>;
3
3
  checkVersion(): Promise<void>;
4
+ isDiscordAlertsEnabled(): any;
4
5
  promptDiscordRegistration(): Promise<void>;
5
6
  };
@@ -67,6 +67,11 @@ export const checkNodePilot = {
67
67
  clm.postStep('Update completed. Run cpilot again to use the latest version');
68
68
  process.exit(0);
69
69
  },
70
+ isDiscordAlertsEnabled() {
71
+ const hcStorage = getHcStorage();
72
+ const user = hcStorage.getItem('user');
73
+ return user && user.webHookEnabled;
74
+ },
70
75
  async promptDiscordRegistration() {
71
76
  const hcStorage = getHcStorage();
72
77
  const join = await promptHelper.confirmPrompt('Do you want to enable Discord notifications for your node?:');
@@ -70,7 +70,7 @@ export const checkProject = {
70
70
  if (nInfo.version !== clusterVersion) {
71
71
  const answer = await input({
72
72
  default: 'y',
73
- message: `A new required network version has been detected. Do you want to upgrade now? (y/n): `
73
+ message: `A new required network version has been detected. ${clusterVersion}. Do you want to upgrade now? (y/n): `
74
74
  });
75
75
  if (answer !== 'y') {
76
76
  process.exit(0);
package/dist/clm.js CHANGED
@@ -1,4 +1,6 @@
1
1
  import chalk from "chalk";
2
+ import { configStore } from "./config-store.js";
3
+ import { serviceLog } from "./helpers/service-log.js";
2
4
  /*
3
5
  Command Line Messaging with color styling
4
6
  */
@@ -9,25 +11,28 @@ export const clm = {
9
11
  }
10
12
  },
11
13
  echo(msg) {
12
- console.log(msg);
14
+ o().log(msg);
13
15
  },
14
16
  echoRepeatLine(char) {
15
17
  process.stdout.write('\r' + char);
16
18
  },
17
19
  error(msg, silent = true) {
18
- console.error(chalk.red(msg));
20
+ o().error(chalk.red(msg));
19
21
  process.exit(silent ? 0 : 1);
20
22
  },
21
23
  postStep(s) {
22
- console.log(chalk.green(s));
24
+ o().log(chalk.green(s));
23
25
  },
24
26
  preStep(s) {
25
- console.log('\n' + chalk.italic(chalk.green(s)));
27
+ o().log('\n' + chalk.italic(chalk.green(s)));
26
28
  },
27
29
  step(msg) {
28
- console.warn(chalk.whiteBright(msg));
30
+ o().warn(chalk.whiteBright(msg));
29
31
  },
30
32
  warn(msg) {
31
- console.warn(chalk.red(msg));
33
+ o().warn(chalk.red(msg));
32
34
  }
33
35
  };
36
+ function o() {
37
+ return configStore.isRestarting() ? serviceLog : console;
38
+ }
@@ -1,13 +1,13 @@
1
1
  import { Command } from '@oclif/core';
2
2
  export default class Clean extends Command {
3
3
  static args: {
4
- layer: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
4
+ layer: import("@oclif/core/interfaces").Arg<string | undefined, Record<string, unknown>>;
5
5
  };
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
- all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
9
  data: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ jars: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
11
  logs: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
12
  };
13
13
  run(): Promise<void>;
@@ -7,39 +7,50 @@ import { dockerService } from "../services/docker-service.js";
7
7
  import { shellService } from "../services/shell-service.js";
8
8
  export default class Clean extends Command {
9
9
  static args = {
10
- layer: Args.string({ description: 'network layer to clean. e.g. gl0', required: true }),
10
+ layer: Args.string({ description: 'network layer to clean. e.g. gl0', required: false }),
11
11
  };
12
12
  static description = 'Remove data and/or logs from a validator node';
13
13
  static examples = [
14
14
  '<%= config.bin %> <%= command.id %>',
15
15
  ];
16
16
  static flags = {
17
- all: Flags.boolean({ char: 'a', description: 'remove all data and logs' }),
18
- data: Flags.boolean({ char: 'd', description: 'remove data' }),
19
- logs: Flags.boolean({ char: 'l', description: 'remove logs' })
17
+ data: Flags.boolean({ char: 'd', description: 'remove only data' }),
18
+ jars: Flags.boolean({ char: 'j', description: 'remove only jars' }),
19
+ logs: Flags.boolean({ char: 'l', description: 'remove only logs' })
20
20
  };
21
21
  async run() {
22
22
  const { args, flags } = await this.parse(Clean);
23
23
  const { layersToRun } = configStore.getProjectInfo();
24
- if (!layersToRun.includes(args.layer)) {
24
+ if (args.layer && !layersToRun.includes(args.layer)) {
25
25
  this.error(`Invalid layer: ${args.layer}. Available layers: ${layersToRun.join(',')}`);
26
26
  }
27
- const deleteLogs = flags.logs || flags.all;
28
- const deleteData = flags.data || flags.all;
29
- if (!deleteLogs && !deleteData) {
30
- this.error('At least one of --data or --logs must be specified.');
31
- }
27
+ const layers = args.layer ? [args.layer] : layersToRun;
28
+ const deleteAll = !flags.data && !flags.logs && !flags.jars;
29
+ const deleteLogs = flags.logs || deleteAll;
30
+ const deleteData = flags.data || deleteAll;
31
+ const deleteJars = flags.jars || deleteAll;
32
32
  if (await dockerService.isRunning()) {
33
33
  clm.preStep('The validator node must be stopped first.');
34
34
  await promptHelper.doYouWishToContinue();
35
35
  await dockerService.dockerDown();
36
36
  }
37
- if (deleteData) {
38
- await shellService.runProjectCommand(`sudo rm -rf ${args.layer}/data`);
39
- projectHelper.prepareDataFolder();
40
- }
41
- if (deleteLogs) {
42
- await shellService.runProjectCommand(`sudo rm -rf ${args.layer}/logs`);
37
+ for (const layer of layers) {
38
+ if (deleteData) {
39
+ // eslint-disable-next-line no-await-in-loop
40
+ await shellService.runProjectCommand(`sudo rm -rf ${layer}/data`);
41
+ if (layer === 'gl0') {
42
+ projectHelper.prepareDataFolder();
43
+ configStore.setProjectFlag('discordChecked', false);
44
+ }
45
+ }
46
+ if (deleteLogs) {
47
+ // eslint-disable-next-line no-await-in-loop
48
+ await shellService.runProjectCommand(`sudo rm -rf ${layer}/logs`);
49
+ }
50
+ if (deleteJars) {
51
+ // eslint-disable-next-line no-await-in-loop
52
+ await shellService.runProjectCommand(`sudo rm -rf ${layer}/dist`);
53
+ }
43
54
  }
44
55
  }
45
56
  }
@@ -1,6 +1,7 @@
1
1
  import { Command } from '@oclif/core';
2
2
  import { configStore } from "../config-store.js";
3
3
  import { configHelper } from "../helpers/config-helper.js";
4
+ import { checkNodePilot } from "../checks/check-pilot.js";
4
5
  export default class Info extends Command {
5
6
  static description = 'Display general info about the validator node';
6
7
  static examples = [
@@ -28,6 +29,8 @@ export default class Info extends Command {
28
29
  // Project Directory
29
30
  configHelper.showEnvInfo('Project Directory', projectInfo.projectDir);
30
31
  // Fast Forward
31
- configHelper.showEnvInfo('Fast Forward Enabled', (projectInfo.fastForward === undefined || Boolean(projectInfo.fastForward)).toString());
32
+ // configHelper.showEnvInfo('Fast Forward Enabled', (projectInfo.fastForward === undefined || Boolean(projectInfo.fastForward)).toString());
33
+ // Discord alerts
34
+ configHelper.showEnvInfo('Discord Alerts Enabled', checkNodePilot.isDiscordAlertsEnabled());
32
35
  }
33
36
  }
@@ -6,6 +6,7 @@ export default class Logs extends Command {
6
6
  static description: string;
7
7
  static examples: string[];
8
8
  static flags: {
9
+ area: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
9
10
  follow: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
11
  numOfLines: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  };
@@ -12,6 +12,7 @@ export default class Logs extends Command {
12
12
  '<%= config.bin %> <%= command.id %>',
13
13
  ];
14
14
  static flags = {
15
+ area: Flags.string({ char: 'a', default: 'hc', description: 'area to view logs for. e.g. "app" or "hc" for health-check', options: ['app', 'hc'] }),
15
16
  follow: Flags.boolean({ char: 'f', description: 'continuously wait for additional data to be appended' }),
16
17
  numOfLines: Flags.string({ char: 'n', description: 'number of lines at the end of the log to display' }),
17
18
  };
@@ -27,8 +28,9 @@ export default class Logs extends Command {
27
28
  if (flags.follow) {
28
29
  tailFlag = ' -f';
29
30
  }
31
+ const file = flags.area === 'app' ? 'app.log' : 'health-check.log';
30
32
  const { projectDir } = configStore.getProjectInfo();
31
- const logPath = path.join(projectDir, args.layer, 'logs', 'app.log');
33
+ const logPath = path.join(projectDir, args.layer, 'logs', file);
32
34
  await shellService.runCommand(`tail ${tailFlag} ${logPath}`).catch(() => 1);
33
35
  }
34
36
  }
@@ -11,4 +11,5 @@ export default class Restart extends BaseCommand {
11
11
  };
12
12
  run(): Promise<void>;
13
13
  private restart;
14
+ private setIsRestarting;
14
15
  }
@@ -25,9 +25,9 @@ export default class Restart extends BaseCommand {
25
25
  configHelper.assertProject('No project found. ');
26
26
  if (flags.update) {
27
27
  serviceLog.log('Executing "cpilot restart --update" at ' + new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
28
+ this.setIsRestarting(true);
28
29
  const project = configStore.getActiveProject();
29
30
  const activeProjects = configStore.getRunningProjects();
30
- // serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
31
31
  for (const project of activeProjects) {
32
32
  configStore.setActiveProject(project);
33
33
  // eslint-disable-next-line no-await-in-loop
@@ -41,13 +41,14 @@ export default class Restart extends BaseCommand {
41
41
  }
42
42
  }
43
43
  configStore.setActiveProject(project);
44
+ this.setIsRestarting(false);
44
45
  return;
45
46
  }
46
47
  if (flags.autostart) {
47
48
  serviceLog.log('Executing "cpilot restart --autostart" at ' + new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
49
+ this.setIsRestarting(true);
48
50
  const project = configStore.getActiveProject();
49
51
  const activeProjects = configStore.getRunningProjects();
50
- // serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
51
52
  for (const project of activeProjects) {
52
53
  serviceLog.log(' ' + project + ' is being auto started...');
53
54
  configStore.setActiveProject(project);
@@ -55,6 +56,7 @@ export default class Restart extends BaseCommand {
55
56
  await this.restart();
56
57
  }
57
58
  configStore.setActiveProject(project);
59
+ this.setIsRestarting(false);
58
60
  return;
59
61
  }
60
62
  if (!await dockerService.isRunning()) {
@@ -75,32 +77,27 @@ export default class Restart extends BaseCommand {
75
77
  await this.restart();
76
78
  }
77
79
  async restart() {
78
- // const pAll = layersToRun.map(l => nodeService.getNodeInfo(l));
79
- // const info = await Promise.all(pAll);
80
- // const isRunning = info.some(n => n.state !== 'Unavailable');
81
- const startRestarted = configStore.isRestarting();
82
- if (startRestarted && (startRestarted + 1000 * 60 * 5 > Date.now())) {
83
- serviceLog.log('Restart already ACTIVE');
84
- return;
80
+ if (await dockerService.isRunning()) {
81
+ await nodeService.leaveClusterAllLayers();
82
+ const { layersToRun } = configStore.getProjectInfo();
83
+ await nodeService.pollForLayersState(layersToRun, 'Offline');
84
+ clm.preStep('Stopping the node...');
85
+ await dockerService.dockerDown();
85
86
  }
86
- configStore.setIsRestarting(Date.now());
87
- try {
88
- if (await dockerService.isRunning()) {
89
- await nodeService.leaveClusterAllLayers();
90
- const { layersToRun } = configStore.getProjectInfo();
91
- await nodeService.pollForLayersState(layersToRun, 'Offline');
92
- clm.preStep('Stopping the node...');
93
- serviceLog.log('Stopping the node...');
94
- await dockerService.dockerDown();
87
+ clm.preStep('Checking for a new version...');
88
+ await checkProject.runUpgrade();
89
+ clm.preStep('Starting the node...');
90
+ await dockerService.dockerRestartAll();
91
+ }
92
+ setIsRestarting(val) {
93
+ if (val) {
94
+ if (configStore.isRestarting()) {
95
+ serviceLog.log('Restart already ACTIVE');
96
+ process.exit(0);
95
97
  }
96
- clm.preStep('Checking for a new version...');
97
- serviceLog.log('Checking for a new version...');
98
- await checkProject.runUpgrade();
99
- clm.preStep('Starting the node...');
100
- serviceLog.log('Starting the node...');
101
- await dockerService.dockerRestartAll();
98
+ configStore.setIsRestarting(Date.now());
102
99
  }
103
- finally {
100
+ else {
104
101
  configStore.setIsRestarting(0);
105
102
  }
106
103
  }
@@ -4,7 +4,7 @@ import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { getRandomNode } from "../services/get-random-node.js";
7
- import { checkNodePilot } from "../checks/check-pilot.js";
7
+ import { StatusTable } from "../helpers/status-table.js";
8
8
  export default class Test extends Command {
9
9
  static description = 'node pilot test command';
10
10
  static hidden = true;
@@ -18,8 +18,8 @@ export default class Test extends Command {
18
18
  //
19
19
  // await archiverService.syncToLatestSnapshot();
20
20
  // await archiverService.checkLogsForMissingSnapshots();
21
- // await StatusTable.run();
22
- await checkNodePilot.checkVersion();
21
+ await StatusTable.run();
22
+ // await checkNodePilot.checkVersion();
23
23
  }
24
24
  async testRandomNode() {
25
25
  const filePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../../projects/hypergraph/networks/mainnet/source-nodes.env`);
@@ -18,7 +18,7 @@ declare class ConfigStore {
18
18
  getSystemInfo(): SystemInfo;
19
19
  hasProjectFlag(name: string): any;
20
20
  hasProjects(): boolean;
21
- isRestarting(): number;
21
+ isRestarting(): boolean;
22
22
  setActiveProject(name: string): void;
23
23
  setClusterStats(info: Partial<ClusterStats>): void;
24
24
  setDockerEnvInfo(info: Partial<{
@@ -106,7 +106,11 @@ class ConfigStore {
106
106
  }
107
107
  isRestarting() {
108
108
  const { restarting } = this.pilotStore.getItem('pilot');
109
- return restarting;
109
+ if (restarting && restarting + 1000 * 60 * 5 < Date.now()) {
110
+ this.setIsRestarting(0);
111
+ return false;
112
+ }
113
+ return restarting > 0;
110
114
  }
111
115
  setActiveProject(name) {
112
116
  const { appDir, project, projects } = this.pilotStore.getItem('pilot');
@@ -1,3 +1,5 @@
1
1
  export declare const serviceLog: {
2
+ error(s: string): void;
2
3
  log(s: string): void;
4
+ warn(s: string): void;
3
5
  };
@@ -2,9 +2,15 @@ import fs from "node:fs";
2
2
  import path from "node:path";
3
3
  import { configStore } from "../config-store.js";
4
4
  export const serviceLog = {
5
+ error(s) {
6
+ this.log(s);
7
+ },
5
8
  log(s) {
6
9
  const appDir = configStore.getAppDir();
7
10
  const logFile = path.join(appDir, 'logs', 'service.log');
8
11
  fs.appendFileSync(logFile, s + '\n');
9
12
  },
13
+ warn(s) {
14
+ this.log(s);
15
+ },
10
16
  };
@@ -19,6 +19,20 @@ export declare const statusTableHeader: ({
19
19
  value: string;
20
20
  width: number;
21
21
  })[];
22
+ export declare const glHeader1: {
23
+ color: string;
24
+ formatter: (value: string) => string;
25
+ headerColor: string;
26
+ value: string;
27
+ width: number;
28
+ }[];
29
+ export declare const glHeader2: {
30
+ color: string;
31
+ formatter: (value: string) => string;
32
+ headerColor: string;
33
+ value: string;
34
+ width: number;
35
+ }[];
22
36
  export type NodeStatusInfo = {
23
37
  clusterOrdinal: number;
24
38
  clusterSession: string;
@@ -57,10 +57,14 @@ class CellFormatter {
57
57
  return this.style(value, "bgRed", "bold");
58
58
  if (value === 'Ready')
59
59
  return this.style(value, "green");
60
+ if (value === 'HydratingSnapshots')
61
+ return this.style(value, "cyan");
60
62
  if (value === 'ReadyToJoin' || value === 'JoiningCluster')
61
63
  return this.style(value, "yellow", "bold");
62
64
  if (value === 'Restarting')
63
65
  return this.style(value, "yellow", "bold");
66
+ if (value.startsWith('Ready'))
67
+ return this.style(value, "green");
64
68
  return this.style(value, "white");
65
69
  }
66
70
  formatUpTIme(startTime) {
@@ -108,3 +112,17 @@ export const statusTableHeader = [
108
112
  { color: 'white', formatter: formatMem, headerColor: 'whiteBright', value: 'Mem Usage (GB)', width: 16 },
109
113
  { color: 'white', formatter: formatError, headerColor: 'whiteBright', value: 'Error', width: 22 },
110
114
  ];
115
+ export const glHeader1 = [
116
+ // { color: 'white', headerColor: 'whiteBright', value: 'Network' },
117
+ // { color: 'whiteBright', headerColor: 'whiteBright', value: 'Layer' },
118
+ { color: 'white', formatter: formatState, headerColor: 'whiteBright', value: 'Node State', width: 22 },
119
+ { color: 'white', formatter: formatUpTIme, headerColor: 'whiteBright', value: 'Uptime', width: 13 },
120
+ { color: 'white', formatter: formatOrdinal, headerColor: 'whiteBright', value: 'Ordinal', width: 16 },
121
+ { color: 'white', formatter: formatDistance, headerColor: 'whiteBright', value: 'Distance', width: 22 },
122
+ ];
123
+ export const glHeader2 = [
124
+ { color: 'white', formatter: formatState, headerColor: 'whiteBright', value: 'Cluster State', width: 22 },
125
+ { color: 'white', formatter: formatCpu, headerColor: 'whiteBright', value: 'CPU Usage', width: 13 },
126
+ { color: 'white', formatter: formatMem, headerColor: 'whiteBright', value: 'Mem Usage (GB)', width: 16 },
127
+ { color: 'white', formatter: formatError, headerColor: 'whiteBright', value: 'Error', width: 22 },
128
+ ];
@@ -3,6 +3,8 @@ export declare class StatusTable {
3
3
  private previousHeight;
4
4
  static run(): Promise<void>;
5
5
  private getProjectInfo;
6
- private monitorState;
7
- private render;
6
+ private monitorNarrow;
7
+ private monitorWide;
8
+ private renderNarrow;
9
+ private renderWide;
8
10
  }
@@ -5,7 +5,7 @@ import os from "node:os";
5
5
  import ttyTable from "tty-table";
6
6
  import { clm } from "../clm.js";
7
7
  import { configStore } from "../config-store.js";
8
- import { formatTimeAgo, statusTableHeader } from "./status-table-helper.js";
8
+ import { formatTimeAgo, glHeader1, glHeader2, statusTableHeader } from "./status-table-helper.js";
9
9
  export class StatusTable {
10
10
  alreadyRendered = false;
11
11
  previousHeight = 0;
@@ -18,7 +18,14 @@ export class StatusTable {
18
18
  process.exit();
19
19
  });
20
20
  const table = new StatusTable();
21
- table.monitorState();
21
+ const cols = process.stdout.columns;
22
+ // console.log(String('Colspan: ' + cols));
23
+ if (cols > 120) {
24
+ table.monitorWide();
25
+ }
26
+ else {
27
+ table.monitorNarrow();
28
+ }
22
29
  const onKeyPress = createPrompt((_, done) => {
23
30
  useKeypress(() => { done(''); });
24
31
  return '';
@@ -45,23 +52,73 @@ export class StatusTable {
45
52
  configStore.setActiveProject(activeProject);
46
53
  return info;
47
54
  }
48
- async monitorState() {
49
- const projects = this.getProjectInfo();
55
+ async monitorNarrow() {
56
+ const projectInfos = this.getProjectInfo();
57
+ const values = {};
58
+ // const err = { date: '', layer: '', msg:'', timeAgo: '' }
59
+ while (true) {
60
+ // const rows = [];
61
+ const tables = [];
62
+ for (const info of projectInfos) {
63
+ if (!values[info.layer])
64
+ values[info.layer] = {};
65
+ const n = fs.existsSync(info.path) ? JSON.parse(fs.readFileSync(info.path, 'utf8')) : {};
66
+ const projectName = info.project === 'hypergraph' ? '' : ':' + info.project;
67
+ const network = info.network === 'integrationnet' ? 'intnet' : info.network;
68
+ const distance = Number.isNaN(n.clusterOrdinal - n.ordinal) ? '-' : String(n.clusterOrdinal - n.ordinal);
69
+ const ordinal = (values[info.layer].ordinal === n.ordinal) ? String(n.ordinal) : n.ordinal + ':true';
70
+ const label = info.layer + ':' + network + projectName;
71
+ const row1 = [
72
+ n.state,
73
+ n.session,
74
+ n.ordinal ? ordinal : '-',
75
+ distance,
76
+ ];
77
+ const row2 = [
78
+ n.clusterState || 'Ready',
79
+ n.cpuUsage || '-',
80
+ n.memUsage || '-',
81
+ n.error || '-'
82
+ ];
83
+ values[info.layer].ordinal = n.ordinal;
84
+ let errorMsg = '';
85
+ if ((info.layer === 'gl0' || info.layer === 'ml0') && n.lastError) {
86
+ const d = new Date(n.errorDate);
87
+ // if under 8 hours ago
88
+ if ((d.getTime() + (8 * 60 * 60 * 1000) > Date.now())) {
89
+ const date = new Date(n.errorDate).toISOString();
90
+ const timeAgo = formatTimeAgo(Date.now() - n.errorDate) || '';
91
+ errorMsg = chalk.green(` AUTO HEALED (${timeAgo}): `) + chalk.yellowBright(`${n.lastError} - ${date}`);
92
+ }
93
+ }
94
+ tables.push({ error: errorMsg, label, row1: [row1], row2: [row2] });
95
+ }
96
+ this.renderNarrow(tables);
97
+ process.stdout.write("\n * press any key to stop monitoring");
98
+ // eslint-disable-next-line no-await-in-loop
99
+ await sleep(1);
100
+ }
101
+ }
102
+ async monitorWide() {
103
+ const projectInfos = this.getProjectInfo();
50
104
  const values = {};
51
- const err = { date: '', layer: '', msg: '', timeAgo: '' };
105
+ // const err = { date: '', layer: '', msg:'', timeAgo: '' }
52
106
  while (true) {
53
107
  const rows = [];
54
- for (const p of projects) {
55
- if (!values[p.layer])
56
- values[p.layer] = {};
57
- const n = fs.existsSync(p.path) ? JSON.parse(fs.readFileSync(p.path, 'utf8')) : {};
58
- const projectName = p.project === 'hypergraph' ? '' : p.project;
59
- const network = p.network === 'integrationnet' ? 'intnet' : p.network;
108
+ // const projects: { error: string; label: string, row1: string[][], row2: string[][] }[] = [];
109
+ let errorMsg = '';
110
+ for (const info of projectInfos) {
111
+ if (!values[info.layer])
112
+ values[info.layer] = {};
113
+ const n = fs.existsSync(info.path) ? JSON.parse(fs.readFileSync(info.path, 'utf8')) : {};
114
+ const projectName = info.project === 'hypergraph' ? '' : ':' + info.project;
115
+ const network = info.network === 'integrationnet' ? 'intnet' : info.network;
60
116
  const distance = Number.isNaN(n.clusterOrdinal - n.ordinal) ? '-' : String(n.clusterOrdinal - n.ordinal);
61
- const ordinal = (values[p.layer].ordinal === n.ordinal) ? String(n.ordinal) : n.ordinal + ':true';
117
+ const ordinal = (values[info.layer].ordinal === n.ordinal) ? String(n.ordinal) : n.ordinal + ':true';
118
+ // const label = info.layer + ':' + network + projectName;
62
119
  rows.push([
63
120
  projectName + ':' + network,
64
- p.layer,
121
+ info.layer,
65
122
  n.session, // n.pilotSession,
66
123
  n.state,
67
124
  n.ordinal ? ordinal : '-',
@@ -71,30 +128,59 @@ export class StatusTable {
71
128
  n.memUsage || '-',
72
129
  n.error || '-'
73
130
  ]);
74
- values[p.layer].ordinal = n.ordinal;
75
- if ((p.layer === 'gl0' || p.layer === 'ml0') && n.lastError) {
76
- err.layer = p.layer.toUpperCase();
77
- err.msg = n.lastError;
78
- err.date = new Date(n.errorDate).toISOString();
79
- err.timeAgo = formatTimeAgo(Date.now() - n.errorDate) || '';
131
+ values[info.layer].ordinal = n.ordinal;
132
+ if ((info.layer === 'gl0' || info.layer === 'ml0') && n.lastError) {
133
+ const d = new Date(n.errorDate);
134
+ // if under 8 hours ago
135
+ if ((d.getTime() + (8 * 60 * 60 * 1000) > Date.now())) {
136
+ const date = new Date(n.errorDate).toISOString();
137
+ const timeAgo = formatTimeAgo(Date.now() - n.errorDate) || '';
138
+ errorMsg = chalk.green(` AUTO HEALED (${timeAgo}): `) + chalk.yellowBright(`${n.lastError} - ${date}`);
139
+ }
80
140
  }
141
+ // projects.push({ error: errorMsg, label, row1, row2 });
81
142
  }
82
- let hasError = err.msg !== '';
83
- if (hasError) {
84
- const d = new Date(err.date);
85
- // if under 8 hours ago
86
- hasError = (d.getTime() + (8 * 60 * 60 * 1000) > Date.now());
87
- }
88
- this.render(rows, hasError);
89
- if (hasError) {
90
- process.stdout.write(chalk.green(` AUTO HEALED (${err.timeAgo}): `) + chalk.red(`${err.layer}:${err.msg} - ${err.date}\n`));
91
- }
92
- process.stdout.write(" * press any key to cancel");
143
+ this.renderWide({ error: errorMsg, label: '', row1: rows, row2: [] });
144
+ process.stdout.write("\n * press any key to stop monitoring");
93
145
  // eslint-disable-next-line no-await-in-loop
94
146
  await sleep(1);
95
147
  }
96
148
  }
97
- render(rows, hasError = false) {
149
+ renderNarrow(project) {
150
+ const options = { terminalAdapter: true };
151
+ // hide cursor
152
+ console.log("\u001B[?25l");
153
+ // wipe existing if already rendered
154
+ if (this.alreadyRendered) {
155
+ // move cursor up number to the top of the previous print before deleting
156
+ console.log(`\u001B[${this.previousHeight}A`);
157
+ // delete to end of terminal
158
+ console.log("\u001B[0J");
159
+ }
160
+ else {
161
+ this.alreadyRendered = true;
162
+ }
163
+ this.previousHeight = 0;
164
+ for (const p of project) {
165
+ const t1 = ttyTable(glHeader1, p.row1, options);
166
+ const t2 = ttyTable(glHeader2, p.row2, options);
167
+ process.stdout.write(" " + chalk.cyanBright(chalk.bold(p.label)));
168
+ process.stdout.write(t1.render());
169
+ process.stdout.write(t2.render());
170
+ // reset the previous height to the height of this output
171
+ // for when we next clear the print
172
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
173
+ // @ts-expect-error
174
+ this.previousHeight += t1.height + t2.height + 1;
175
+ if (p.error) {
176
+ process.stdout.write("\n" + p.error + "\n");
177
+ this.previousHeight++;
178
+ this.previousHeight++;
179
+ }
180
+ console.log("");
181
+ }
182
+ }
183
+ renderWide(table) {
98
184
  const header = [...statusTableHeader];
99
185
  // const emptyColumns = Array.from({length: rows[0].length}).fill(0) as number[];
100
186
  // for (const [, row] of rows.entries()) {
@@ -112,13 +198,13 @@ export class StatusTable {
112
198
  // }
113
199
  // }
114
200
  const options = { terminalAdapter: true };
115
- const t1 = ttyTable(header, rows, options);
201
+ const t1 = ttyTable(header, table.row1, options);
116
202
  // hide cursor
117
203
  console.log("\u001B[?25l");
118
204
  // wipe existing if already rendered
119
205
  if (this.alreadyRendered) {
120
206
  // move cursor up number to the top of the previous print before deleting
121
- console.log(`\u001B[${this.previousHeight + (hasError ? 4 : 3)}A`);
207
+ console.log(`\u001B[${this.previousHeight + 4}A`);
122
208
  // delete to end of terminal
123
209
  console.log("\u001B[0J");
124
210
  }
@@ -131,6 +217,10 @@ export class StatusTable {
131
217
  // eslint-disable-next-line @typescript-eslint/ban-ts-comment
132
218
  // @ts-expect-error
133
219
  this.previousHeight = t1.height;
220
+ if (table.error) {
221
+ console.log(table.error);
222
+ this.previousHeight++;
223
+ }
134
224
  }
135
225
  }
136
226
  function sleep(sec) {
@@ -6,7 +6,7 @@
6
6
  "layer": {
7
7
  "description": "network layer to clean. e.g. gl0",
8
8
  "name": "layer",
9
- "required": true
9
+ "required": false
10
10
  }
11
11
  },
12
12
  "description": "Remove data and/or logs from a validator node",
@@ -14,23 +14,23 @@
14
14
  "<%= config.bin %> <%= command.id %>"
15
15
  ],
16
16
  "flags": {
17
- "all": {
18
- "char": "a",
19
- "description": "remove all data and logs",
20
- "name": "all",
21
- "allowNo": false,
22
- "type": "boolean"
23
- },
24
17
  "data": {
25
18
  "char": "d",
26
- "description": "remove data",
19
+ "description": "remove only data",
27
20
  "name": "data",
28
21
  "allowNo": false,
29
22
  "type": "boolean"
30
23
  },
24
+ "jars": {
25
+ "char": "j",
26
+ "description": "remove only jars",
27
+ "name": "jars",
28
+ "allowNo": false,
29
+ "type": "boolean"
30
+ },
31
31
  "logs": {
32
32
  "char": "l",
33
- "description": "remove logs",
33
+ "description": "remove only logs",
34
34
  "name": "logs",
35
35
  "allowNo": false,
36
36
  "type": "boolean"
@@ -111,6 +111,19 @@
111
111
  "<%= config.bin %> <%= command.id %>"
112
112
  ],
113
113
  "flags": {
114
+ "area": {
115
+ "char": "a",
116
+ "description": "area to view logs for. e.g. \"app\" or \"hc\" for health-check",
117
+ "name": "area",
118
+ "default": "hc",
119
+ "hasDynamicHelp": false,
120
+ "multiple": false,
121
+ "options": [
122
+ "app",
123
+ "hc"
124
+ ],
125
+ "type": "option"
126
+ },
114
127
  "follow": {
115
128
  "char": "f",
116
129
  "description": "continuously wait for additional data to be appended",
@@ -336,5 +349,5 @@
336
349
  ]
337
350
  }
338
351
  },
339
- "version": "0.1.0"
352
+ "version": "0.2.1"
340
353
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@constellation-network/node-pilot",
3
3
  "description": "An easy deployment and monitoring tool for Constellation nodes.",
4
- "version": "0.1.0",
4
+ "version": "0.2.1",
5
5
  "author": "Frank Fox",
6
6
  "bin": {
7
7
  "cpilot": "bin/run.js"
@@ -39,7 +39,7 @@ COPY dist/gl0.jar /app/jars/gl0.jar
39
39
  #COPY ./health-check /health-check
40
40
  #RUN chmod +x /health-check/bin/run.sh
41
41
  #RUN chmod +x /health-check/bin/hydrate.sh
42
- RUN npm install -g "@constellation-network/node-pilot-health-check@0.0.15"
42
+ RUN npm install -g "@constellation-network/node-pilot-health-check@0.0.16"
43
43
 
44
44
  # Add entrypoint
45
45
  COPY ./entrypoint.sh /app/entrypoint.sh
@@ -20,8 +20,8 @@ services:
20
20
  volumes:
21
21
  - ./gl0/data:/app/data
22
22
  - ./gl0/logs:/app/logs
23
+ - ./seedlist:/app/seedlist
23
24
  - ./key.p12:/app/key.p12:ro
24
- - ./seedlist:/app/seedlist:ro
25
25
  healthcheck:
26
26
  test: ["CMD", "cpilotHC"]
27
27
  # test: ["CMD", "/health-check/bin/run.sh"]