@constellation-network/node-pilot 0.0.17 → 0.0.18

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.18 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.18/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.18/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.18/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.18/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.18/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.18/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.18/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.18/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.18/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
+ }
@@ -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
  }
@@ -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
+ }
@@ -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
  }
@@ -6,5 +6,6 @@ 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>;
10
11
  };
@@ -65,11 +65,7 @@ export const projectHelper = {
65
65
  await this.installEmbedded('hypergraph');
66
66
  const { projectDir } = configStore.getProjectInfo();
67
67
  const { platform } = configStore.getSystemInfo();
68
- // Create gl0 folder for the fast-forward feature before Docker does
69
- const gl0DataDir = path.join(projectDir, 'gl0', 'data');
70
- fs.mkdirSync(path.join(gl0DataDir, 'incremental_snapshot'), { recursive: true });
71
- fs.mkdirSync(path.join(gl0DataDir, 'snapshot_info'));
72
- fs.mkdirSync(path.join(gl0DataDir, 'tmp'));
68
+ this.prepareDataFolder();
73
69
  if (platform === 'linux') {
74
70
  const layerDir = path.join(projectDir, 'gl0');
75
71
  // set permission for group "docker" on the layer folder and any subfolders created later
@@ -91,6 +87,16 @@ export const projectHelper = {
91
87
  clm.debug(`Installing project from ${projectFolder} to ${projectDir}`);
92
88
  fs.cpSync(projectFolder, projectDir, { recursive: true });
93
89
  },
90
+ prepareDataFolder() {
91
+ const { projectDir } = configStore.getProjectInfo();
92
+ // Create gl0 folder for the fast-forward feature before Docker does
93
+ const gl0DataDir = path.join(projectDir, 'gl0', 'data');
94
+ if (!fs.existsSync(gl0DataDir)) {
95
+ fs.mkdirSync(path.join(gl0DataDir, 'incremental_snapshot'), { recursive: true });
96
+ fs.mkdirSync(path.join(gl0DataDir, 'snapshot_info'));
97
+ fs.mkdirSync(path.join(gl0DataDir, 'tmp'));
98
+ }
99
+ },
94
100
  async selectProject() {
95
101
  // prompt user to install hypergraph or metagraph
96
102
  const networkType = await select({
@@ -1,15 +1,18 @@
1
+ import os from "node:os";
1
2
  class CellFormatter {
2
3
  formatCpu(value) {
3
4
  if (value === '-')
4
5
  return value;
5
- const num = Number.parseInt(value, 10);
6
+ const cores = os.cpus().length;
7
+ const num = Number.parseFloat(value) / cores;
8
+ value = num.toFixed(1) + '%';
6
9
  if (num === 0)
7
10
  return value;
8
- if (num < 60)
9
- return this.style(value.toString(), "green");
10
- if (num < 99)
11
- return this.style(value.toString(), "yellow", "bold");
12
- return this.style(value.toString(), "bgRed", "bold");
11
+ if (num < 40)
12
+ return this.style(value, "green");
13
+ if (num < 85)
14
+ return this.style(value, "yellow", "bold");
15
+ return this.style(value, "bgRed", "bold");
13
16
  }
14
17
  formatDistance(value) {
15
18
  if (value === '-')
@@ -29,12 +32,12 @@ class CellFormatter {
29
32
  formatMem(value) {
30
33
  if (value === '-')
31
34
  return value;
32
- const num = Number.parseInt(value.split('(')[1], 10);
35
+ const num = Number.parseInt(value, 10);
33
36
  if (num === 0)
34
37
  return value;
35
- if (num < 70)
38
+ if (num < 86)
36
39
  return this.style(value.toString(), "green");
37
- if (num < 90)
40
+ if (num < 99)
38
41
  return this.style(value.toString(), "yellow", "bold");
39
42
  return this.style(value.toString(), "red", "bold");
40
43
  }
@@ -57,6 +60,8 @@ class CellFormatter {
57
60
  return this.style(value, "white");
58
61
  }
59
62
  formatUpTIme(startTime) {
63
+ if (!startTime)
64
+ return '-';
60
65
  const formattedTime = formatTime(Date.now() - Number(startTime), true);
61
66
  return formattedTime ?? '-';
62
67
  }
@@ -68,6 +68,9 @@ export const clusterService = {
68
68
  const { CL_L0_PEER_HTTP_HOST } = configStore.getEnvNetworkInfo(type);
69
69
  clm.debug(`http://${CL_L0_PEER_HTTP_HOST}:${CL_PUBLIC_HTTP_PORT}/${path}`);
70
70
  return fetch(`http://${CL_L0_PEER_HTTP_HOST}:${CL_PUBLIC_HTTP_PORT}/${path}`)
71
- .then(res => res.json());
71
+ .then(res => res.json())
72
+ .catch(() => {
73
+ throw new Error(`Unable to connect to source node at http://${CL_L0_PEER_HTTP_HOST}:${CL_PUBLIC_HTTP_PORT}/${path}`);
74
+ });
72
75
  }
73
76
  };
@@ -1,8 +1,9 @@
1
1
  import { TessellationLayer } from "../types.js";
2
2
  export declare const dockerService: {
3
3
  dockerBuild(): Promise<void>;
4
- dockerDown(): Promise<void>;
4
+ dockerDown(layers?: TessellationLayer[]): Promise<void>;
5
5
  dockerRestart(layer: TessellationLayer): Promise<void>;
6
+ dockerRestartAll(): Promise<void>;
6
7
  dockerStartLayers(layers: TessellationLayer[]): Promise<void>;
7
8
  dockerUp(): Promise<void>;
8
9
  isRunning(): Promise<boolean>;
@@ -19,16 +19,23 @@ export const dockerService = {
19
19
  if (silent) {
20
20
  spinner.stop();
21
21
  }
22
- clm.postStep('Node container built.');
22
+ clm.postStep('Node container built.');
23
23
  }
24
24
  },
25
- async dockerDown() {
26
- await run('down');
25
+ async dockerDown(layers) {
26
+ await run('down', layers);
27
27
  configStore.setProjectStatusToRunning(false);
28
28
  },
29
29
  async dockerRestart(layer) {
30
30
  await run('restart', [layer]);
31
31
  },
32
+ async dockerRestartAll() {
33
+ if (await this.isRunning()) {
34
+ await this.dockerDown();
35
+ }
36
+ await run('up -d');
37
+ configStore.setProjectStatusToRunning(true);
38
+ },
32
39
  async dockerStartLayers(layers) {
33
40
  await run('up -d', layers);
34
41
  },
@@ -6,6 +6,7 @@ import path from "node:path";
6
6
  import { clm } from "../clm.js";
7
7
  import { configStore } from "../config-store.js";
8
8
  import { promptHelper } from "../helpers/prompt-helper.js";
9
+ import { shellService } from "./shell-service.js";
9
10
  const CHUNK_SIZE = 20_000;
10
11
  export class FastforwardService {
11
12
  dataDir;
@@ -51,7 +52,7 @@ export class FastforwardService {
51
52
  clm.debug('Saving compressedSnapshotIncremental to: ' + this.tmpDir);
52
53
  fs.writeFileSync(path.join(this.tmpDir, ordinal.toString() + '.c'), compressedSnapshotIncremental);
53
54
  await this.saveSnapshotFiles(ordinal.toString(), hash);
54
- clm.postStep(`Fastforward to snapshot "${ordinal}" completed.`);
55
+ clm.postStep(`✅ Fastforward to snapshot ${chalk.bold(ordinal)} completed.`);
55
56
  }
56
57
  async fetchLatestSnapshot() {
57
58
  const url = `${this.lbUrl}/global-snapshots/latest/combined`;
@@ -92,8 +93,18 @@ export class FastforwardService {
92
93
  const incrementalSnapshotDir = path.join(this.dataDir, 'incremental_snapshot');
93
94
  const hashDir = path.join(incrementalSnapshotDir, 'hash', hashSubdir);
94
95
  const ordinalDir = path.join(incrementalSnapshotDir, 'ordinal', ordinalRounded.toString());
95
- fs.mkdirSync(hashDir, { recursive: true });
96
- fs.mkdirSync(ordinalDir, { recursive: true });
96
+ try {
97
+ fs.mkdirSync(hashDir, { recursive: true });
98
+ }
99
+ catch {
100
+ await shellService.runCommand(`sudo mkdir -p ${hashDir}`);
101
+ }
102
+ try {
103
+ fs.mkdirSync(ordinalDir, { recursive: true });
104
+ }
105
+ catch {
106
+ await shellService.runCommand(`sudo mkdir -p ${ordinalDir}`);
107
+ }
97
108
  const destOrdinalFile = path.join(ordinalDir, ordinal);
98
109
  if (fs.existsSync(destOrdinalFile)) {
99
110
  clm.warn(`Snapshot ${destOrdinalFile} already exists. Skipping...`);
@@ -29,53 +29,6 @@ check_acl() {
29
29
  fi
30
30
  }
31
31
 
32
- # Check and install curl
33
- check_curl() {
34
- # echo "Checking for curl..."
35
- if command -v curl >/dev/null 2>&1; then
36
- echo "✅ curl is already installed."
37
- return 0
38
- fi
39
-
40
- echo "⚠️ curl not found. Will attempt to install curl."
41
-
42
- case "$(uname)" in
43
- Linux)
44
- if command -v apt >/dev/null 2>&1; then
45
- echo "Installing curl using apt..."
46
- sudo apt update
47
- sudo apt install -y curl
48
- elif command -v yum >/dev/null 2>&1; then
49
- echo "Installing curl using yum..."
50
- sudo yum install -y curl
51
- else
52
- echo "⚠️ Unsupported Linux distribution. Please install curl manually."
53
- return 1
54
- fi
55
- ;;
56
- Darwin)
57
- if command -v brew >/dev/null 2>&1; then
58
- echo "Installing curl using Homebrew..."
59
- brew install curl
60
- else
61
- echo "⚠️ Homebrew not found. Please install curl manually."
62
- return 1
63
- fi
64
- ;;
65
- MINGW*|MSYS*|CYGWIN*)
66
- echo "On Windows, please install curl manually."
67
- return 1
68
- ;;
69
- *)
70
- echo "⚠️ Unsupported OS: $(uname). Please install curl manually."
71
- return 1
72
- ;;
73
- esac
74
-
75
- echo "✅ curl installation complete."
76
- return 0
77
- }
78
-
79
32
  # Check and install Docker Engine
80
33
  check_docker() {
81
34
  # echo "Checking for Docker..."
@@ -115,5 +68,4 @@ check_docker() {
115
68
  # Run all checks
116
69
  echo "🔍 Checking and installing required dependencies..."
117
70
  check_acl
118
- check_curl
119
71
  check_docker
@@ -1,5 +1,56 @@
1
1
  {
2
2
  "commands": {
3
+ "clean": {
4
+ "aliases": [],
5
+ "args": {
6
+ "layer": {
7
+ "description": "network layer to clean. e.g. gl0",
8
+ "name": "layer",
9
+ "required": true
10
+ }
11
+ },
12
+ "description": "Remove data and/or logs from a validator node",
13
+ "examples": [
14
+ "<%= config.bin %> <%= command.id %>"
15
+ ],
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
+ "data": {
25
+ "char": "d",
26
+ "description": "remove data",
27
+ "name": "data",
28
+ "allowNo": false,
29
+ "type": "boolean"
30
+ },
31
+ "logs": {
32
+ "char": "l",
33
+ "description": "remove logs",
34
+ "name": "logs",
35
+ "allowNo": false,
36
+ "type": "boolean"
37
+ }
38
+ },
39
+ "hasDynamicHelp": false,
40
+ "hiddenAliases": [],
41
+ "id": "clean",
42
+ "pluginAlias": "@constellation-network/node-pilot",
43
+ "pluginName": "@constellation-network/node-pilot",
44
+ "pluginType": "core",
45
+ "strict": true,
46
+ "enableJsonFlag": false,
47
+ "isESM": true,
48
+ "relativePath": [
49
+ "dist",
50
+ "commands",
51
+ "clean.js"
52
+ ]
53
+ },
3
54
  "config": {
4
55
  "aliases": [],
5
56
  "args": {},
@@ -151,7 +202,16 @@
151
202
  "examples": [
152
203
  "<%= config.bin %> <%= command.id %>"
153
204
  ],
154
- "flags": {},
205
+ "flags": {
206
+ "layer": {
207
+ "char": "l",
208
+ "description": "specify a layer to shutdown. e.g. gl0",
209
+ "name": "layer",
210
+ "hasDynamicHelp": false,
211
+ "multiple": false,
212
+ "type": "option"
213
+ }
214
+ },
155
215
  "hasDynamicHelp": false,
156
216
  "hiddenAliases": [],
157
217
  "id": "shutdown",
@@ -276,5 +336,5 @@
276
336
  ]
277
337
  }
278
338
  },
279
- "version": "0.0.17"
339
+ "version": "0.0.18"
280
340
  }
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.0.17",
4
+ "version": "0.0.18",
5
5
  "author": "Frank Fox",
6
6
  "bin": {
7
7
  "cpilot": "bin/run.js"