@constellation-network/node-pilot 0.0.13 → 0.0.15
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 +9 -9
- package/dist/checks/check-layers.js +2 -11
- package/dist/checks/check-network.d.ts +1 -0
- package/dist/checks/check-network.js +6 -0
- package/dist/checks/check-project.js +19 -19
- package/dist/commands/info.js +1 -1
- package/dist/commands/restart.js +20 -12
- package/dist/commands/status.js +1 -0
- package/dist/commands/test.js +2 -0
- package/dist/config-store.d.ts +2 -0
- package/dist/config-store.js +8 -1
- package/dist/helpers/project-helper.js +14 -5
- package/dist/helpers/status-table-helper.d.ts +29 -0
- package/dist/helpers/status-table-helper.js +62 -0
- package/dist/helpers/status-table.d.ts +8 -0
- package/dist/helpers/status-table.js +129 -0
- package/dist/services/fastforward-service.js +14 -5
- package/dist/services/node-service.js +4 -3
- package/dist/types.d.ts +0 -8
- package/install-dependencies.sh +31 -10
- package/oclif.manifest.json +1 -1
- package/package.json +3 -2
- package/projects/hypergraph/docker-compose.yml +13 -12
- package/projects/hypergraph/scripts/install-dependencies.sh +1 -91
- package/projects/hypergraph/scripts/install.sh +5 -0
- package/scripts/install_services.sh +78 -29
- package/scripts/node-pilot-update.service +11 -0
- package/projects/hypergraph/networks/integrationnet/source-nodes.env +0 -9
- package/projects/hypergraph/networks/mainnet/source-nodes.env +0 -9
- package/projects/hypergraph/networks/testnet/source-nodes.env +0 -9
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.
|
24
|
+
@constellation-network/node-pilot/0.0.15 darwin-arm64 node-v24.8.0
|
25
25
|
$ cpilot --help [COMMAND]
|
26
26
|
USAGE
|
27
27
|
$ cpilot COMMAND
|
@@ -62,7 +62,7 @@ EXAMPLES
|
|
62
62
|
$ cpilot config
|
63
63
|
```
|
64
64
|
|
65
|
-
_See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.
|
65
|
+
_See code: [src/commands/config.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/config.ts)_
|
66
66
|
|
67
67
|
## `cpilot config get [NAME]`
|
68
68
|
|
@@ -86,7 +86,7 @@ EXAMPLES
|
|
86
86
|
$ cpilot config get gl0:CL_PUBLIC_HTTP_PORT
|
87
87
|
```
|
88
88
|
|
89
|
-
_See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.
|
89
|
+
_See code: [src/commands/config/get.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/config/get.ts)_
|
90
90
|
|
91
91
|
## `cpilot config set NAME VALUE`
|
92
92
|
|
@@ -109,7 +109,7 @@ EXAMPLES
|
|
109
109
|
$ cpilot config set gl0:CL_PUBLIC_HTTP_PORT 9000
|
110
110
|
```
|
111
111
|
|
112
|
-
_See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.
|
112
|
+
_See code: [src/commands/config/set.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/config/set.ts)_
|
113
113
|
|
114
114
|
## `cpilot help [COMMAND]`
|
115
115
|
|
@@ -146,7 +146,7 @@ EXAMPLES
|
|
146
146
|
$ cpilot info
|
147
147
|
```
|
148
148
|
|
149
|
-
_See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.
|
149
|
+
_See code: [src/commands/info.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/info.ts)_
|
150
150
|
|
151
151
|
## `cpilot logs LAYER`
|
152
152
|
|
@@ -170,7 +170,7 @@ EXAMPLES
|
|
170
170
|
$ cpilot logs
|
171
171
|
```
|
172
172
|
|
173
|
-
_See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.
|
173
|
+
_See code: [src/commands/logs.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/logs.ts)_
|
174
174
|
|
175
175
|
## `cpilot restart [LAYER]`
|
176
176
|
|
@@ -198,7 +198,7 @@ EXAMPLES
|
|
198
198
|
$ cpilot restart
|
199
199
|
```
|
200
200
|
|
201
|
-
_See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.
|
201
|
+
_See code: [src/commands/restart.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/restart.ts)_
|
202
202
|
|
203
203
|
## `cpilot shutdown`
|
204
204
|
|
@@ -215,7 +215,7 @@ EXAMPLES
|
|
215
215
|
$ cpilot shutdown
|
216
216
|
```
|
217
217
|
|
218
|
-
_See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.
|
218
|
+
_See code: [src/commands/shutdown.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/shutdown.ts)_
|
219
219
|
|
220
220
|
## `cpilot status`
|
221
221
|
|
@@ -229,5 +229,5 @@ DESCRIPTION
|
|
229
229
|
Display node status and configuration settings
|
230
230
|
```
|
231
231
|
|
232
|
-
_See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.
|
232
|
+
_See code: [src/commands/status.ts](https://github.com/Constellation-Labs/node-pilot/blob/v0.0.15/src/commands/status.ts)_
|
233
233
|
<!-- commandsstop -->
|
@@ -2,6 +2,7 @@ import { input } from "@inquirer/prompts";
|
|
2
2
|
import chalk from "chalk";
|
3
3
|
import { clm } from "../clm.js";
|
4
4
|
import { configStore } from "../config-store.js";
|
5
|
+
import { StatusTable } from "../helpers/status-table.js";
|
5
6
|
import { dockerService } from "../services/docker-service.js";
|
6
7
|
import { nodeService } from "../services/node-service.js";
|
7
8
|
export const checkLayers = {
|
@@ -62,17 +63,7 @@ export const checkLayers = {
|
|
62
63
|
}
|
63
64
|
},
|
64
65
|
async layersStatus() {
|
65
|
-
|
66
|
-
const promises = layersToRun.map(l => nodeService.getNodeInfo(l));
|
67
|
-
const results = await Promise.all(promises);
|
68
|
-
const states = results.map(r => r.state);
|
69
|
-
clm.preStep("-----------------------");
|
70
|
-
clm.preStep(" VALIDATOR NODE STATUS ");
|
71
|
-
clm.preStep("-----------------------");
|
72
|
-
for (const [i, layer] of layersToRun.entries()) {
|
73
|
-
const state = states[i];
|
74
|
-
clm.step(` "${layer.toUpperCase()}" is "${state}"`);
|
75
|
-
}
|
66
|
+
await StatusTable.run();
|
76
67
|
},
|
77
68
|
async nodeReadyToJoin(layer) {
|
78
69
|
const status = await nodeService.getNodeInfo(layer);
|
@@ -8,6 +8,12 @@ import { clusterService } from "../services/cluster-service.js";
|
|
8
8
|
import { dockerService } from "../services/docker-service.js";
|
9
9
|
import { shellService } from "../services/shell-service.js";
|
10
10
|
export const checkNetwork = {
|
11
|
+
checkExternalIpAddress() {
|
12
|
+
const { CL_EXTERNAL_IP } = configStore.getEnvInfo();
|
13
|
+
if (!CL_EXTERNAL_IP) {
|
14
|
+
return this.configureIpAddress();
|
15
|
+
}
|
16
|
+
},
|
11
17
|
async checkForExistingNodeIdInCluster() {
|
12
18
|
if (configStore.hasProjectFlag('duplicateNodeIdChecked')) {
|
13
19
|
return;
|
@@ -1,6 +1,4 @@
|
|
1
1
|
import { input, select } from "@inquirer/prompts";
|
2
|
-
import chalk from "chalk";
|
3
|
-
import ora from 'ora';
|
4
2
|
import { clm } from "../clm.js";
|
5
3
|
import { configStore } from "../config-store.js";
|
6
4
|
import { configHelper } from "../helpers/config-helper.js";
|
@@ -96,29 +94,31 @@ export const checkProject = {
|
|
96
94
|
if (isRunning) {
|
97
95
|
await dockerService.dockerDown();
|
98
96
|
}
|
99
|
-
const silent = !process.env.DEBUG;
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
97
|
+
// const silent = !process.env.DEBUG;
|
98
|
+
//
|
99
|
+
// const spinner = ora('');
|
100
|
+
//
|
101
|
+
// if (silent) {
|
102
|
+
// spinner.start();
|
103
|
+
// spinner.color = 'green';
|
104
|
+
// }
|
105
|
+
// else {
|
106
|
+
clm.preStep('Running install script...');
|
107
|
+
// }
|
108
108
|
// const node = await clusterService.getClusterNodeInfo();
|
109
109
|
// const NODE_URL = `http://${node.host}:${node.publicPort}`;
|
110
110
|
// NOTE: may be different for metagraphs
|
111
|
-
await shellService.runProjectCommand(`scripts/install.sh ${nInfo.type}`, undefined,
|
111
|
+
await shellService.runProjectCommand(`scripts/install.sh ${nInfo.type}`, undefined, false)
|
112
112
|
.catch(() => {
|
113
|
-
spinner.stop();
|
114
|
-
if (silent) {
|
115
|
-
|
116
|
-
}
|
113
|
+
// spinner.stop();
|
114
|
+
// if (silent) {
|
115
|
+
// clm.error(`Install script failed. run: ${chalk.cyan("DEBUG=true cpilot")} for more details`);
|
116
|
+
// }
|
117
117
|
clm.error('Install script failed. Please run cpilot again after correcting the error');
|
118
118
|
});
|
119
|
-
if (silent) {
|
120
|
-
|
121
|
-
}
|
119
|
+
// if (silent) {
|
120
|
+
// spinner.stop();
|
121
|
+
// }
|
122
122
|
rInfo = await configHelper.getReleaseInfo();
|
123
123
|
configStore.setNetworkInfo({
|
124
124
|
type: rInfo.network,
|
package/dist/commands/info.js
CHANGED
@@ -20,7 +20,7 @@ export default class Info extends Command {
|
|
20
20
|
// Node ID
|
21
21
|
configHelper.showEnvInfo('Node ID', projectInfo.nodeId);
|
22
22
|
// Layers to Run
|
23
|
-
configHelper.showEnvInfo('Layers to Run', projectInfo.layersToRun
|
23
|
+
configHelper.showEnvInfo('Layers to Run', projectInfo.layersToRun?.join(', '));
|
24
24
|
// Network type
|
25
25
|
configHelper.showEnvInfo('Network', networkInfo.type);
|
26
26
|
// Network version
|
package/dist/commands/restart.js
CHANGED
@@ -27,7 +27,7 @@ export default class Restart extends BaseCommand {
|
|
27
27
|
serviceLog.log('Executing "cpilot restart --update" at ' + new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
|
28
28
|
const project = configStore.getActiveProject();
|
29
29
|
const activeProjects = configStore.getRunningProjects();
|
30
|
-
serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
|
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
|
@@ -44,7 +44,7 @@ export default class Restart extends BaseCommand {
|
|
44
44
|
serviceLog.log('Executing "cpilot restart --autostart" at ' + new Date().toLocaleString('en-US', { timeZone: 'America/Los_Angeles' }));
|
45
45
|
const project = configStore.getActiveProject();
|
46
46
|
const activeProjects = configStore.getRunningProjects();
|
47
|
-
serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
|
47
|
+
// serviceLog.log(` Active projects: ${activeProjects.join(', ')}...`);
|
48
48
|
for (const project of activeProjects) {
|
49
49
|
serviceLog.log(' ' + project + ' is restarting...');
|
50
50
|
configStore.setActiveProject(project);
|
@@ -75,16 +75,24 @@ export default class Restart extends BaseCommand {
|
|
75
75
|
// const pAll = layersToRun.map(l => nodeService.getNodeInfo(l));
|
76
76
|
// const info = await Promise.all(pAll);
|
77
77
|
// const isRunning = info.some(n => n.state !== 'Unavailable');
|
78
|
-
if (
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
78
|
+
if (configStore.isRestarting())
|
79
|
+
return;
|
80
|
+
configStore.setIsRestarting(true);
|
81
|
+
try {
|
82
|
+
if (await dockerService.isRunning()) {
|
83
|
+
await nodeService.leaveClusterAllLayers();
|
84
|
+
const { layersToRun } = configStore.getProjectInfo();
|
85
|
+
await nodeService.pollForLayersState(layersToRun, 'Offline');
|
86
|
+
clm.preStep('Stopping the node...');
|
87
|
+
await dockerService.dockerDown();
|
88
|
+
}
|
89
|
+
clm.preStep('Checking for a new version...');
|
90
|
+
await checkProject.runUpgrade();
|
91
|
+
clm.preStep('Starting the node...');
|
92
|
+
await dockerService.dockerUp();
|
93
|
+
}
|
94
|
+
finally {
|
95
|
+
configStore.setIsRestarting(false);
|
84
96
|
}
|
85
|
-
clm.preStep('Checking for a new version...');
|
86
|
-
await checkProject.runUpgrade();
|
87
|
-
clm.preStep('Starting the node...');
|
88
|
-
await dockerService.dockerUp();
|
89
97
|
}
|
90
98
|
}
|
package/dist/commands/status.js
CHANGED
@@ -17,6 +17,7 @@ export default class Status extends Command {
|
|
17
17
|
export async function checkInstallationAndConfigurationStatus() {
|
18
18
|
await checkInitialSetup.firstTimeRun();
|
19
19
|
await checkProject.projectInstallation();
|
20
|
+
await checkNetwork.checkExternalIpAddress();
|
20
21
|
await checkNetwork.isNetworkConnectable();
|
21
22
|
await checkProject.releaseVersion();
|
22
23
|
await checkNodeCtl.check4Migration();
|
package/dist/commands/test.js
CHANGED
@@ -4,6 +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
8
|
export default class Test extends Command {
|
8
9
|
static description = 'node pilot test command';
|
9
10
|
static hidden = true;
|
@@ -17,6 +18,7 @@ export default class Test extends Command {
|
|
17
18
|
//
|
18
19
|
// await archiverService.syncToLatestSnapshot();
|
19
20
|
// await archiverService.checkLogsForMissingSnapshots();
|
21
|
+
await StatusTable.run();
|
20
22
|
}
|
21
23
|
async testRandomNode() {
|
22
24
|
const filePath = path.resolve(path.dirname(fileURLToPath(import.meta.url)), `../../projects/hypergraph/networks/mainnet/source-nodes.env`);
|
package/dist/config-store.d.ts
CHANGED
@@ -18,6 +18,7 @@ declare class ConfigStore {
|
|
18
18
|
getSystemInfo(): SystemInfo;
|
19
19
|
hasProjectFlag(name: string): any;
|
20
20
|
hasProjects(): boolean;
|
21
|
+
isRestarting(): boolean;
|
21
22
|
setActiveProject(name: string): void;
|
22
23
|
setClusterStats(info: Partial<ClusterStats>): void;
|
23
24
|
setDockerEnvInfo(info: Partial<{
|
@@ -27,6 +28,7 @@ declare class ConfigStore {
|
|
27
28
|
setEnvInfo(info: Partial<EnvInfo>): void;
|
28
29
|
setEnvLayerInfo(network: NetworkType, layer: TessellationLayer, info: Partial<EnvLayerInfo>): void;
|
29
30
|
setEnvNetworkInfo(network: NetworkType, info: Partial<EnvNetworkInfo>): void;
|
31
|
+
setIsRestarting(val: boolean): void;
|
30
32
|
setNetworkInfo(info: Partial<NetworkInfo>): void;
|
31
33
|
setProjectFlag(name: string, value: boolean): any;
|
32
34
|
setProjectInfo(info: Partial<ProjectInfo>): void;
|
package/dist/config-store.js
CHANGED
@@ -20,7 +20,7 @@ class ConfigStore {
|
|
20
20
|
this.pilotStore = new JSONStorage(path.join(appDir, 'config'));
|
21
21
|
const appInfo = this.pilotStore.getItem('pilot');
|
22
22
|
if (!appInfo) {
|
23
|
-
this.pilotStore.setItem('pilot', { appDir, project: 'undefined', projects: [], running: [] });
|
23
|
+
this.pilotStore.setItem('pilot', { appDir, project: 'undefined', projects: [], restarting: false, running: [] });
|
24
24
|
}
|
25
25
|
const { project } = this.pilotStore.getItem('pilot');
|
26
26
|
this.projectStore = project === 'undefined' ? new EmptyStorage() : new JSONStorage(path.join(appDir, project, 'config'));
|
@@ -104,6 +104,10 @@ class ConfigStore {
|
|
104
104
|
const { projects } = this.pilotStore.getItem('pilot');
|
105
105
|
return projects.length > 0;
|
106
106
|
}
|
107
|
+
isRestarting() {
|
108
|
+
const { restarting } = this.pilotStore.getItem('pilot');
|
109
|
+
return restarting;
|
110
|
+
}
|
107
111
|
setActiveProject(name) {
|
108
112
|
const { appDir, project, projects } = this.pilotStore.getItem('pilot');
|
109
113
|
if (projects && projects.includes(name)) {
|
@@ -142,6 +146,9 @@ class ConfigStore {
|
|
142
146
|
networks = {};
|
143
147
|
this.projectStore.setItem('network-env', { ...networks, [network]: { ...networks[network], ...info } });
|
144
148
|
}
|
149
|
+
setIsRestarting(val) {
|
150
|
+
this.setPilotInfo({ restarting: val });
|
151
|
+
}
|
145
152
|
setNetworkInfo(info) {
|
146
153
|
const oldInfo = this.projectStore.getItem('network');
|
147
154
|
this.projectStore.setItem('network', { ...oldInfo, ...info });
|
@@ -6,6 +6,7 @@ import { fileURLToPath } from 'node:url';
|
|
6
6
|
import { clm } from "../clm.js";
|
7
7
|
import { configStore } from "../config-store.js";
|
8
8
|
import { githubService } from "../services/github-service.js";
|
9
|
+
import { shellService } from "../services/shell-service.js";
|
9
10
|
import { configHelper } from "./config-helper.js";
|
10
11
|
import { getLayerEnvFileContent } from "./env-templates.js";
|
11
12
|
export const projectHelper = {
|
@@ -63,11 +64,19 @@ export const projectHelper = {
|
|
63
64
|
async installHypergraph() {
|
64
65
|
await this.installEmbedded('hypergraph');
|
65
66
|
const { projectDir } = configStore.getProjectInfo();
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
67
|
+
const { platform } = configStore.getSystemInfo();
|
68
|
+
// Create gl0 folder for the fast-forward feature before Docker does
|
69
|
+
if (platform === 'linux') {
|
70
|
+
const layerDir = path.join(projectDir, 'gl0');
|
71
|
+
// set permission for group "docker" on the layer folder and any subfolders created later
|
72
|
+
await shellService.runCommand(`sudo setfacl -m g:docker:rwX -dm g:docker:rwX ${layerDir}`);
|
73
|
+
}
|
74
|
+
else {
|
75
|
+
const gl0DataDir = path.join(projectDir, 'gl0', 'data');
|
76
|
+
fs.mkdirSync(path.join(gl0DataDir, 'incremental_snapshot'), { recursive: true });
|
77
|
+
fs.mkdirSync(path.join(gl0DataDir, 'snapshot_info'));
|
78
|
+
fs.mkdirSync(path.join(gl0DataDir, 'tmp'));
|
79
|
+
}
|
71
80
|
this.importEnvFiles();
|
72
81
|
},
|
73
82
|
async installProject(name, projectFolder) {
|
@@ -0,0 +1,29 @@
|
|
1
|
+
export declare const statusTableHeader: ({
|
2
|
+
color: string;
|
3
|
+
headerColor: string;
|
4
|
+
value: string;
|
5
|
+
formatter?: undefined;
|
6
|
+
width?: undefined;
|
7
|
+
} | {
|
8
|
+
color: string;
|
9
|
+
formatter: (startTime: number | string) => string;
|
10
|
+
headerColor: string;
|
11
|
+
value: string;
|
12
|
+
width?: undefined;
|
13
|
+
} | {
|
14
|
+
color: string;
|
15
|
+
formatter: (value: string) => string;
|
16
|
+
headerColor: string;
|
17
|
+
value: string;
|
18
|
+
width: number;
|
19
|
+
})[];
|
20
|
+
export type NodeStatusInfo = {
|
21
|
+
clusterOrdinal: number;
|
22
|
+
clusterSession: string;
|
23
|
+
clusterState: string;
|
24
|
+
error: string;
|
25
|
+
ordinal: number;
|
26
|
+
pilotSession: string;
|
27
|
+
session: string;
|
28
|
+
state: string;
|
29
|
+
};
|
@@ -0,0 +1,62 @@
|
|
1
|
+
class CellFormatter {
|
2
|
+
formatDistance(value) {
|
3
|
+
if (value === '-')
|
4
|
+
return value;
|
5
|
+
const num = Number(value);
|
6
|
+
if (num === 0)
|
7
|
+
return value;
|
8
|
+
if (num < 4)
|
9
|
+
return this.style(value.toString(), "bgYellow", "bold");
|
10
|
+
return this.style(value.toString(), "bgRed", "bold");
|
11
|
+
}
|
12
|
+
formatError(value) {
|
13
|
+
if (value === '-')
|
14
|
+
return value;
|
15
|
+
return this.style(value, "bgRed", "bold");
|
16
|
+
}
|
17
|
+
formatOrdinal(value) {
|
18
|
+
const [v, changed] = value.split(':');
|
19
|
+
if (changed) {
|
20
|
+
return this.style(v, "bgCyan");
|
21
|
+
}
|
22
|
+
return this.style(v, "cyan");
|
23
|
+
}
|
24
|
+
formatState(value) {
|
25
|
+
if (value === 'Offline')
|
26
|
+
return this.style(value, "bgRed", "bold");
|
27
|
+
if (value === 'Ready')
|
28
|
+
return this.style(value, "green");
|
29
|
+
if (value === 'ReadyToJoin')
|
30
|
+
return this.style(value, "yellow", "bold");
|
31
|
+
if (value === 'Restarting')
|
32
|
+
return this.style(value, "yellow", "bold");
|
33
|
+
return this.style(value, "white");
|
34
|
+
}
|
35
|
+
formatUpTIme(startTime) {
|
36
|
+
if (!startTime)
|
37
|
+
return '-';
|
38
|
+
const upTimeMs = Date.now() - Number(startTime);
|
39
|
+
const upTimeSec = Math.floor(upTimeMs / 1000);
|
40
|
+
const hours = Math.floor(upTimeSec / 3600);
|
41
|
+
const minutes = Math.floor((upTimeSec % 3600) / 60);
|
42
|
+
const seconds = upTimeSec % 60;
|
43
|
+
if (hours < 1 && minutes < 1)
|
44
|
+
return `${seconds}s`;
|
45
|
+
return hours > 0 ? `${hours}h ${minutes}m ${seconds}s` : `${minutes}m ${seconds}s`;
|
46
|
+
}
|
47
|
+
style(value, color, style) {
|
48
|
+
return `c${color}v${value}s${style}`;
|
49
|
+
}
|
50
|
+
}
|
51
|
+
const { formatDistance, formatError, formatOrdinal, formatState, formatUpTIme } = new CellFormatter();
|
52
|
+
// Layer | Uptime | State | Ordinal | Distance from cluster | Cluster State | Error
|
53
|
+
export const statusTableHeader = [
|
54
|
+
{ color: 'white', headerColor: 'whiteBright', value: 'Network' },
|
55
|
+
{ color: 'whiteBright', headerColor: 'whiteBright', value: 'Layer' },
|
56
|
+
{ color: 'white', formatter: formatUpTIme, headerColor: 'whiteBright', value: 'Uptime' },
|
57
|
+
{ color: 'white', formatter: formatState, headerColor: 'whiteBright', value: 'Node State', width: 18 },
|
58
|
+
{ color: 'white', formatter: formatOrdinal, headerColor: 'whiteBright', value: 'Ordinal' },
|
59
|
+
{ color: 'white', formatter: formatDistance, headerColor: 'whiteBright', value: 'Ord. lag' },
|
60
|
+
{ color: 'white', formatter: formatState, headerColor: 'whiteBright', value: 'Cluster State', width: 16 },
|
61
|
+
{ color: 'white', formatter: formatError, headerColor: 'whiteBright', value: 'Error', width: 22 },
|
62
|
+
];
|
@@ -0,0 +1,129 @@
|
|
1
|
+
import { createPrompt, useKeypress } from '@inquirer/core';
|
2
|
+
import fs from "node:fs";
|
3
|
+
import os from "node:os";
|
4
|
+
import ttyTable from "tty-table";
|
5
|
+
import { clm } from "../clm.js";
|
6
|
+
import { configStore } from "../config-store.js";
|
7
|
+
import { statusTableHeader } from "./status-table-helper.js";
|
8
|
+
// mismatched snapshot hash, cluster fork, lagging behind cluster, stalled state, missing snapshot
|
9
|
+
// const possibleErrors = [
|
10
|
+
// '-',
|
11
|
+
// 'Bad snapshot hash',
|
12
|
+
// 'Cluster fork',
|
13
|
+
// 'Lagging behind',
|
14
|
+
// 'Stalled state',
|
15
|
+
// 'Missing snapshot',
|
16
|
+
// ]
|
17
|
+
//
|
18
|
+
// const possibleStates =
|
19
|
+
// [
|
20
|
+
// 'Ready',
|
21
|
+
// 'Restarting',
|
22
|
+
// 'Offline',
|
23
|
+
// 'SessionStarted'
|
24
|
+
// ]
|
25
|
+
export class StatusTable {
|
26
|
+
alreadyRendered = false;
|
27
|
+
previousHeight = 0;
|
28
|
+
static async run() {
|
29
|
+
process.on("exit", () => {
|
30
|
+
// show cursor
|
31
|
+
console.log("\u001B[?25h");
|
32
|
+
});
|
33
|
+
process.on("SIGINT", () => {
|
34
|
+
process.exit();
|
35
|
+
});
|
36
|
+
const table = new StatusTable();
|
37
|
+
table.monitorState();
|
38
|
+
const onKeyPress = createPrompt((_, done) => {
|
39
|
+
useKeypress(() => { done(''); });
|
40
|
+
return '';
|
41
|
+
});
|
42
|
+
await onKeyPress({});
|
43
|
+
console.log("\u001B[?25h");
|
44
|
+
process.exit();
|
45
|
+
}
|
46
|
+
getProjectInfo() {
|
47
|
+
const appPath = os.homedir() + '/.node-pilot';
|
48
|
+
const activeProject = configStore.getActiveProject();
|
49
|
+
const projects = configStore.getRunningProjects();
|
50
|
+
if (!activeProject || projects.length === 0) {
|
51
|
+
clm.error("No running projects found.");
|
52
|
+
}
|
53
|
+
const info = [];
|
54
|
+
for (const project of projects) {
|
55
|
+
configStore.setActiveProject(project);
|
56
|
+
const { layersToRun } = configStore.getProjectInfo();
|
57
|
+
const { type: network } = configStore.getNetworkInfo();
|
58
|
+
for (const layer of layersToRun) {
|
59
|
+
info.push({ layer, network, path: `${appPath}/${project}/${layer}/data/health-check/node`, project });
|
60
|
+
}
|
61
|
+
}
|
62
|
+
configStore.setActiveProject(activeProject);
|
63
|
+
return info;
|
64
|
+
}
|
65
|
+
// Layer | Uptime | State | Ordinal | Distance from cluster | Cluster State | Error
|
66
|
+
// state: Restarting...
|
67
|
+
// cluster state: Ready, Offline, Restarting...
|
68
|
+
// possibleErrors: mismatched snapshot hash, cluster fork, lagging behind cluster, stalled state, missing snapshot
|
69
|
+
async monitorState() {
|
70
|
+
const projects = this.getProjectInfo();
|
71
|
+
const values = {};
|
72
|
+
while (true) {
|
73
|
+
const rows = [];
|
74
|
+
for (const p of projects) {
|
75
|
+
if (!values[p.layer])
|
76
|
+
values[p.layer] = {};
|
77
|
+
const n = JSON.parse(fs.readFileSync(p.path, 'utf8'));
|
78
|
+
const projectName = p.project === 'hypergraph' ? '' : p.project;
|
79
|
+
const network = p.network === 'integrationnet' ? 'intnet' : p.network;
|
80
|
+
const distance = Number.isNaN(n.clusterOrdinal - n.ordinal) ? '-' : String(n.clusterOrdinal - n.ordinal);
|
81
|
+
const ordinal = (values[p.layer].ordinal === n.ordinal) ? String(n.ordinal) : n.ordinal + ':true';
|
82
|
+
rows.push([
|
83
|
+
projectName + ':' + network,
|
84
|
+
p.layer,
|
85
|
+
n.session, // n.pilotSession,
|
86
|
+
n.state,
|
87
|
+
n.ordinal ? ordinal : '-',
|
88
|
+
distance,
|
89
|
+
n.clusterState || 'Ready',
|
90
|
+
n.error || '-'
|
91
|
+
]);
|
92
|
+
values[p.layer].ordinal = n.ordinal;
|
93
|
+
}
|
94
|
+
// const rows = [
|
95
|
+
// ["GL0", '1760103674654', possibleStates[Math.floor(Math.random()*possibleStates.length)], '5174762', String(Math.floor(Math.random()*8)), possibleStates[Math.floor(Math.random()*possibleStates.length)], possibleErrors[Math.floor(Math.random()*possibleErrors.length)]],
|
96
|
+
// ["GL1", '1760102674654', possibleStates[Math.floor(Math.random()*possibleStates.length)], '4174762', String(Math.floor(Math.random()*8)), possibleStates[Math.floor(Math.random()*possibleStates.length)], possibleErrors[Math.floor(Math.random()*possibleErrors.length)]],
|
97
|
+
// ]
|
98
|
+
this.render(rows);
|
99
|
+
process.stdout.write(" *press any key to cancel");
|
100
|
+
// eslint-disable-next-line no-await-in-loop
|
101
|
+
await sleep(1);
|
102
|
+
}
|
103
|
+
}
|
104
|
+
render(body) {
|
105
|
+
const options = { terminalAdapter: true };
|
106
|
+
const t1 = ttyTable(statusTableHeader, body, options);
|
107
|
+
// hide cursor
|
108
|
+
console.log("\u001B[?25l");
|
109
|
+
// wipe existing if already rendered
|
110
|
+
if (this.alreadyRendered) {
|
111
|
+
// move cursor up number to the top of the previous print before deleting
|
112
|
+
console.log(`\u001B[${this.previousHeight + 3}A`);
|
113
|
+
// delete to end of terminal
|
114
|
+
console.log("\u001B[0J");
|
115
|
+
}
|
116
|
+
else {
|
117
|
+
this.alreadyRendered = true;
|
118
|
+
}
|
119
|
+
console.log(t1.render());
|
120
|
+
// reset the previous height to the height of this output
|
121
|
+
// for when we next clear the print
|
122
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
123
|
+
// @ts-expect-error
|
124
|
+
this.previousHeight = t1.height;
|
125
|
+
}
|
126
|
+
}
|
127
|
+
function sleep(sec) {
|
128
|
+
return new Promise(resolve => { setTimeout(resolve, sec * 1000); });
|
129
|
+
}
|
@@ -5,6 +5,7 @@ import 'json-bigint-patch';
|
|
5
5
|
import path from "node:path";
|
6
6
|
import { clm } from "../clm.js";
|
7
7
|
import { configStore } from "../config-store.js";
|
8
|
+
import { promptHelper } from "../helpers/prompt-helper.js";
|
8
9
|
const CHUNK_SIZE = 20_000;
|
9
10
|
export class FastforwardService {
|
10
11
|
dataDir;
|
@@ -19,17 +20,19 @@ export class FastforwardService {
|
|
19
20
|
this.dataDir = path.join(projectDir, 'gl0', 'data');
|
20
21
|
fs.mkdirSync(this.tmpDir, { recursive: true });
|
21
22
|
fs.mkdirSync(this.dataDir, { recursive: true });
|
22
|
-
const env = configStore.getEnvNetworkInfo(type);
|
23
|
-
|
24
|
-
this.lbUrl = `http://${env.CL_L0_PEER_HTTP_HOST}:${env.CL_L0_PEER_HTTP_PORT}`;
|
23
|
+
// const env = configStore.getEnvNetworkInfo(type);
|
24
|
+
this.lbUrl = `https://l0-lb-${this.network}.constellationnetwork.io`;
|
25
|
+
// this.lbUrl = `http://${env.CL_L0_PEER_HTTP_HOST}:${env.CL_L0_PEER_HTTP_PORT}`;
|
25
26
|
}
|
26
27
|
static async synctoLatestSnapshot() {
|
27
28
|
const ffs = new FastforwardService();
|
28
|
-
await ffs.runFastForwardSnapshot().catch(() => {
|
29
|
+
await ffs.runFastForwardSnapshot().catch(async (error) => {
|
30
|
+
clm.debug(error);
|
29
31
|
const { projectDir } = configStore.getProjectInfo();
|
30
32
|
const dataDir = path.join(projectDir, 'gl0', 'data', 'incremental_snapshot', 'ordinal');
|
31
33
|
if (fs.existsSync(dataDir) && fs.readdirSync(dataDir).length > 0) {
|
32
34
|
clm.warn('Failed to fast forward to latest snapshot. Skipping...');
|
35
|
+
await promptHelper.doYouWishToContinue();
|
33
36
|
return;
|
34
37
|
}
|
35
38
|
clm.error('Failed to fast forward to latest snapshot. Please try again later.');
|
@@ -43,7 +46,10 @@ export class FastforwardService {
|
|
43
46
|
// clm.debug('hash', Buffer.from(jsSha256.sha256(compressedSnapshotIncremental)).toString('hex'));
|
44
47
|
const snapshotInfoDir = path.join(this.dataDir, 'snapshot_info');
|
45
48
|
fs.mkdirSync(snapshotInfoDir, { recursive: true });
|
46
|
-
|
49
|
+
const ordinalDir = path.join(snapshotInfoDir, ordinal.toString());
|
50
|
+
clm.debug('Saving compressedSnapshotInfo to: ' + ordinalDir);
|
51
|
+
fs.writeFileSync(ordinalDir, compressedSnapshotInfo);
|
52
|
+
clm.debug('Saving compressedSnapshotIncremental to: ' + this.tmpDir);
|
47
53
|
fs.writeFileSync(path.join(this.tmpDir, ordinal.toString() + '.c'), compressedSnapshotIncremental);
|
48
54
|
await this.saveSnapshotFiles(ordinal.toString(), hash);
|
49
55
|
clm.postStep(`Fastforward to snapshot "${ordinal}" completed.`);
|
@@ -94,9 +100,12 @@ export class FastforwardService {
|
|
94
100
|
clm.warn(`Snapshot ${destOrdinalFile} already exists. Skipping...`);
|
95
101
|
return;
|
96
102
|
}
|
103
|
+
clm.debug(`Saving snapshot ${ordinal} to ${destOrdinalFile}`);
|
97
104
|
fs.copyFileSync(ordinalFile, destOrdinalFile);
|
98
105
|
const hashFile = path.join(hashDir, hash);
|
106
|
+
clm.debug(`Saving hashFile to ${hashFile}`);
|
99
107
|
fs.linkSync(destOrdinalFile, hashFile);
|
108
|
+
clm.debug(`Saving snapshot version to ${path.join(this.dataDir, 'snapshot-version')}`);
|
100
109
|
fs.writeFileSync(path.join(this.dataDir, 'snapshot-version'), `${this.network}:${ordinal}`);
|
101
110
|
}
|
102
111
|
}
|
@@ -13,6 +13,7 @@ export const nodeService = {
|
|
13
13
|
.then(res => {
|
14
14
|
if (res.ok)
|
15
15
|
return res.json().then(i => ({ ...i, layer }));
|
16
|
+
clm.warn(`Failed to get node info from ${res.url} (${res.status})`);
|
16
17
|
throw new Error(`Failed`);
|
17
18
|
})
|
18
19
|
.catch(() => ({ layer, state: "Unavailable" }));
|
@@ -62,7 +63,7 @@ export const nodeService = {
|
|
62
63
|
const url = `http://localhost:${cliPort}/cluster/join`;
|
63
64
|
clm.preStep(`Joining ${layer} to cluster`); // on ${cliPort}...${body} ... ${url}`);
|
64
65
|
await shellService.execDockerShell(layer, `curl -X POST '${url}' -H 'Content-Type: application/json' --data '${body}'`);
|
65
|
-
await this.pollForState(layer, 'Ready');
|
66
|
+
// await this.pollForState(layer, 'Ready');
|
66
67
|
},
|
67
68
|
async leaveCluster(layer) {
|
68
69
|
const { state } = await this.getNodeInfo(layer);
|
@@ -103,7 +104,7 @@ export const nodeService = {
|
|
103
104
|
async pollForState(layer, expectedState = 'ReadyToJoin') {
|
104
105
|
clm.preStep(`Waiting for ${layer.toUpperCase()} Validator Node to become ${expectedState}...`);
|
105
106
|
await sleep(1);
|
106
|
-
for (let i = 1; i <=
|
107
|
+
for (let i = 1; i <= 24; i++) {
|
107
108
|
// eslint-disable-next-line no-await-in-loop
|
108
109
|
const { state } = await this.getNodeInfo(layer);
|
109
110
|
if (expectedState === 'Offline' && (state === "Unavailable" || state === "ReadyToJoin" || state === "SessionStarted"))
|
@@ -123,7 +124,7 @@ export const nodeService = {
|
|
123
124
|
// eslint-disable-next-line no-await-in-loop
|
124
125
|
await sleep(5);
|
125
126
|
}
|
126
|
-
clm.warn(`${layer} is not ${expectedState} after
|
127
|
+
clm.warn(`${layer} is not ${expectedState} after 2 minutes`);
|
127
128
|
return false;
|
128
129
|
}
|
129
130
|
};
|
package/dist/types.d.ts
CHANGED
@@ -38,14 +38,6 @@ export type ClusterStats = {
|
|
38
38
|
startTime: string;
|
39
39
|
total: number;
|
40
40
|
};
|
41
|
-
export type NodeStatusInfo = {
|
42
|
-
clusterSession: string;
|
43
|
-
id: string;
|
44
|
-
inConsensus: boolean;
|
45
|
-
inNetwork: boolean;
|
46
|
-
ip: string;
|
47
|
-
session: string;
|
48
|
-
};
|
49
41
|
export type NodeDiagnosticInfo = {
|
50
42
|
collateral: number;
|
51
43
|
hasCollateral: boolean;
|
package/install-dependencies.sh
CHANGED
@@ -3,6 +3,32 @@
|
|
3
3
|
# Break on any error
|
4
4
|
set -e
|
5
5
|
|
6
|
+
check_acl() {
|
7
|
+
# echo "Checking if ACL is enabled on filesystem"
|
8
|
+
if [[ "$(getfacl . | grep 'mask:')" != "" ]]; then
|
9
|
+
echo "✅ ACL is enabled on filesystem."
|
10
|
+
return 0
|
11
|
+
else
|
12
|
+
echo "⚠️ ACL is not enabled on filesystem. Attempting to enable it now."
|
13
|
+
if [[ "$(uname)" == "Linux" ]]; then
|
14
|
+
if command -v apt >/dev/null 2>&1; then
|
15
|
+
echo "Enabling ACL using apt..."
|
16
|
+
sudo apt install -y acl
|
17
|
+
else
|
18
|
+
echo "⚠️ Unsupported Linux distribution. Please enable ACL manually."
|
19
|
+
return 1
|
20
|
+
fi
|
21
|
+
echo "✅ ACL installation complete. "
|
22
|
+
elif [[ "$(uname)" == "Darwin" ]]; then
|
23
|
+
echo "⚠️ macOS does not support enabling ACL via this script. Please ensure ACL is enabled manually."
|
24
|
+
return 1
|
25
|
+
else
|
26
|
+
echo "⚠️ Unsupported OS: $(uname). Please enable ACL manually."
|
27
|
+
return 1
|
28
|
+
fi
|
29
|
+
fi
|
30
|
+
}
|
31
|
+
|
6
32
|
# Check and install curl
|
7
33
|
check_curl() {
|
8
34
|
# echo "Checking for curl..."
|
@@ -62,16 +88,10 @@ check_docker() {
|
|
62
88
|
|
63
89
|
case "$(uname)" in
|
64
90
|
Linux)
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
sudo usermod -aG docker $USER
|
70
|
-
echo "Docker installed. You may need to log out and back in for group changes to take effect."
|
71
|
-
else
|
72
|
-
echo "⚠️ Unsupported Linux distribution. Please install Docker manually."
|
73
|
-
return 1
|
74
|
-
fi
|
91
|
+
echo "Installing Docker using script..."
|
92
|
+
curl -fsSL https://get.docker.com | sudo sh
|
93
|
+
sudo usermod -aG docker $USER
|
94
|
+
echo "Docker installed. You NEED to log out and log back in for the changes to take effect."
|
75
95
|
;;
|
76
96
|
Darwin)
|
77
97
|
echo "Please install Docker Desktop manually from https://www.docker.com/products/docker-desktop"
|
@@ -94,5 +114,6 @@ check_docker() {
|
|
94
114
|
|
95
115
|
# Run all checks
|
96
116
|
echo "🔍 Checking and installing required dependencies..."
|
117
|
+
check_acl
|
97
118
|
check_curl
|
98
119
|
check_docker
|
package/oclif.manifest.json
CHANGED
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.
|
4
|
+
"version": "0.0.15",
|
5
5
|
"author": "Frank Fox",
|
6
6
|
"bin": {
|
7
7
|
"cpilot": "bin/run.js"
|
@@ -70,7 +70,8 @@
|
|
70
70
|
"projects",
|
71
71
|
"scripts",
|
72
72
|
"README.md",
|
73
|
-
"LICENSE"
|
73
|
+
"LICENSE",
|
74
|
+
"!**/source-nodes.env"
|
74
75
|
],
|
75
76
|
"homepage": "https://github.com/Constellation-Labs/node-pilot",
|
76
77
|
"keywords": [
|
@@ -3,10 +3,10 @@ networks:
|
|
3
3
|
driver: bridge
|
4
4
|
|
5
5
|
services:
|
6
|
-
autoheal:
|
7
|
-
image: willfarrell/autoheal
|
8
|
-
volumes:
|
9
|
-
- /var/run/docker.sock:/var/run/docker.sock
|
6
|
+
# autoheal:
|
7
|
+
# image: willfarrell/autoheal
|
8
|
+
# volumes:
|
9
|
+
# - /var/run/docker.sock:/var/run/docker.sock
|
10
10
|
gl0:
|
11
11
|
image: constellationnetwork/tessellation${CL_DOCKER_CORE_REPO:-}:${DOCKER_IMAGE_VERSION:-latest}
|
12
12
|
networks:
|
@@ -30,8 +30,8 @@ services:
|
|
30
30
|
timeout: 15s
|
31
31
|
start_period: 60s
|
32
32
|
profiles: ["gl0"]
|
33
|
-
labels:
|
34
|
-
autoheal: "true"
|
33
|
+
# labels:
|
34
|
+
# autoheal: "true"
|
35
35
|
gl1:
|
36
36
|
image: constellationnetwork/tessellation${CL_DOCKER_CORE_REPO:-}:${DOCKER_IMAGE_VERSION:-latest}
|
37
37
|
restart: unless-stopped
|
@@ -46,10 +46,11 @@ services:
|
|
46
46
|
- ./gl1/data:/app/data
|
47
47
|
- ./gl1/logs:/app/logs
|
48
48
|
- ./key.p12:/app/key.p12:ro
|
49
|
-
|
50
|
-
|
51
|
-
#
|
52
|
-
|
53
|
-
|
54
|
-
|
49
|
+
healthcheck:
|
50
|
+
test: ["CMD", "cpilotHC"]
|
51
|
+
# test: ["CMD", "/health-check/bin/run.sh"]
|
52
|
+
interval: 15s
|
53
|
+
retries: 1
|
54
|
+
timeout: 15s
|
55
|
+
start_period: 60s
|
55
56
|
profiles: ["gl1"]
|
@@ -11,7 +11,7 @@ check_java_home() {
|
|
11
11
|
JAVA_HOME_LINE='export JAVA_HOME="$(dirname "$(dirname "$(readlink -f "$(which java)")")")"'
|
12
12
|
echo "JAVA_HOME is not set. Attempting to set automatically"
|
13
13
|
echo "Please ensure the following line is in your ~/.bashrc or ~/.zshrc file:"
|
14
|
-
echo "Script will attempt to now add it for you and run it, but this only adds to .bashrc"
|
14
|
+
echo "Script will attempt to now add it for you and run it, but this only adds to .bashrc"
|
15
15
|
echo $JAVA_HOME_LINE
|
16
16
|
echo "$JAVA_HOME_LINE" >> $HOME/.bashrc
|
17
17
|
echo "Adding JAVA_HOME to current environment"
|
@@ -215,94 +215,6 @@ check_curl() {
|
|
215
215
|
return 0
|
216
216
|
}
|
217
217
|
|
218
|
-
# Check and install Docker Engine
|
219
|
-
check_docker() {
|
220
|
-
# echo "Checking for Docker..."
|
221
|
-
if command -v docker >/dev/null 2>&1; then
|
222
|
-
echo "✅ Docker is already installed."
|
223
|
-
return 0
|
224
|
-
fi
|
225
|
-
|
226
|
-
echo "⚠️ Docker not found. Will attempt to install Docker."
|
227
|
-
|
228
|
-
case "$(uname)" in
|
229
|
-
Linux)
|
230
|
-
if command -v apt >/dev/null 2>&1; then
|
231
|
-
echo "Installing Docker using script..."
|
232
|
-
curl -fsSL https://get.docker.com -o get-docker.sh
|
233
|
-
sudo sh ./get-docker.sh
|
234
|
-
sudo usermod -aG docker $USER
|
235
|
-
echo "Docker installed. You may need to log out and back in for group changes to take effect."
|
236
|
-
else
|
237
|
-
echo "⚠️ Unsupported Linux distribution. Please install Docker manually."
|
238
|
-
return 1
|
239
|
-
fi
|
240
|
-
;;
|
241
|
-
Darwin)
|
242
|
-
echo "Please install Docker Desktop manually from https://www.docker.com/products/docker-desktop"
|
243
|
-
return 1
|
244
|
-
;;
|
245
|
-
MINGW*|MSYS*|CYGWIN*)
|
246
|
-
echo "On Windows, please install Docker Desktop manually from https://www.docker.com/products/docker-desktop"
|
247
|
-
return 1
|
248
|
-
;;
|
249
|
-
*)
|
250
|
-
echo "⚠️ Unsupported OS: $(uname). Please install Docker manually."
|
251
|
-
return 1
|
252
|
-
;;
|
253
|
-
esac
|
254
|
-
|
255
|
-
echo "✅ Docker installation complete."
|
256
|
-
return 0
|
257
|
-
}
|
258
|
-
|
259
|
-
# Check and install Node.js
|
260
|
-
check_node() {
|
261
|
-
# echo "Checking for Node.js..."
|
262
|
-
if [ -d "$HOME/.nvm" ]; then
|
263
|
-
echo "✅ Node.js is already installed."
|
264
|
-
return 0
|
265
|
-
fi
|
266
|
-
|
267
|
-
echo "⚠️ Node.js not found. Will attempt to install Node.js."
|
268
|
-
|
269
|
-
case "$(uname)" in
|
270
|
-
Linux)
|
271
|
-
echo "Installing Node.js using nvm..."
|
272
|
-
# NPM
|
273
|
-
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
|
274
|
-
|
275
|
-
# Load nvm without needing to open a new terminal
|
276
|
-
export NVM_DIR="$HOME/.nvm"
|
277
|
-
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
|
278
|
-
[ -s "$NVM_DIR/bash_completion" ] && \. "$NVM_DIR/bash_completion" # This loads nvm bash_completion
|
279
|
-
|
280
|
-
# Install node
|
281
|
-
nvm install 22
|
282
|
-
|
283
|
-
# Verify installation
|
284
|
-
echo "Node version: $(node -v)"
|
285
|
-
echo "NPM version: $(npm -v)"
|
286
|
-
echo "NPX version: $(npx -v)"
|
287
|
-
;;
|
288
|
-
Darwin)
|
289
|
-
if command -v brew >/dev/null 2>&1; then
|
290
|
-
echo "Installing Node.js using Homebrew..."
|
291
|
-
brew install node
|
292
|
-
else
|
293
|
-
echo "⚠️ Homebrew not found. Please install Node.js manually."
|
294
|
-
return 1
|
295
|
-
fi
|
296
|
-
;;
|
297
|
-
*)
|
298
|
-
echo "⚠️ No Node.js installation needed for this OS: $(uname)"
|
299
|
-
return 0
|
300
|
-
;;
|
301
|
-
esac
|
302
|
-
|
303
|
-
echo "✅ Node.js installation complete."
|
304
|
-
return 0
|
305
|
-
}
|
306
218
|
|
307
219
|
# Run all checks
|
308
220
|
echo "🔍 Checking and installing required dependencies..."
|
@@ -311,7 +223,5 @@ check_java_home
|
|
311
223
|
check_jq
|
312
224
|
check_wget
|
313
225
|
check_curl
|
314
|
-
check_docker
|
315
|
-
check_node
|
316
226
|
|
317
227
|
echo "All dependency checks completed."
|
@@ -103,6 +103,11 @@ download_asset() {
|
|
103
103
|
|
104
104
|
# Download the asset
|
105
105
|
echo "$ASSET_NAME -> $OUTPUT_NAME"
|
106
|
+
# DEBUG=${DEBUG:-false}
|
107
|
+
# SILENT="-s"
|
108
|
+
# if [ "$DEBUG" = "true" ]; then
|
109
|
+
# SILENT="-#"
|
110
|
+
# fi
|
106
111
|
curl -# -L -H "Accept: application/octet-stream" -o "$OUTPUT_DIR/$OUTPUT_NAME" "$DOWNLOAD_URL"
|
107
112
|
|
108
113
|
if [ $? -ne 0 ]; then
|
@@ -1,70 +1,119 @@
|
|
1
1
|
#!/usr/bin/env bash
|
2
2
|
|
3
|
+
# Find cpilot in PATH
|
3
4
|
CPILOT=$(command -v cpilot)
|
4
5
|
if [ -z "$CPILOT" ]; then
|
5
6
|
echo "cpilot command not found. Please ensure Node Pilot is installed and accessible in your PATH."
|
6
7
|
exit 1
|
7
8
|
fi
|
8
9
|
|
9
|
-
|
10
|
+
# Get current user and home directory reliably
|
11
|
+
CURRENT_USER=$(id -un)
|
12
|
+
if [ "$CURRENT_USER" = "root" ]; then
|
13
|
+
IS_ROOT=true
|
14
|
+
WORKING_DIR="/etc/systemd/system"
|
15
|
+
HOME_DIR="/root"
|
16
|
+
UNIT_TARGET="multi-user.target"
|
17
|
+
else
|
18
|
+
IS_ROOT=false
|
19
|
+
HOME_DIR=$(eval echo ~$CURRENT_USER)
|
20
|
+
WORKING_DIR="$HOME_DIR/.config/systemd/user"
|
21
|
+
UNIT_TARGET="default.target"
|
22
|
+
fi
|
23
|
+
|
24
|
+
BIN_DIRECTORY="$HOME_DIR/.node-pilot/scripts"
|
10
25
|
|
11
|
-
mkdir -p $
|
26
|
+
mkdir -p "$BIN_DIRECTORY"
|
12
27
|
|
13
|
-
|
28
|
+
# Create working directory if it doesn't exist
|
29
|
+
mkdir -p "$WORKING_DIR"
|
30
|
+
|
31
|
+
# Create restart-unhealthy.service
|
32
|
+
cat << EOF > "$WORKING_DIR/restart-unhealthy.service"
|
14
33
|
[Unit]
|
15
34
|
Description=Restart unhealthy docker containers
|
16
35
|
After=docker.service
|
17
36
|
Wants=docker.service
|
18
37
|
|
19
38
|
[Service]
|
20
|
-
|
21
|
-
ExecStart=$
|
22
|
-
|
23
|
-
|
39
|
+
Type=oneshot
|
40
|
+
ExecStart=$BIN_DIRECTORY/restart-unhealthy.sh
|
41
|
+
EOF
|
42
|
+
|
43
|
+
# Create restart-unhealthy.timer
|
44
|
+
cat <<EOF > "$WORKING_DIR/restart-unhealthy.timer"
|
45
|
+
[Unit]
|
46
|
+
Description=Run docker unhealthy restart every 5 minutes
|
47
|
+
Requires=restart-unhealthy.service
|
48
|
+
After=docker.service
|
49
|
+
Wants=docker.service
|
50
|
+
|
51
|
+
[Timer]
|
52
|
+
OnCalendar=*:0/1
|
53
|
+
Persistent=true
|
54
|
+
|
55
|
+
[Install]
|
56
|
+
WantedBy=timers.target
|
24
57
|
EOF
|
25
58
|
|
26
|
-
|
59
|
+
# Create restart-unhealthy.sh
|
60
|
+
cat << EOF > "$BIN_DIRECTORY/restart-unhealthy.sh"
|
27
61
|
#!/usr/bin/env bash
|
28
62
|
docker ps -q -f health=unhealthy | xargs --no-run-if-empty docker restart
|
29
63
|
EOF
|
30
64
|
|
31
|
-
chmod +x $
|
65
|
+
chmod +x "$BIN_DIRECTORY/restart-unhealthy.sh"
|
32
66
|
|
33
|
-
|
67
|
+
# Create node-pilot-autostart.service
|
68
|
+
cat << EOF > "$WORKING_DIR/node-pilot-autostart.service"
|
34
69
|
[Unit]
|
35
70
|
Description=Constellation Network Node Auto Restart Service
|
36
|
-
After
|
71
|
+
After=$UNIT_TARGET
|
37
72
|
|
38
73
|
[Service]
|
39
|
-
ExecStart
|
74
|
+
ExecStart=cpilot restart --autostart
|
40
75
|
|
41
76
|
[Install]
|
42
|
-
WantedBy
|
77
|
+
WantedBy=$UNIT_TARGET
|
43
78
|
EOF
|
44
79
|
|
45
|
-
|
80
|
+
# Create node-pilot-update.service
|
81
|
+
cat << EOF > "$WORKING_DIR/node-pilot-update.service"
|
46
82
|
[Unit]
|
47
83
|
Description=Constellation Node Pilot Updater Service
|
48
|
-
After
|
84
|
+
After=$UNIT_TARGET
|
49
85
|
|
50
86
|
[Service]
|
51
|
-
|
52
|
-
ExecStart=$CPILOT restart --update
|
87
|
+
ExecStart=cpilot restart --update
|
53
88
|
Restart=always
|
54
89
|
RestartSec=5m
|
55
90
|
|
56
91
|
[Install]
|
57
|
-
WantedBy
|
92
|
+
WantedBy=$UNIT_TARGET
|
58
93
|
EOF
|
59
94
|
|
60
|
-
|
61
|
-
|
62
|
-
systemctl
|
63
|
-
systemctl
|
64
|
-
systemctl
|
65
|
-
|
66
|
-
|
67
|
-
systemctl
|
68
|
-
systemctl
|
69
|
-
|
70
|
-
|
95
|
+
# Enable and start services/timers
|
96
|
+
if [ "$IS_ROOT" = true ]; then
|
97
|
+
systemctl daemon-reload
|
98
|
+
systemctl enable restart-unhealthy.timer
|
99
|
+
systemctl enable node-pilot-autostart.service
|
100
|
+
systemctl enable node-pilot-update.service
|
101
|
+
|
102
|
+
systemctl start restart-unhealthy.timer
|
103
|
+
systemctl start node-pilot-autostart.service
|
104
|
+
systemctl start node-pilot-update.service
|
105
|
+
else
|
106
|
+
systemctl --user daemon-reload
|
107
|
+
systemctl --user enable restart-unhealthy.timer
|
108
|
+
systemctl --user enable node-pilot-autostart.service
|
109
|
+
systemctl --user enable node-pilot-update.service
|
110
|
+
|
111
|
+
systemctl --user start restart-unhealthy.timer
|
112
|
+
systemctl --user start node-pilot-autostart.service
|
113
|
+
systemctl --user start node-pilot-update.service
|
114
|
+
|
115
|
+
# Enable linger for the user to allow user services to run without login
|
116
|
+
loginctl enable-linger "$CURRENT_USER"
|
117
|
+
fi
|
118
|
+
#systemctl --user status node-pilot-update.service
|
119
|
+
# systemctl --user status restart-unhealthy
|
@@ -1,9 +0,0 @@
|
|
1
|
-
SOURCE_NODE_1_PORT=9000
|
2
|
-
SOURCE_NODE_1_HOST=13.52.205.240
|
3
|
-
SOURCE_NODE_1_ID=e2f4496e5872682d7a55aa06e507a58e96b5d48a5286bfdff7ed780fa464d9e789b2760ecd840f4cb3ee6e1c1d81b2ee844c88dbebf149b1084b7313eb680714
|
4
|
-
SOURCE_NODE_2_PORT=9000
|
5
|
-
SOURCE_NODE_2_HOST=50.18.155.22
|
6
|
-
SOURCE_NODE_2_ID=3458a688925a4bd89f2ac2c695362e44d2e0c2903bdbb41b341a4d39283b22d8c85b487bd33cc5d36dbe5e31b5b00a10a6eab802718ead4ed7192ade5a5d1941
|
7
|
-
SOURCE_NODE_3_PORT=9000
|
8
|
-
SOURCE_NODE_3_HOST=52.9.216.57
|
9
|
-
SOURCE_NODE_3_ID=46daea11ca239cb8c0c8cdeb27db9dbe9c03744908a8a389a60d14df2ddde409260a93334d74957331eec1af323f458b12b3a6c3b8e05885608aae7e3a77eac7
|
@@ -1,9 +0,0 @@
|
|
1
|
-
SOURCE_NODE_1_PORT=9000
|
2
|
-
SOURCE_NODE_1_HOST=52.53.46.33
|
3
|
-
SOURCE_NODE_1_ID=e0c1ee6ec43510f0e16d2969a7a7c074a5c8cdb477c074fe9c32a9aad8cbc8ff1dff60bb81923e0db437d2686a9b65b86c403e6a21fa32b6acc4e61be4d70925
|
4
|
-
SOURCE_NODE_2_PORT=9000
|
5
|
-
SOURCE_NODE_2_HOST=54.215.18.98
|
6
|
-
SOURCE_NODE_2_ID=629880a5b8d4cc6d12aec26f24230a463825c429723153aeaff29475b29e39d2406af0f8b034ba7798ae598dbd5f513d642bcbbeef088290abeadac61a0445d6
|
7
|
-
SOURCE_NODE_3_PORT=9000
|
8
|
-
SOURCE_NODE_3_HOST=54.151.19.111
|
9
|
-
SOURCE_NODE_3_ID=710b3dc521b805aea7a798d61f5d4dae39601124f1f34fac9738a78047adeff60931ba522250226b87a2194d3b7d39da8d2cbffa35d6502c70f1a7e97132a4b0
|
@@ -1,9 +0,0 @@
|
|
1
|
-
SOURCE_NODE_1_PORT=9000
|
2
|
-
SOURCE_NODE_1_HOST=52.8.132.193
|
3
|
-
SOURCE_NODE_1_ID=e2f4496e5872682d7a55aa06e507a58e96b5d48a5286bfdff7ed780fa464d9e789b2760ecd840f4cb3ee6e1c1d81b2ee844c88dbebf149b1084b7313eb680714
|
4
|
-
SOURCE_NODE_2_PORT=9000
|
5
|
-
SOURCE_NODE_2_HOST=13.57.110.45
|
6
|
-
SOURCE_NODE_2_ID=3458a688925a4bd89f2ac2c695362e44d2e0c2903bdbb41b341a4d39283b22d8c85b487bd33cc5d36dbe5e31b5b00a10a6eab802718ead4ed7192ade5a5d1941
|
7
|
-
SOURCE_NODE_3_PORT=9000
|
8
|
-
SOURCE_NODE_3_HOST=54.153.23.79
|
9
|
-
SOURCE_NODE_3_ID=46daea11ca239cb8c0c8cdeb27db9dbe9c03744908a8a389a60d14df2ddde409260a93334d74957331eec1af323f458b12b3a6c3b8e05885608aae7e3a77eac7
|