@airmoney-degn/airmoney-cli 0.16.2 → 0.18.0
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/dist/cli/create.js +48 -58
- package/dist/cli/dapp.js +97 -0
- package/dist/cli/demo.js +34 -28
- package/dist/cli/setup.js +21 -11
- package/dist/cli/upload.js +70 -63
- package/dist/cli/wallet.js +46 -30
- package/dist/config.json +1 -1
- package/dist/index.js +26 -11
- package/dist/service/airmoney/AirmoneyService.js +3 -2
- package/dist/service/dapp/DappService.js +162 -0
- package/dist/service/log/LogService.js +85 -0
- package/dist/service/port/PortManager.js +5 -4
- package/dist/service/serve/ServeOrchestrator.js +2 -1
- package/dist/service/simulator/BaseSimulatorService.js +5 -4
- package/dist/util/cryptoProcess.js +22 -21
- package/dist/util/env.js +44 -0
- package/dist/util/format.js +74 -0
- package/dist/util/metadata.js +3 -2
- package/dist/util/network.js +43 -0
- package/dist/util/tarball.js +6 -5
- package/package.json +3 -2
package/dist/index.js
CHANGED
|
@@ -8,8 +8,10 @@ const create_1 = require("./cli/create");
|
|
|
8
8
|
const serve_1 = require("./cli/serve");
|
|
9
9
|
const upload_1 = require("./cli/upload");
|
|
10
10
|
const setup_1 = require("./cli/setup");
|
|
11
|
+
const dapp_1 = require("./cli/dapp");
|
|
11
12
|
const wallet_1 = require("./cli/wallet");
|
|
12
13
|
const config_json_1 = require("./config.json");
|
|
14
|
+
const LogService_1 = require("./service/log/LogService");
|
|
13
15
|
// Load environment from config
|
|
14
16
|
(0, env_1.loadEnvFromConfig)();
|
|
15
17
|
const program = new commander_1.Command();
|
|
@@ -22,7 +24,7 @@ program
|
|
|
22
24
|
.description('Setup env with userAddress, apiKey, rpc')
|
|
23
25
|
.requiredOption('-u, --user <string>', 'developer user')
|
|
24
26
|
.requiredOption('-k, --key <string>', 'API key')
|
|
25
|
-
.option('-n, --network <string>', 'network devnet|mainnet'
|
|
27
|
+
.option('-n, --network <string>', 'network devnet|mainnet')
|
|
26
28
|
.action(opts => {
|
|
27
29
|
const { user, key, network } = opts;
|
|
28
30
|
(0, setup_1.setupCommand)({ network, userId: user, apiKey: key });
|
|
@@ -31,12 +33,11 @@ program
|
|
|
31
33
|
.command('create')
|
|
32
34
|
.description('Initialize a new project')
|
|
33
35
|
.requiredOption('-N, --name <string>', 'Project name')
|
|
34
|
-
// .option('-n, --network <string>', 'network devnet|mainnet', 'devnet')
|
|
35
36
|
.option('-f, --app-path <string>', 'path where project will be created')
|
|
36
37
|
.option('--template', 'initialize project based on git quickstart')
|
|
37
|
-
.action(
|
|
38
|
+
.action(opts => {
|
|
38
39
|
const { name, appPath, template } = opts;
|
|
39
|
-
|
|
40
|
+
(0, create_1.createCommand)({ name, template, locationFolder: appPath });
|
|
40
41
|
});
|
|
41
42
|
program
|
|
42
43
|
.command('serve')
|
|
@@ -44,16 +45,20 @@ program
|
|
|
44
45
|
.option('-f, --index-app-path <string>', 'path for the index.html', './')
|
|
45
46
|
.option('--no-browser', 'stop browser from being open')
|
|
46
47
|
.option('-u, --app-url <string>', 'url where the app is running')
|
|
47
|
-
.action(
|
|
48
|
+
.action(opts => {
|
|
48
49
|
let { indexAppPath, browser, appUrl } = opts;
|
|
49
50
|
if (indexAppPath == './') {
|
|
50
51
|
indexAppPath = undefined;
|
|
51
52
|
}
|
|
52
53
|
if (indexAppPath && appUrl) {
|
|
53
|
-
|
|
54
|
+
(0, LogService_1.log)('both --index-app-path and --app-url must not be used simuntaniusly').red();
|
|
54
55
|
return;
|
|
55
56
|
}
|
|
56
|
-
|
|
57
|
+
(0, serve_1.serveCommand)({
|
|
58
|
+
noBrowser: !browser,
|
|
59
|
+
locationFolder: indexAppPath,
|
|
60
|
+
appUrl,
|
|
61
|
+
});
|
|
57
62
|
});
|
|
58
63
|
program
|
|
59
64
|
.command('wallet')
|
|
@@ -93,16 +98,26 @@ program
|
|
|
93
98
|
program
|
|
94
99
|
.command('upload')
|
|
95
100
|
.description('Publish the app to the DEGN Dapp Store')
|
|
96
|
-
.option('-n, --network <string>', 'network devnet|mainnet'
|
|
101
|
+
.option('-n, --network <string>', 'network devnet|mainnet')
|
|
97
102
|
.option('-f, --index-app-path <string>', 'path for the index.html', './')
|
|
98
103
|
.option('-i, --button-image <string>', 'path for the button images', 'assets')
|
|
99
|
-
.action(
|
|
104
|
+
.action(opts => {
|
|
100
105
|
let { network, indexAppPath, buttonImage } = opts;
|
|
101
106
|
if (indexAppPath == './') {
|
|
102
107
|
indexAppPath = undefined;
|
|
103
108
|
}
|
|
104
|
-
|
|
105
|
-
|
|
109
|
+
(0, upload_1.uploadCommand)({
|
|
110
|
+
network,
|
|
111
|
+
locationFolder: indexAppPath,
|
|
112
|
+
buttonImages: buttonImage,
|
|
113
|
+
});
|
|
114
|
+
});
|
|
115
|
+
program
|
|
116
|
+
.command('dapp')
|
|
117
|
+
.description('Manage dapps')
|
|
118
|
+
.option('-s, --status', 'show all dapps list')
|
|
119
|
+
.action(() => {
|
|
120
|
+
(0, dapp_1.dappStatusCommand)();
|
|
106
121
|
});
|
|
107
122
|
program
|
|
108
123
|
.command('demo')
|
|
@@ -7,6 +7,7 @@ exports.AirmoneyService = void 0;
|
|
|
7
7
|
const express_1 = __importDefault(require("express"));
|
|
8
8
|
const cors_1 = __importDefault(require("cors"));
|
|
9
9
|
const server_1 = require("../../util/server");
|
|
10
|
+
const LogService_1 = require("../log/LogService");
|
|
10
11
|
class AirmoneyService {
|
|
11
12
|
constructor(config) {
|
|
12
13
|
this.simulatorClient = null;
|
|
@@ -40,7 +41,7 @@ class AirmoneyService {
|
|
|
40
41
|
return;
|
|
41
42
|
}
|
|
42
43
|
else {
|
|
43
|
-
|
|
44
|
+
(0, LogService_1.log)('No simulator client connected').white();
|
|
44
45
|
res.status(503).json({ error: 'Simulator not connected' });
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
@@ -62,7 +63,7 @@ class AirmoneyService {
|
|
|
62
63
|
return new Promise(resolve => {
|
|
63
64
|
this.server = this.app.listen(this.config.port, () => {
|
|
64
65
|
const url = `http://localhost:${this.config.port}`;
|
|
65
|
-
|
|
66
|
+
(0, LogService_1.log)(`Starting airmoney service server at ${url}`).green();
|
|
66
67
|
resolve();
|
|
67
68
|
});
|
|
68
69
|
});
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.DappService = void 0;
|
|
7
|
+
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
8
|
+
const network_1 = require("../../util/network");
|
|
9
|
+
/**
|
|
10
|
+
* Service for fetching and managing dapp information
|
|
11
|
+
*/
|
|
12
|
+
class DappService {
|
|
13
|
+
constructor(userId, apiKey, network) {
|
|
14
|
+
this.userId = userId;
|
|
15
|
+
this.apiKey = apiKey;
|
|
16
|
+
this.network = network;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Parses JSON-RPC error response and extracts error message
|
|
20
|
+
* Combines error.message and error.data fields (message first, then data)
|
|
21
|
+
*/
|
|
22
|
+
parseJsonRpcError(json) {
|
|
23
|
+
if (!json.error) {
|
|
24
|
+
return 'Unknown error';
|
|
25
|
+
}
|
|
26
|
+
let errorMessageParts = [];
|
|
27
|
+
// Add message field first if it exists
|
|
28
|
+
if (json.error.message) {
|
|
29
|
+
errorMessageParts.push(json.error.message);
|
|
30
|
+
}
|
|
31
|
+
// Parse and add data field if it exists and is different from message
|
|
32
|
+
if (json.error.data !== undefined) {
|
|
33
|
+
let dataMessage = '';
|
|
34
|
+
if (typeof json.error.data === 'string') {
|
|
35
|
+
try {
|
|
36
|
+
const parsed = JSON.parse(json.error.data);
|
|
37
|
+
dataMessage = parsed;
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
dataMessage = json.error.data.replace(/^"|"$/g, '');
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
else {
|
|
44
|
+
dataMessage = String(json.error.data);
|
|
45
|
+
}
|
|
46
|
+
// Only add data if it's different from message
|
|
47
|
+
if (dataMessage && dataMessage !== json.error.message) {
|
|
48
|
+
errorMessageParts.push(dataMessage);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
// Combine into final error message
|
|
52
|
+
return errorMessageParts.length > 0
|
|
53
|
+
? errorMessageParts.join(' - ')
|
|
54
|
+
: 'Unknown error';
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Makes a JSON-RPC request and handles common response patterns
|
|
58
|
+
* Throws error for HTTP errors or JSON-RPC errors
|
|
59
|
+
*/
|
|
60
|
+
async makeJsonRpcRequest(method, params) {
|
|
61
|
+
const apiUrl = (0, network_1.networkToRpcUrl)(this.network);
|
|
62
|
+
const body = JSON.stringify({
|
|
63
|
+
jsonrpc: '2.0',
|
|
64
|
+
id: 1,
|
|
65
|
+
method,
|
|
66
|
+
params,
|
|
67
|
+
});
|
|
68
|
+
const res = await (0, node_fetch_1.default)(apiUrl, {
|
|
69
|
+
method: 'POST',
|
|
70
|
+
body,
|
|
71
|
+
headers: {
|
|
72
|
+
'Content-Type': 'application/json',
|
|
73
|
+
},
|
|
74
|
+
});
|
|
75
|
+
if (!res.ok) {
|
|
76
|
+
throw new Error(`HTTP ${res.status} - ${await res.text()}`);
|
|
77
|
+
}
|
|
78
|
+
const json = (await res.json());
|
|
79
|
+
if ('error' in json) {
|
|
80
|
+
throw new Error(this.parseJsonRpcError(json));
|
|
81
|
+
}
|
|
82
|
+
return json.result;
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Validates API key before making dapp API calls
|
|
86
|
+
*/
|
|
87
|
+
async validateApiKey() {
|
|
88
|
+
return await this.makeJsonRpcRequest('checkApiKey', [this.userId, this.apiKey]);
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Fetches dapp list from API
|
|
92
|
+
*/
|
|
93
|
+
async fetchDappList() {
|
|
94
|
+
return await this.makeJsonRpcRequest('getDappList', [this.userId, this.apiKey]);
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Fetches dapp versions/builds from API
|
|
98
|
+
*/
|
|
99
|
+
async fetchDappVersions(dappName) {
|
|
100
|
+
return await this.makeJsonRpcRequest('checkDappVersions', [this.userId, this.apiKey, dappName]);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Fetches JSON from an external URL
|
|
104
|
+
* Used for fetching metadata from meta_url
|
|
105
|
+
*/
|
|
106
|
+
async fetchJsonFromUrl(url) {
|
|
107
|
+
try {
|
|
108
|
+
const res = await (0, node_fetch_1.default)(url);
|
|
109
|
+
if (!res.ok) {
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
return await res.json();
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
return null;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Fetches metadata for builds
|
|
120
|
+
*/
|
|
121
|
+
async fetchMetaForBuilds(builds) {
|
|
122
|
+
const updatedBuilds = [];
|
|
123
|
+
for (const b of builds) {
|
|
124
|
+
let metaAuthor = 'N/A';
|
|
125
|
+
let metaIdentifier = 'N/A';
|
|
126
|
+
let metaMaintainer = 'N/A';
|
|
127
|
+
if (b.meta_url) {
|
|
128
|
+
const metaJson = await this.fetchJsonFromUrl(b.meta_url);
|
|
129
|
+
if (metaJson) {
|
|
130
|
+
if (metaJson.author)
|
|
131
|
+
metaAuthor = metaJson.author;
|
|
132
|
+
if (metaJson.identifier)
|
|
133
|
+
metaIdentifier = metaJson.identifier;
|
|
134
|
+
if (metaJson.maintainer)
|
|
135
|
+
metaMaintainer = metaJson.maintainer;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
updatedBuilds.push({
|
|
139
|
+
...b,
|
|
140
|
+
author: metaAuthor,
|
|
141
|
+
identifier: metaIdentifier,
|
|
142
|
+
maintainer: metaMaintainer,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return updatedBuilds;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* Fetches dapp details including builds with metadata
|
|
149
|
+
*/
|
|
150
|
+
async fetchDappDetails(dappName) {
|
|
151
|
+
const builds = await this.fetchDappVersions(dappName);
|
|
152
|
+
const buildsWithMeta = await this.fetchMetaForBuilds(builds);
|
|
153
|
+
// Sort by version (descending)
|
|
154
|
+
buildsWithMeta.sort((a, b) => {
|
|
155
|
+
const aVer = parseFloat(a.version || '0') || 0;
|
|
156
|
+
const bVer = parseFloat(b.version || '0') || 0;
|
|
157
|
+
return bVer - aVer;
|
|
158
|
+
});
|
|
159
|
+
return buildsWithMeta;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
exports.DappService = DappService;
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Logging service with chainable API support
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.log = log;
|
|
7
|
+
/**
|
|
8
|
+
* LogBuilder class for chainable logging
|
|
9
|
+
*/
|
|
10
|
+
class LogBuilder {
|
|
11
|
+
constructor(message) {
|
|
12
|
+
this.isBold = false;
|
|
13
|
+
this.isItalic = false;
|
|
14
|
+
this.color = null;
|
|
15
|
+
this.message = message;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Makes the message bold
|
|
19
|
+
*/
|
|
20
|
+
bold() {
|
|
21
|
+
this.isBold = true;
|
|
22
|
+
return this;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Makes the message italic
|
|
26
|
+
*/
|
|
27
|
+
italic() {
|
|
28
|
+
this.isItalic = true;
|
|
29
|
+
return this;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Sets the color to red and logs
|
|
33
|
+
*/
|
|
34
|
+
red() {
|
|
35
|
+
this.color = 'red';
|
|
36
|
+
this.execute();
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Sets the color to green and logs
|
|
40
|
+
*/
|
|
41
|
+
green() {
|
|
42
|
+
this.color = 'green';
|
|
43
|
+
this.execute();
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Sets the color to white and logs
|
|
47
|
+
*/
|
|
48
|
+
white() {
|
|
49
|
+
this.color = 'white';
|
|
50
|
+
this.execute();
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Executes the log with all formatting applied
|
|
54
|
+
*/
|
|
55
|
+
execute() {
|
|
56
|
+
const codes = [];
|
|
57
|
+
if (this.isBold)
|
|
58
|
+
codes.push('1');
|
|
59
|
+
if (this.isItalic)
|
|
60
|
+
codes.push('3');
|
|
61
|
+
if (this.color === 'red') {
|
|
62
|
+
codes.push('31');
|
|
63
|
+
}
|
|
64
|
+
else if (this.color === 'green') {
|
|
65
|
+
codes.push('32');
|
|
66
|
+
}
|
|
67
|
+
else if (this.color === 'white') {
|
|
68
|
+
// White is default, no color code needed
|
|
69
|
+
}
|
|
70
|
+
if (codes.length > 0) {
|
|
71
|
+
const escapeCode = '\x1b[' + codes.join(';') + 'm';
|
|
72
|
+
console.log(escapeCode + this.message + '\x1b[0m');
|
|
73
|
+
}
|
|
74
|
+
else {
|
|
75
|
+
console.log(this.message);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Creates a log builder for chainable logging
|
|
81
|
+
* Usage: log('message').italic().red()
|
|
82
|
+
*/
|
|
83
|
+
function log(message) {
|
|
84
|
+
return new LogBuilder(message);
|
|
85
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.PortManager = void 0;
|
|
4
4
|
const cryptoProcess_1 = require("../../util/cryptoProcess");
|
|
5
|
+
const LogService_1 = require("../log/LogService");
|
|
5
6
|
class PortManager {
|
|
6
7
|
/**
|
|
7
8
|
* Check if any of the required ports are in use
|
|
@@ -9,11 +10,11 @@ class PortManager {
|
|
|
9
10
|
static async checkPortConflicts(ports = this.DEFAULT_PORTS) {
|
|
10
11
|
const inUsePorts = await (0, cryptoProcess_1.checkPortsInUse)(ports);
|
|
11
12
|
if (inUsePorts.length > 0) {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
(0, LogService_1.log)('Port conflict detected!').red();
|
|
14
|
+
(0, LogService_1.log)(`The following ports are already in use: ${inUsePorts.join(', ')}`).red();
|
|
15
|
+
(0, LogService_1.log)('\nPlease kill the processes using these ports before running serve:').red();
|
|
15
16
|
const killCommand = `lsof -ti:${inUsePorts.join(',')} | xargs kill -9`;
|
|
16
|
-
|
|
17
|
+
(0, LogService_1.log)(` ${killCommand}`).red();
|
|
17
18
|
process.exit(1);
|
|
18
19
|
}
|
|
19
20
|
}
|
|
@@ -10,6 +10,7 @@ const cryptoProcess_1 = require("../../util/cryptoProcess");
|
|
|
10
10
|
const PortManager_1 = require("../port/PortManager");
|
|
11
11
|
const SimulatorService_1 = require("../simulator/SimulatorService");
|
|
12
12
|
const AirmoneyService_1 = require("../airmoney/AirmoneyService");
|
|
13
|
+
const LogService_1 = require("../log/LogService");
|
|
13
14
|
class ServeOrchestrator {
|
|
14
15
|
constructor(config) {
|
|
15
16
|
this.config = config;
|
|
@@ -40,7 +41,7 @@ class ServeOrchestrator {
|
|
|
40
41
|
await PortManager_1.PortManager.checkPortConflicts();
|
|
41
42
|
// Validate metadata
|
|
42
43
|
if (!this.metadata) {
|
|
43
|
-
|
|
44
|
+
(0, LogService_1.log)('No metadata found. Skipping some possible checks.').white();
|
|
44
45
|
}
|
|
45
46
|
// Start simulator service
|
|
46
47
|
await this.simulatorService.start();
|
|
@@ -10,6 +10,7 @@ const express_ws_1 = __importDefault(require("express-ws"));
|
|
|
10
10
|
const cors_1 = __importDefault(require("cors"));
|
|
11
11
|
const open_1 = __importDefault(require("open"));
|
|
12
12
|
const http_proxy_middleware_1 = require("http-proxy-middleware");
|
|
13
|
+
const LogService_1 = require("../log/LogService");
|
|
13
14
|
class BaseSimulatorService {
|
|
14
15
|
constructor(config) {
|
|
15
16
|
this.simulatorClient = null;
|
|
@@ -37,7 +38,7 @@ class BaseSimulatorService {
|
|
|
37
38
|
this.onClientConnectedCallback(this.getClient());
|
|
38
39
|
}
|
|
39
40
|
ws.on('message', (msg) => {
|
|
40
|
-
|
|
41
|
+
(0, LogService_1.log)(String(msg)).white();
|
|
41
42
|
});
|
|
42
43
|
ws.on('close', () => {
|
|
43
44
|
this.simulatorClient = null;
|
|
@@ -47,7 +48,7 @@ class BaseSimulatorService {
|
|
|
47
48
|
}
|
|
48
49
|
});
|
|
49
50
|
ws.on('error', (error) => {
|
|
50
|
-
|
|
51
|
+
(0, LogService_1.log)(`WebSocket error: ${error}`).red();
|
|
51
52
|
this.simulatorClient = null;
|
|
52
53
|
// Notify callback about disconnection due to error
|
|
53
54
|
if (this.onClientConnectedCallback) {
|
|
@@ -81,7 +82,7 @@ class BaseSimulatorService {
|
|
|
81
82
|
await (0, open_1.default)(url);
|
|
82
83
|
}
|
|
83
84
|
catch (err) {
|
|
84
|
-
|
|
85
|
+
(0, LogService_1.log)(`Failed to open web browser ${err}`).red();
|
|
85
86
|
}
|
|
86
87
|
}
|
|
87
88
|
}
|
|
@@ -114,7 +115,7 @@ class BaseSimulatorService {
|
|
|
114
115
|
return new Promise(resolve => {
|
|
115
116
|
this.server = this.app.listen(this.config.port, () => {
|
|
116
117
|
const url = `http://localhost:${this.config.port}/simulator`;
|
|
117
|
-
|
|
118
|
+
(0, LogService_1.log)(`Starting simulator server at ${url}`).green();
|
|
118
119
|
this.openBrowser();
|
|
119
120
|
resolve();
|
|
120
121
|
});
|
|
@@ -16,6 +16,7 @@ const path_1 = __importDefault(require("path"));
|
|
|
16
16
|
const child_process_1 = require("child_process");
|
|
17
17
|
const env_1 = require("./env");
|
|
18
18
|
const CryptoService_1 = require("../service/crypto/CryptoService");
|
|
19
|
+
const LogService_1 = require("../service/log/LogService");
|
|
19
20
|
// Utility functions for port management
|
|
20
21
|
function isPortInUse(port) {
|
|
21
22
|
return new Promise(resolve => {
|
|
@@ -102,29 +103,29 @@ function startCryptoService() {
|
|
|
102
103
|
}, (error, stdout, stderr) => {
|
|
103
104
|
// Ignore errors if the process was intentionally killed
|
|
104
105
|
if (error && !isKilled && !hasResolved) {
|
|
105
|
-
|
|
106
|
-
|
|
106
|
+
(0, LogService_1.log)('Crypto service failed to start:').red();
|
|
107
|
+
(0, LogService_1.log)(`Error details: ${error.message}`).red();
|
|
107
108
|
// Check for specific Rust panic errors
|
|
108
109
|
if (stderr &&
|
|
109
110
|
stderr.includes('Cannot drop a runtime in a context where blocking is not allowed')) {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
(0, LogService_1.log)('\n🔧 This appears to be a Rust/tokio runtime issue in the crypto service binary.').red();
|
|
112
|
+
(0, LogService_1.log)('Possible solutions:').red();
|
|
113
|
+
(0, LogService_1.log)('1. The binary may be corrupted - try rebuilding: npm run build').red();
|
|
114
|
+
(0, LogService_1.log)('2. There may be a compatibility issue with your system').red();
|
|
115
|
+
(0, LogService_1.log)('3. Try running the binary directly to see more details:').red();
|
|
116
|
+
(0, LogService_1.log)(` ${bin}`).red();
|
|
116
117
|
}
|
|
117
118
|
else if (stderr) {
|
|
118
|
-
|
|
119
|
+
(0, LogService_1.log)(`Stderr: ${stderr}`).red();
|
|
119
120
|
}
|
|
120
121
|
reject(new Error(`Crypto service failed: ${error.message}`));
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
123
124
|
if (stdout) {
|
|
124
|
-
//
|
|
125
|
+
// log(`Crypto service stdout: ${stdout}`).white();
|
|
125
126
|
}
|
|
126
127
|
if (stderr && !isKilled) {
|
|
127
|
-
|
|
128
|
+
(0, LogService_1.log)(`Crypto service stderr: ${stderr}`).red();
|
|
128
129
|
}
|
|
129
130
|
});
|
|
130
131
|
// Wait 2 seconds before resolving to ensure service is ready
|
|
@@ -153,10 +154,10 @@ function getCryptoServicePath() {
|
|
|
153
154
|
function startCryptoServiceSimple() {
|
|
154
155
|
const bin = getCryptoServicePath();
|
|
155
156
|
if (!(0, fs_1.existsSync)(bin)) {
|
|
156
|
-
|
|
157
|
+
(0, LogService_1.log)(`crypto service not found at ${bin}`).red();
|
|
157
158
|
return;
|
|
158
159
|
}
|
|
159
|
-
|
|
160
|
+
(0, LogService_1.log)('Starting crypto service at http://localhost:5050').green();
|
|
160
161
|
(0, child_process_1.exec)(bin, { env: { SECURE_STORAGE: (0, env_1.configDir)(), RUST_BACKTRACE: '1' } }, (_, stdout, stderr) => {
|
|
161
162
|
// console.log("stdout", stdout);
|
|
162
163
|
// console.log("stderr", stderr);
|
|
@@ -182,17 +183,17 @@ async function withCryptoService(operation) {
|
|
|
182
183
|
startedCryptoService = true;
|
|
183
184
|
}
|
|
184
185
|
catch (error) {
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
186
|
+
(0, LogService_1.log)('Failed to start crypto service:').red();
|
|
187
|
+
(0, LogService_1.log)('This might be due to:').red();
|
|
188
|
+
(0, LogService_1.log)('- Missing or corrupted binary files').red();
|
|
189
|
+
(0, LogService_1.log)('- Insufficient permissions').red();
|
|
190
|
+
(0, LogService_1.log)('- System compatibility issues').red();
|
|
191
|
+
(0, LogService_1.log)('- Port conflicts').red();
|
|
192
|
+
(0, LogService_1.log)('\nTry running: npm run build').red();
|
|
192
193
|
throw error;
|
|
193
194
|
}
|
|
194
195
|
const cryptoServiceClient = new CryptoService_1.CryptoService();
|
|
195
|
-
//
|
|
196
|
+
// log('Crypto service client created').white();
|
|
196
197
|
return await operation(cryptoServiceClient);
|
|
197
198
|
}
|
|
198
199
|
catch (error) {
|
package/dist/util/env.js
CHANGED
|
@@ -35,10 +35,15 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.configDir = configDir;
|
|
37
37
|
exports.loadEnvFromConfig = loadEnvFromConfig;
|
|
38
|
+
exports.getDeveloperAddress = getDeveloperAddress;
|
|
39
|
+
exports.getApiKey = getApiKey;
|
|
40
|
+
exports.getRpc = getRpc;
|
|
41
|
+
exports.validateCredential = validateCredential;
|
|
38
42
|
const fs = __importStar(require("fs"));
|
|
39
43
|
const path = __importStar(require("path"));
|
|
40
44
|
const os = __importStar(require("os"));
|
|
41
45
|
const dotenv = __importStar(require("dotenv"));
|
|
46
|
+
const network_1 = require("./network");
|
|
42
47
|
function configDir() {
|
|
43
48
|
// similar to Rust's ProjectDirs::from("fun", "air", "simulator");
|
|
44
49
|
// We'll do a rough approach
|
|
@@ -54,3 +59,42 @@ function loadEnvFromConfig() {
|
|
|
54
59
|
dotenv.config({ path: envPath });
|
|
55
60
|
}
|
|
56
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Gets the developer address from environment variables
|
|
64
|
+
*/
|
|
65
|
+
function getDeveloperAddress() {
|
|
66
|
+
return process.env.DEVELOPER_ADDRESS || '';
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Gets the API key from environment variables
|
|
70
|
+
*/
|
|
71
|
+
function getApiKey() {
|
|
72
|
+
return process.env.API_KEY || '';
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Gets the RPC/network from environment variables
|
|
76
|
+
*/
|
|
77
|
+
function getRpc() {
|
|
78
|
+
return process.env.RPC;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Validates that credentials exist and returns them
|
|
82
|
+
* Throws error if credentials are missing
|
|
83
|
+
*/
|
|
84
|
+
function validateCredential() {
|
|
85
|
+
const userId = getDeveloperAddress();
|
|
86
|
+
const apiKey = getApiKey();
|
|
87
|
+
const rpc = getRpc();
|
|
88
|
+
if (!userId || !apiKey) {
|
|
89
|
+
throw new Error('Missing credentials. Run "airmoney-cli setup" to configure your credentials.');
|
|
90
|
+
}
|
|
91
|
+
if ((0, network_1.validateNetwork)(rpc)) {
|
|
92
|
+
return {
|
|
93
|
+
userId,
|
|
94
|
+
apiKey,
|
|
95
|
+
rpc,
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
throw new Error('Invalid network. Must be "devnet" or "mainnet". Your current network is: ' +
|
|
99
|
+
rpc);
|
|
100
|
+
}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Formatting utilities for CLI output
|
|
4
|
+
*/
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.stripAnsiCodes = stripAnsiCodes;
|
|
7
|
+
exports.padString = padString;
|
|
8
|
+
exports.printTableRow = printTableRow;
|
|
9
|
+
exports.printSeparator = printSeparator;
|
|
10
|
+
exports.shortenString = shortenString;
|
|
11
|
+
const LogService_1 = require("../service/log/LogService");
|
|
12
|
+
/**
|
|
13
|
+
* Strips ANSI color codes from a string
|
|
14
|
+
*/
|
|
15
|
+
function stripAnsiCodes(str) {
|
|
16
|
+
// Remove ANSI escape codes (CSI escape sequences)
|
|
17
|
+
return str.replace(/\x1b\[[0-9;]*m/g, '');
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Formats a string to fixed width for table display
|
|
21
|
+
* Handles ANSI color codes by only counting visible characters for width calculation
|
|
22
|
+
*/
|
|
23
|
+
function padString(str, width) {
|
|
24
|
+
const s = String(str || 'N/A');
|
|
25
|
+
const visibleLength = stripAnsiCodes(s).length;
|
|
26
|
+
if (visibleLength > width) {
|
|
27
|
+
// Truncate the visible text, keeping color codes
|
|
28
|
+
const visibleText = stripAnsiCodes(s);
|
|
29
|
+
const truncated = visibleText.substring(0, width - 3) + '...';
|
|
30
|
+
// Try to preserve color codes at the beginning
|
|
31
|
+
const colorMatch = s.match(/^(\x1b\[[0-9;]*m)/);
|
|
32
|
+
const resetCode = '\x1b[0m';
|
|
33
|
+
if (colorMatch) {
|
|
34
|
+
return colorMatch[1] + truncated + resetCode;
|
|
35
|
+
}
|
|
36
|
+
return truncated;
|
|
37
|
+
}
|
|
38
|
+
// Pad with spaces, but only count visible characters
|
|
39
|
+
const padding = ' '.repeat(width - visibleLength);
|
|
40
|
+
return s + padding;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* Prints a table row
|
|
44
|
+
*/
|
|
45
|
+
function printTableRow(columns, widths, isHeader = false) {
|
|
46
|
+
const row = columns.map((col, i) => padString(col, widths[i])).join(' | ');
|
|
47
|
+
if (isHeader) {
|
|
48
|
+
(0, LogService_1.log)(row).bold().white();
|
|
49
|
+
}
|
|
50
|
+
else {
|
|
51
|
+
(0, LogService_1.log)(row).white();
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Prints a separator line
|
|
56
|
+
*/
|
|
57
|
+
function printSeparator(widths) {
|
|
58
|
+
const line = widths.map(w => '-'.repeat(w)).join('-|-');
|
|
59
|
+
(0, LogService_1.log)(line).white();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Shortens a string to show first N and last N characters with ellipsis
|
|
63
|
+
* @param str - The string to shorten
|
|
64
|
+
* @param maxLength - Maximum length before shortening (default: 14)
|
|
65
|
+
* @param prefixLength - Number of characters to show at start (default: 6)
|
|
66
|
+
* @param suffixLength - Number of characters to show at end (default: 6)
|
|
67
|
+
* @returns Shortened string or original if not long enough
|
|
68
|
+
*/
|
|
69
|
+
function shortenString(str, maxLength = 14, prefixLength = 6, suffixLength = 6) {
|
|
70
|
+
if (!str || str.length <= maxLength) {
|
|
71
|
+
return str;
|
|
72
|
+
}
|
|
73
|
+
return str.substring(0, prefixLength) + '…' + str.substring(str.length - suffixLength);
|
|
74
|
+
}
|