@constellation-network/node-pilot 0.0.17 → 0.0.19

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.0.17 darwin-arm64 node-v24.8.0
24
+ @constellation-network/node-pilot/0.0.19 darwin-arm64 node-v24.8.0
25
25
  $ cpilot --help [COMMAND]
26
26
  USAGE
27
27
  $ cpilot COMMAND
@@ -37,6 +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
41
  * [`cpilot config`](#cpilot-config)
41
42
  * [`cpilot config get [NAME]`](#cpilot-config-get-name)
42
43
  * [`cpilot config set NAME VALUE`](#cpilot-config-set-name-value)
@@ -47,6 +48,31 @@ If no command is entered, node-pilot will automatically perform a series of chec
47
48
  * [`cpilot shutdown`](#cpilot-shutdown)
48
49
  * [`cpilot status`](#cpilot-status)
49
50
 
51
+ ## `cpilot clean LAYER`
52
+
53
+ Remove data and/or logs from a validator node
54
+
55
+ ```
56
+ USAGE
57
+ $ cpilot clean LAYER [-a] [-d] [-l]
58
+
59
+ ARGUMENTS
60
+ LAYER network layer to clean. e.g. gl0
61
+
62
+ FLAGS
63
+ -a, --all remove all data and logs
64
+ -d, --data remove data
65
+ -l, --logs remove logs
66
+
67
+ DESCRIPTION
68
+ Remove data and/or logs from a validator node
69
+
70
+ EXAMPLES
71
+ $ cpilot clean
72
+ ```
73
+
74
+ _See code: [src/commands/clean.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.19/src/commands/clean.ts)_
75
+
50
76
  ## `cpilot config`
51
77
 
52
78
  Update configuration settings
@@ -62,7 +88,7 @@ EXAMPLES
62
88
  $ cpilot config
63
89
  ```
64
90
 
65
- _See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.17/src/commands/config.ts)_
91
+ _See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.19/src/commands/config.ts)_
66
92
 
67
93
  ## `cpilot config get [NAME]`
68
94
 
@@ -86,7 +112,7 @@ EXAMPLES
86
112
  $ cpilot config get gl0:CL_PUBLIC_HTTP_PORT
87
113
  ```
88
114
 
89
- _See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.17/src/commands/config/get.ts)_
115
+ _See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.19/src/commands/config/get.ts)_
90
116
 
91
117
  ## `cpilot config set NAME VALUE`
92
118
 
@@ -109,7 +135,7 @@ EXAMPLES
109
135
  $ cpilot config set gl0:CL_PUBLIC_HTTP_PORT 9000
110
136
  ```
111
137
 
112
- _See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.17/src/commands/config/set.ts)_
138
+ _See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.19/src/commands/config/set.ts)_
113
139
 
114
140
  ## `cpilot help [COMMAND]`
115
141
 
@@ -146,7 +172,7 @@ EXAMPLES
146
172
  $ cpilot info
147
173
  ```
148
174
 
149
- _See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.17/src/commands/info.ts)_
175
+ _See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.19/src/commands/info.ts)_
150
176
 
151
177
  ## `cpilot logs LAYER`
152
178
 
@@ -170,7 +196,7 @@ EXAMPLES
170
196
  $ cpilot logs
171
197
  ```
172
198
 
173
- _See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.17/src/commands/logs.ts)_
199
+ _See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.19/src/commands/logs.ts)_
174
200
 
175
201
  ## `cpilot restart [LAYER]`
176
202
 
@@ -198,7 +224,7 @@ EXAMPLES
198
224
  $ cpilot restart
199
225
  ```
200
226
 
201
- _See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.17/src/commands/restart.ts)_
227
+ _See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.19/src/commands/restart.ts)_
202
228
 
203
229
  ## `cpilot shutdown`
204
230
 
@@ -206,7 +232,10 @@ A full shutdown of the validator node
206
232
 
207
233
  ```
208
234
  USAGE
209
- $ cpilot shutdown
235
+ $ cpilot shutdown [-l <value>]
236
+
237
+ FLAGS
238
+ -l, --layer=<value> specify a layer to shutdown. e.g. gl0
210
239
 
211
240
  DESCRIPTION
212
241
  A full shutdown of the validator node
@@ -215,7 +244,7 @@ EXAMPLES
215
244
  $ cpilot shutdown
216
245
  ```
217
246
 
218
- _See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.17/src/commands/shutdown.ts)_
247
+ _See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.19/src/commands/shutdown.ts)_
219
248
 
220
249
  ## `cpilot status`
221
250
 
@@ -229,5 +258,5 @@ DESCRIPTION
229
258
  Display node status and configuration settings
230
259
  ```
231
260
 
232
- _See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.17/src/commands/status.ts)_
261
+ _See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.19/src/commands/status.ts)_
233
262
  <!-- commandsstop -->
@@ -1,4 +1,5 @@
1
1
  import fs from "node:fs";
2
+ import os from "node:os";
2
3
  import path from "node:path";
3
4
  import { fileURLToPath } from "node:url";
4
5
  import shell from "shelljs";
@@ -8,37 +9,42 @@ import { shellService } from "../services/shell-service.js";
8
9
  export const checkDependencies = async () => {
9
10
  const dockerComposeVersion = await shellService.runCommand('docker compose version').catch(() => '');
10
11
  clm.debug(`Docker compose version check: ${dockerComposeVersion || 'NOT_INSTALLED'}`);
11
- if (dockerComposeVersion) {
12
- return;
13
- }
14
- const isDockerV1Installed = await shellService.checkCommandAvailable('docker-compose');
15
- if (isDockerV1Installed) {
16
- clm.warn('You are using docker-compose v1. It needs to be uninstalled before upgrading to the latest version.');
17
- // await promptHelper.doYouWishToContinue();
18
- clm.step('\nRun the following command to uninstall docker-compose v1 and then run cpilot again:');
19
- clm.preStep('for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done');
20
- clm.echo('');
21
- process.exit(0);
12
+ if (!dockerComposeVersion) {
13
+ const isDockerV1Installed = await shellService.checkCommandAvailable('docker-compose');
14
+ if (isDockerV1Installed) {
15
+ clm.warn('You are using docker-compose v1. It needs to be uninstalled before upgrading to the latest version.');
16
+ // await promptHelper.doYouWishToContinue();
17
+ clm.step('\nRun the following command to uninstall docker-compose v1 and then run cpilot again:');
18
+ clm.preStep('for pkg in docker.io docker-doc docker-compose docker-compose-v2 podman-docker containerd runc; do sudo apt-get remove $pkg; done');
19
+ clm.echo('');
20
+ process.exit(0);
21
+ }
22
22
  }
23
23
  const isDockerInstalled = await shellService.checkCommandAvailable('docker');
24
- if (!isDockerInstalled) {
25
- const pilotDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../..`);
26
- if (fs.existsSync(path.join(pilotDir, 'install-dependencies.sh'))) {
27
- // Skip prompt if already informed user of the upgrade
28
- if (!isDockerV1Installed) {
29
- clm.step('Docker is required and needs to be installed.');
30
- await promptHelper.doYouWishToContinue();
31
- }
32
- clm.debug(`Running install-dependencies.sh from ${pilotDir}`);
33
- // const silent = !process.env.DEBUG;
34
- const result = shell.exec('bash install-dependencies.sh', { cwd: pilotDir });
35
- if (result.code > 0) {
36
- console.log(result.stderr);
37
- clm.error(`Failed to install dependencies. Please try again after resolving any errors.`);
38
- }
24
+ const pilotDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../..`);
25
+ if (fs.existsSync(path.join(pilotDir, 'install-dependencies.sh'))) {
26
+ if (!isDockerInstalled) {
27
+ clm.step('Docker is required and needs to be installed.');
28
+ await promptHelper.doYouWishToContinue();
29
+ await installDependencies(pilotDir);
39
30
  clm.postStep(`\nDocker has been installed. Please logout and login again to ensure the changes take effect. Then run cpilot again.`);
40
31
  clm.echo('');
41
32
  process.exit(0);
42
33
  }
34
+ const isACLInstalled = await shellService.checkCommandAvailable('setfacl');
35
+ if (!isACLInstalled && os.platform() === 'linux') {
36
+ clm.step('ACL is required and needs to be installed.');
37
+ await promptHelper.doYouWishToContinue();
38
+ await installDependencies(pilotDir);
39
+ }
43
40
  }
44
41
  };
42
+ async function installDependencies(pilotDir) {
43
+ clm.debug(`Running install-dependencies.sh from ${pilotDir}`);
44
+ // const silent = !process.env.DEBUG;
45
+ const result = shell.exec('bash install-dependencies.sh', { cwd: pilotDir });
46
+ if (result.code > 0) {
47
+ console.log(result.stderr);
48
+ clm.error(`Failed to install dependencies. Please try again after resolving any errors.`);
49
+ }
50
+ }
@@ -34,7 +34,7 @@ export const checkLayers = {
34
34
  await nodeService.pollForLayersState(layersToRun);
35
35
  }
36
36
  else {
37
- clm.postStep('Node not started.');
37
+ clm.postStep('Validator Node not started.');
38
38
  }
39
39
  });
40
40
  }
@@ -48,7 +48,7 @@ export const checkLayers = {
48
48
  await nodeService.pollForLayersState(layersNotRunning);
49
49
  }
50
50
  else {
51
- clm.echo('Node not started.');
51
+ clm.echo('Validator Node not started.');
52
52
  }
53
53
  });
54
54
  }
@@ -29,7 +29,7 @@ export const checkNetwork = {
29
29
  clm.error(`Or to change the node ID, configure the Key File: use ${chalk.cyan('cpilot config')}, and select ${chalk.cyan('Key File')}`);
30
30
  }
31
31
  configStore.setProjectFlag('duplicateNodeIdChecked', true);
32
- clm.postStep('No duplicate Node found.');
32
+ clm.postStep('No duplicate Node found.');
33
33
  },
34
34
  async checkSeedList() {
35
35
  if (configStore.hasProjectFlag('seedListChecked')) {
@@ -42,7 +42,7 @@ export const checkNetwork = {
42
42
  if (fs.existsSync(seedListFile)) {
43
43
  const found = fs.readFileSync(seedListFile, 'utf8').includes(nodeId);
44
44
  if (found) {
45
- clm.postStep(`Node ID found in ${type.toUpperCase()} seed list.`);
45
+ clm.postStep(`✅ Node ID found in ${type.toUpperCase()} seed list.`);
46
46
  configStore.setProjectFlag('seedListChecked', true);
47
47
  return;
48
48
  }
@@ -0,0 +1,5 @@
1
+ export declare const checkNodePilot: {
2
+ checkDiscordRegistration(): Promise<void>;
3
+ checkVersion(): Promise<void>;
4
+ promptDiscordRegistration(): Promise<void>;
5
+ };
@@ -0,0 +1,72 @@
1
+ import { input } from "@inquirer/prompts";
2
+ import { JSONStorage } from "node-localstorage";
3
+ import fs from "node:fs";
4
+ import path from "node:path";
5
+ import packageJson from '../../package.json' with { type: 'json' };
6
+ import { clm } from "../clm.js";
7
+ import { configStore } from "../config-store.js";
8
+ import { projectHelper } from "../helpers/project-helper.js";
9
+ import { promptHelper } from "../helpers/prompt-helper.js";
10
+ import { shellService } from "../services/shell-service.js";
11
+ const REGISTRY_URL = 'https://registry.npmjs.org/';
12
+ export const checkNodePilot = {
13
+ async checkDiscordRegistration() {
14
+ if (configStore.hasProjectFlag('discordChecked')) {
15
+ return;
16
+ }
17
+ await this.promptDiscordRegistration();
18
+ configStore.setProjectFlag('discordChecked', true);
19
+ },
20
+ async checkVersion() {
21
+ const packageUrl = new URL(encodeURIComponent(packageJson.name).replace(/^%40/, '@'), REGISTRY_URL);
22
+ const headers = {
23
+ accept: 'application/vnd.npm.install-v1+json; q=1.0, application/json; q=0.8, */*',
24
+ };
25
+ const result = await fetch(packageUrl.toString(), { headers }).then(res => res.json()).catch(() => null);
26
+ if (!result) {
27
+ return;
28
+ }
29
+ const latestVersion = result['dist-tags'].latest;
30
+ if (packageJson.version !== latestVersion) {
31
+ console.log('There is a new node-pilot version available.');
32
+ if (await promptHelper.confirmPrompt('Do you wish to upgrade now?')) {
33
+ await shellService.runCommand('npm install -g @constellationnetwork/node-pilot@latest');
34
+ await projectHelper.upgradeHypergraph();
35
+ clm.postStep('Run cpilot again to use the latest version');
36
+ process.exit(0);
37
+ }
38
+ }
39
+ },
40
+ async promptDiscordRegistration() {
41
+ const hcStorage = getHcStorage();
42
+ const join = await promptHelper.confirmPrompt('Do you want to enable Discord notifications for your node?:');
43
+ if (!join) {
44
+ hcStorage.setItem('user', { webHookEnabled: false });
45
+ clm.postStep('Discord notifications are disabled.');
46
+ return;
47
+ }
48
+ let answer = await input({
49
+ default: '',
50
+ message: `Provide your Discord username to have it included in the notification: `
51
+ });
52
+ answer = answer.trim();
53
+ if (answer === '') {
54
+ hcStorage.setItem('user', { webHookEnabled: true });
55
+ }
56
+ else {
57
+ if (answer.charAt(0) === '@')
58
+ answer = answer.slice(1);
59
+ hcStorage.setItem('user', { discordUser: answer.trim(), webHookEnabled: true });
60
+ }
61
+ clm.postStep('Discord notifications are enabled.');
62
+ }
63
+ };
64
+ function getHcStorage() {
65
+ const { layersToRun, projectDir } = configStore.getProjectInfo();
66
+ const layer = layersToRun[0];
67
+ const hcPath = path.join(projectDir, layer, 'data', 'health-check');
68
+ if (fs.existsSync(hcPath)) {
69
+ fs.mkdirSync(hcPath, { recursive: true });
70
+ }
71
+ return new JSONStorage(hcPath);
72
+ }
@@ -103,7 +103,8 @@ export const checkProject = {
103
103
  // spinner.color = 'green';
104
104
  // }
105
105
  // else {
106
- clm.preStep('Running install script...');
106
+ clm.preStep('Tessellation and dependencies need to be installed. This may take a few minutes...');
107
+ await promptHelper.doYouWishToContinue();
107
108
  // }
108
109
  // const node = await clusterService.getClusterNodeInfo();
109
110
  // const NODE_URL = `http://${node.host}:${node.publicPort}`;
@@ -30,7 +30,7 @@ export const checkWallet = {
30
30
  await promptHelper.doYouWishToContinue();
31
31
  }
32
32
  else {
33
- clm.postStep('Collateral check PASSED');
33
+ clm.postStep('Collateral check PASSED');
34
34
  }
35
35
  configStore.setProjectFlag('skipCollateralCheck', true);
36
36
  }
@@ -0,0 +1,14 @@
1
+ import { Command } from '@oclif/core';
2
+ export default class Clean extends Command {
3
+ static args: {
4
+ layer: import("@oclif/core/interfaces").Arg<string, Record<string, unknown>>;
5
+ };
6
+ static description: string;
7
+ static examples: string[];
8
+ static flags: {
9
+ all: import("@oclif/core/interfaces").BooleanFlag<boolean>;
10
+ data: import("@oclif/core/interfaces").BooleanFlag<boolean>;
11
+ logs: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
+ };
13
+ run(): Promise<void>;
14
+ }
@@ -0,0 +1,45 @@
1
+ import { Args, Command, Flags } from '@oclif/core';
2
+ import { clm } from "../clm.js";
3
+ import { configStore } from "../config-store.js";
4
+ import { projectHelper } from "../helpers/project-helper.js";
5
+ import { promptHelper } from "../helpers/prompt-helper.js";
6
+ import { dockerService } from "../services/docker-service.js";
7
+ import { shellService } from "../services/shell-service.js";
8
+ export default class Clean extends Command {
9
+ static args = {
10
+ layer: Args.string({ description: 'network layer to clean. e.g. gl0', required: true }),
11
+ };
12
+ static description = 'Remove data and/or logs from a validator node';
13
+ static examples = [
14
+ '<%= config.bin %> <%= command.id %>',
15
+ ];
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' })
20
+ };
21
+ async run() {
22
+ const { args, flags } = await this.parse(Clean);
23
+ const { layersToRun } = configStore.getProjectInfo();
24
+ if (!layersToRun.includes(args.layer)) {
25
+ this.error(`Invalid layer: ${args.layer}. Available layers: ${layersToRun.join(',')}`);
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
+ }
32
+ if (await dockerService.isRunning()) {
33
+ clm.preStep('The validator node must be stopped first.');
34
+ await promptHelper.doYouWishToContinue();
35
+ await dockerService.dockerDown();
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`);
43
+ }
44
+ }
45
+ }
@@ -7,6 +7,7 @@ import { configHelper } from "../helpers/config-helper.js";
7
7
  import { keyFileHelper } from "../helpers/key-file-helper.js";
8
8
  import { promptHelper } from "../helpers/prompt-helper.js";
9
9
  import { dockerService } from "../services/docker-service.js";
10
+ import { checkNodePilot } from "../checks/check-pilot.js";
10
11
  export default class Config extends Command {
11
12
  static description = 'Update configuration settings';
12
13
  static examples = [
@@ -19,6 +20,7 @@ export default class Config extends Command {
19
20
  const answer = await select({
20
21
  choices: [
21
22
  { name: 'External IP Address', value: 'externalIp' },
23
+ { name: `Discord Alerts`, value: 'discordAlerts' },
22
24
  { name: 'Java Memory', value: 'javaMemory' },
23
25
  { name: 'Key File', value: 'keyFile' },
24
26
  { name: 'Layers To Run', value: 'layersToRun' },
@@ -30,6 +32,9 @@ export default class Config extends Command {
30
32
  if (answer === 'externalIp') {
31
33
  await checkNetwork.configureIpAddress();
32
34
  }
35
+ else if (answer === 'discordAlerts') {
36
+ await checkNodePilot.promptDiscordRegistration();
37
+ }
33
38
  else if (answer === 'javaMemory') {
34
39
  await shutdownNodeIfRunning();
35
40
  await promptHelper.configureJavaMemoryArguments();
@@ -36,6 +36,9 @@ export default class Restart extends BaseCommand {
36
36
  // eslint-disable-next-line no-await-in-loop
37
37
  await this.restart();
38
38
  }
39
+ else {
40
+ serviceLog.log(' ' + project + ' version is the same. ');
41
+ }
39
42
  }
40
43
  configStore.setActiveProject(project);
41
44
  return;
@@ -46,7 +49,7 @@ export default class Restart extends BaseCommand {
46
49
  const activeProjects = configStore.getRunningProjects();
47
50
  // serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
48
51
  for (const project of activeProjects) {
49
- serviceLog.log(' ' + project + ' is restarting...');
52
+ serviceLog.log(' ' + project + ' is being auto started...');
50
53
  configStore.setActiveProject(project);
51
54
  // eslint-disable-next-line no-await-in-loop
52
55
  await this.restart();
@@ -75,8 +78,10 @@ export default class Restart extends BaseCommand {
75
78
  // const pAll = layersToRun.map(l => nodeService.getNodeInfo(l));
76
79
  // const info = await Promise.all(pAll);
77
80
  // const isRunning = info.some(n => n.state !== 'Unavailable');
78
- if (configStore.isRestarting())
81
+ if (configStore.isRestarting()) {
82
+ serviceLog.log('Restart already ACTIVE');
79
83
  return;
84
+ }
80
85
  configStore.setIsRestarting(true);
81
86
  try {
82
87
  if (await dockerService.isRunning()) {
@@ -89,7 +94,7 @@ export default class Restart extends BaseCommand {
89
94
  clm.preStep('Checking for a new version...');
90
95
  await checkProject.runUpgrade();
91
96
  clm.preStep('Starting the node...');
92
- await dockerService.dockerUp();
97
+ await dockerService.dockerRestartAll();
93
98
  }
94
99
  finally {
95
100
  configStore.setIsRestarting(false);
@@ -2,5 +2,8 @@ import { Command } from '@oclif/core';
2
2
  export default class Shutdown extends Command {
3
3
  static description: string;
4
4
  static examples: string[];
5
+ static flags: {
6
+ layer: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
7
+ };
5
8
  run(): Promise<void>;
6
9
  }
@@ -1,4 +1,4 @@
1
- import { Command } from '@oclif/core';
1
+ import { Command, Flags } from '@oclif/core';
2
2
  import { configStore } from "../config-store.js";
3
3
  import { configHelper } from "../helpers/config-helper.js";
4
4
  import { dockerService } from "../services/docker-service.js";
@@ -8,11 +8,26 @@ export default class Shutdown extends Command {
8
8
  static examples = [
9
9
  '<%= config.bin %> <%= command.id %>',
10
10
  ];
11
+ static flags = {
12
+ layer: Flags.string({ char: 'l', description: 'specify a layer to shutdown. e.g. gl0' })
13
+ };
11
14
  async run() {
12
15
  configHelper.assertProject('No project found. ');
16
+ const { flags } = await this.parse(Shutdown);
13
17
  const { layersToRun } = configStore.getProjectInfo();
14
- await nodeService.leaveClusterAllLayers();
15
- await nodeService.pollForLayersState(layersToRun, 'Offline');
16
- await dockerService.dockerDown();
18
+ const layer = flags.layer;
19
+ if (layer) {
20
+ if (!layersToRun.includes(layer)) {
21
+ this.error(`Invalid layer: ${layer}. Available layers: ${layersToRun.join(',')}`);
22
+ }
23
+ await nodeService.leaveCluster(layer);
24
+ await nodeService.pollForLayersState([], 'Offline');
25
+ await dockerService.dockerDown([layer]);
26
+ }
27
+ else {
28
+ await nodeService.leaveClusterAllLayers();
29
+ await nodeService.pollForLayersState(layersToRun, 'Offline');
30
+ await dockerService.dockerDown();
31
+ }
17
32
  }
18
33
  }
@@ -3,6 +3,7 @@ import { checkInitialSetup } from "../checks/check-initial-setup.js";
3
3
  import { checkLayers } from "../checks/check-layers.js";
4
4
  import { checkNetwork } from "../checks/check-network.js";
5
5
  import { checkNodeCtl } from "../checks/check-node-ctl.js";
6
+ import { checkNodePilot } from "../checks/check-pilot.js";
6
7
  import { checkProject } from "../checks/check-project.js";
7
8
  import { checkWallet } from "../checks/check-wallet.js";
8
9
  import { keyFileHelper } from "../helpers/key-file-helper.js";
@@ -19,6 +20,8 @@ export async function checkInstallationAndConfigurationStatus() {
19
20
  await checkProject.projectInstallation();
20
21
  await checkNetwork.checkExternalIpAddress();
21
22
  await checkNetwork.isNetworkConnectable();
23
+ await checkNodePilot.checkDiscordRegistration();
24
+ await checkNodePilot.checkVersion();
22
25
  await checkProject.releaseVersion();
23
26
  await checkNodeCtl.check4Migration();
24
27
  await keyFileHelper.promptIfNoKeyFile();
@@ -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 { StatusTable } from "../helpers/status-table.js";
7
+ import { checkNodePilot } from "../checks/check-pilot.js";
8
8
  export default class Test extends Command {
9
9
  static description = 'node pilot test command';
10
10
  static hidden = true;
@@ -18,7 +18,8 @@ export default class Test extends Command {
18
18
  //
19
19
  // await archiverService.syncToLatestSnapshot();
20
20
  // await archiverService.checkLogsForMissingSnapshots();
21
- await StatusTable.run();
21
+ // await StatusTable.run();
22
+ await checkNodePilot.checkVersion();
22
23
  }
23
24
  async testRandomNode() {
24
25
  const filePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../../projects/hypergraph/networks/mainnet/source-nodes.env`);
@@ -60,40 +60,72 @@ export const keyFileHelper = {
60
60
  return shellService.runCommandWithOutput(`java -jar ${projectDir}/dist/wallet.jar show-id`, env);
61
61
  },
62
62
  async importKeyFile() {
63
- const userKeyPath = await input({
64
- message: 'Enter the path to your key file:',
65
- validate(value) {
66
- const fullPath = shellService.resolvePath(value.trim());
67
- if (!fs.existsSync(fullPath)) {
68
- return `${fullPath} does not exist. Please provide a valid path.`;
63
+ const p12Files = fs.readdirSync(os.homedir())
64
+ .filter(file => file.endsWith('.p12'))
65
+ .map(file => ({ name: 'Import ~/' + file, value: file }));
66
+ let userKeyPath = '';
67
+ let answer = 'importAnother';
68
+ if (p12Files.length > 0) {
69
+ const choices = [
70
+ ...p12Files,
71
+ { name: 'Import a different key file', value: 'importAnother' }
72
+ ];
73
+ answer = await select({ choices, message: 'Choose an option:' });
74
+ userKeyPath = path.join(os.homedir(), answer);
75
+ }
76
+ if (answer === 'importAnother') {
77
+ userKeyPath = await input({
78
+ message: 'Enter the path to your key file:',
79
+ validate(value) {
80
+ const fullPath = shellService.resolvePath(value.trim());
81
+ if (!fs.existsSync(fullPath)) {
82
+ return `${fullPath} does not exist. Please provide a valid path.`;
83
+ }
84
+ return true;
69
85
  }
70
- return true;
71
- }
72
- });
86
+ });
87
+ }
73
88
  const { projectDir } = configStore.getProjectInfo();
74
89
  const fullPath = shellService.resolvePath(userKeyPath.trim());
75
90
  const keyStorePath = path.join(projectDir, "key.p12");
91
+ const tempKeyPath = path.join(projectDir, "temp.p12");
92
+ const envInfo = configStore.getEnvInfo();
93
+ // prompt for password
94
+ const keyPassword = await password({ message: 'Enter the key file password:' });
95
+ const keyAlias = await input({ message: 'Enter the key file alias:' });
96
+ configStore.setEnvInfo({ CL_KEYALIAS: keyAlias, CL_KEYSTORE: keyStorePath, CL_PASSWORD: keyPassword });
76
97
  try {
98
+ if (fs.existsSync(keyStorePath))
99
+ fs.cpSync(keyStorePath, tempKeyPath, { force: true });
77
100
  fs.copyFileSync(fullPath, keyStorePath);
78
101
  }
79
102
  catch (error) {
80
103
  clm.error('Failed to import key file:' + error);
81
104
  }
82
- // prompt for password
83
- const keyPassword = await password({ message: 'Enter the key file password:' });
84
- const keyAlias = await input({ message: 'Enter the key file alias:' });
85
- configStore.setEnvInfo({ CL_KEYALIAS: keyAlias, CL_KEYSTORE: keyStorePath, CL_PASSWORD: keyPassword });
86
105
  try {
87
106
  const dagAddress = await this.getAddress();
88
107
  const nodeId = await this.getId();
89
108
  configStore.setProjectInfo({ dagAddress, nodeId });
90
109
  }
91
110
  catch {
92
- fs.rmSync(keyStorePath);
111
+ if (fs.existsSync(tempKeyPath)) {
112
+ // Revert back to original key file
113
+ fs.cpSync(tempKeyPath, keyStorePath, { force: true });
114
+ configStore.setEnvInfo({ CL_KEYALIAS: envInfo.CL_KEYALIAS, CL_KEYSTORE: envInfo.CL_KEYSTORE, CL_PASSWORD: envInfo.CL_PASSWORD });
115
+ }
116
+ else {
117
+ fs.rmSync(keyStorePath);
118
+ }
93
119
  clm.error('Failed to unlock the key file. Please check your key file information and try again.');
94
120
  // await this.promptForKeyFile();
95
121
  // return;
96
122
  }
123
+ try {
124
+ fs.copyFileSync(fullPath, keyStorePath);
125
+ }
126
+ catch (error) {
127
+ clm.error('Failed to import key file:' + error);
128
+ }
97
129
  clm.postStep('Key file imported successfully.\n');
98
130
  },
99
131
  async promptForKeyFile() {
@@ -6,5 +6,9 @@ export declare const projectHelper: {
6
6
  installFromGithub(_repo: string): Promise<never>;
7
7
  installHypergraph(): Promise<void>;
8
8
  installProject(name: string, projectFolder: string): Promise<void>;
9
+ prepareDataFolder(): void;
9
10
  selectProject(): Promise<void>;
11
+ upgradeEmbedded(name: string): Promise<void>;
12
+ upgradeHypergraph(): Promise<void>;
13
+ upgradeProject(name: string, projectFolder: string): Promise<void>;
10
14
  };