@proofofprotocol/inscribe-mcp 0.1.0 → 0.2.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/package.json +1 -1
- package/src/cli/commands/init.js +193 -30
package/package.json
CHANGED
package/src/cli/commands/init.js
CHANGED
|
@@ -6,8 +6,11 @@
|
|
|
6
6
|
|
|
7
7
|
import { Command } from 'commander';
|
|
8
8
|
import { createInterface } from 'readline';
|
|
9
|
+
import { PrivateKey } from '@hashgraph/sdk';
|
|
9
10
|
import { writeConfig, configExists, getConfigPath } from '../lib/config.js';
|
|
10
11
|
import { EXIT_CODES } from '../lib/exit-codes.js';
|
|
12
|
+
import { exec } from 'child_process';
|
|
13
|
+
import { platform } from 'os';
|
|
11
14
|
|
|
12
15
|
// Simple prompt helper (no external dependency)
|
|
13
16
|
function prompt(question) {
|
|
@@ -34,6 +37,57 @@ const colors = {
|
|
|
34
37
|
dim: (s) => `\x1b[2m${s}\x1b[0m`
|
|
35
38
|
};
|
|
36
39
|
|
|
40
|
+
// Open URL in browser
|
|
41
|
+
function openBrowser(url) {
|
|
42
|
+
const os = platform();
|
|
43
|
+
let cmd;
|
|
44
|
+
if (os === 'darwin') {
|
|
45
|
+
cmd = `open "${url}"`;
|
|
46
|
+
} else if (os === 'win32') {
|
|
47
|
+
cmd = `start "" "${url}"`;
|
|
48
|
+
} else {
|
|
49
|
+
cmd = `xdg-open "${url}"`;
|
|
50
|
+
}
|
|
51
|
+
exec(cmd, () => {});
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Generate ECDSA key pair and EVM address
|
|
55
|
+
function generateKeyPair() {
|
|
56
|
+
const privateKey = PrivateKey.generateECDSA();
|
|
57
|
+
const publicKey = privateKey.publicKey;
|
|
58
|
+
|
|
59
|
+
// Get EVM address from public key
|
|
60
|
+
const evmAddress = publicKey.toEvmAddress();
|
|
61
|
+
|
|
62
|
+
return {
|
|
63
|
+
privateKey: privateKey.toStringDer(),
|
|
64
|
+
privateKeyRaw: privateKey.toStringRaw(),
|
|
65
|
+
publicKey: publicKey.toStringDer(),
|
|
66
|
+
evmAddress: `0x${evmAddress}`
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Fetch account ID from EVM address via Mirror Node
|
|
71
|
+
async function fetchAccountIdFromEvmAddress(evmAddress, network) {
|
|
72
|
+
const mirrorUrl = network === 'mainnet'
|
|
73
|
+
? 'https://mainnet.mirrornode.hedera.com'
|
|
74
|
+
: 'https://testnet.mirrornode.hedera.com';
|
|
75
|
+
|
|
76
|
+
// Remove 0x prefix if present
|
|
77
|
+
const address = evmAddress.startsWith('0x') ? evmAddress.slice(2) : evmAddress;
|
|
78
|
+
|
|
79
|
+
try {
|
|
80
|
+
const response = await fetch(`${mirrorUrl}/api/v1/accounts/${address}`);
|
|
81
|
+
if (response.ok) {
|
|
82
|
+
const data = await response.json();
|
|
83
|
+
return data.account;
|
|
84
|
+
}
|
|
85
|
+
} catch {
|
|
86
|
+
// Ignore errors
|
|
87
|
+
}
|
|
88
|
+
return null;
|
|
89
|
+
}
|
|
90
|
+
|
|
37
91
|
export const initCommand = new Command('init')
|
|
38
92
|
.description('Initialize inscribe-mcp configuration')
|
|
39
93
|
.option('-f, --force', 'Overwrite existing configuration')
|
|
@@ -90,53 +144,162 @@ export const initCommand = new Command('init')
|
|
|
90
144
|
console.log(colors.bold('2. Hedera Account'));
|
|
91
145
|
console.log('');
|
|
92
146
|
|
|
147
|
+
let accountId;
|
|
148
|
+
let privateKey;
|
|
149
|
+
|
|
93
150
|
if (network === 'testnet') {
|
|
94
|
-
|
|
95
|
-
console.log('
|
|
151
|
+
// Testnet: offer new or existing account
|
|
152
|
+
console.log(' 1) ' + colors.cyan('Create new account') + colors.dim(' (generate key + faucet)'));
|
|
153
|
+
console.log(' 2) ' + colors.dim('Use existing account'));
|
|
96
154
|
console.log('');
|
|
97
|
-
|
|
155
|
+
|
|
156
|
+
let accountChoice;
|
|
157
|
+
while (!accountChoice) {
|
|
158
|
+
const choice = await prompt('Select (1 or 2): ');
|
|
159
|
+
if (choice === '1') accountChoice = 'new';
|
|
160
|
+
else if (choice === '2') accountChoice = 'existing';
|
|
161
|
+
else console.log(colors.red('Invalid selection. Enter 1 or 2.'));
|
|
162
|
+
}
|
|
98
163
|
console.log('');
|
|
164
|
+
|
|
165
|
+
if (accountChoice === 'new') {
|
|
166
|
+
// Generate new key pair
|
|
167
|
+
console.log(colors.bold('Generating ECDSA key pair...'));
|
|
168
|
+
const keyPair = generateKeyPair();
|
|
169
|
+
console.log('');
|
|
170
|
+
console.log(colors.green('✓') + ' Key pair generated');
|
|
171
|
+
console.log('');
|
|
172
|
+
console.log(colors.bold('Your EVM Address:'));
|
|
173
|
+
console.log(colors.cyan(` ${keyPair.evmAddress}`));
|
|
174
|
+
console.log('');
|
|
175
|
+
|
|
176
|
+
privateKey = keyPair.privateKey;
|
|
177
|
+
|
|
178
|
+
// Open faucet
|
|
179
|
+
console.log(colors.bold('3. Get Free HBAR'));
|
|
180
|
+
console.log('');
|
|
181
|
+
console.log('Opening faucet in browser...');
|
|
182
|
+
console.log(colors.dim(' https://portal.hedera.com/faucet'));
|
|
183
|
+
console.log('');
|
|
184
|
+
openBrowser('https://portal.hedera.com/faucet');
|
|
185
|
+
|
|
186
|
+
console.log('Steps:');
|
|
187
|
+
console.log(' 1. Paste your EVM address above in the faucet');
|
|
188
|
+
console.log(' 2. Click "RECEIVE 100 TESTNET HBAR"');
|
|
189
|
+
console.log(' 3. Wait a few seconds for the account to be created');
|
|
190
|
+
console.log('');
|
|
191
|
+
|
|
192
|
+
await prompt('Press Enter when you have received HBAR...');
|
|
193
|
+
console.log('');
|
|
194
|
+
|
|
195
|
+
// Try to fetch account ID automatically
|
|
196
|
+
console.log('Looking up your Account ID...');
|
|
197
|
+
let retries = 3;
|
|
198
|
+
while (retries > 0 && !accountId) {
|
|
199
|
+
accountId = await fetchAccountIdFromEvmAddress(keyPair.evmAddress, network);
|
|
200
|
+
if (!accountId) {
|
|
201
|
+
retries--;
|
|
202
|
+
if (retries > 0) {
|
|
203
|
+
console.log(colors.dim(' Waiting for account creation...'));
|
|
204
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (accountId) {
|
|
210
|
+
console.log(colors.green('✓') + ` Account ID: ${accountId}`);
|
|
211
|
+
} else {
|
|
212
|
+
console.log(colors.yellow('Could not find Account ID automatically.'));
|
|
213
|
+
console.log('');
|
|
214
|
+
console.log('Check HashScan:');
|
|
215
|
+
console.log(colors.cyan(` https://hashscan.io/testnet/account/${keyPair.evmAddress.slice(2)}`));
|
|
216
|
+
console.log('');
|
|
217
|
+
while (!accountId) {
|
|
218
|
+
const input = await prompt('Enter your Account ID (e.g., 0.0.123456): ');
|
|
219
|
+
if (/^0\.0\.\d+$/.test(input)) {
|
|
220
|
+
accountId = input;
|
|
221
|
+
} else {
|
|
222
|
+
console.log(colors.red('Invalid format. Use 0.0.XXXXXX'));
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
console.log('');
|
|
227
|
+
|
|
228
|
+
} else {
|
|
229
|
+
// Existing account
|
|
230
|
+
console.log(colors.cyan('Enter your existing Hedera account details.'));
|
|
231
|
+
console.log('');
|
|
232
|
+
|
|
233
|
+
while (!accountId) {
|
|
234
|
+
const input = await prompt('Account ID (e.g., 0.0.123456): ');
|
|
235
|
+
if (/^0\.0\.\d+$/.test(input)) {
|
|
236
|
+
accountId = input;
|
|
237
|
+
} else {
|
|
238
|
+
console.log(colors.red('Invalid format. Use 0.0.XXXXXX'));
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
console.log(colors.green('✓') + ` Account ID: ${accountId}`);
|
|
243
|
+
console.log('');
|
|
244
|
+
|
|
245
|
+
// Private key input
|
|
246
|
+
console.log(colors.bold('3. Private Key'));
|
|
247
|
+
console.log('');
|
|
248
|
+
console.log(colors.dim('Your private key is stored locally and never transmitted.'));
|
|
249
|
+
console.log('');
|
|
250
|
+
|
|
251
|
+
while (!privateKey) {
|
|
252
|
+
const input = await prompt('Private Key (hex or DER format): ');
|
|
253
|
+
if (input.length >= 32) {
|
|
254
|
+
privateKey = input;
|
|
255
|
+
} else {
|
|
256
|
+
console.log(colors.red('Private key seems too short.'));
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
console.log(colors.green('✓') + ' Private key received');
|
|
261
|
+
console.log('');
|
|
262
|
+
}
|
|
263
|
+
|
|
99
264
|
} else {
|
|
265
|
+
// Mainnet: always use existing account
|
|
100
266
|
console.log(colors.cyan('Create Mainnet Account:'));
|
|
101
267
|
console.log(' HashPack: https://www.hashpack.app/');
|
|
102
268
|
console.log(' Blade: https://www.bladewallet.io/');
|
|
103
269
|
console.log('');
|
|
104
270
|
console.log('Transfer a small amount of HBAR (1-5 HBAR recommended).');
|
|
105
271
|
console.log('');
|
|
106
|
-
}
|
|
107
272
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
console.log(colors.red('Invalid format. Use 0.0.XXXXXX'));
|
|
273
|
+
while (!accountId) {
|
|
274
|
+
const input = await prompt('Account ID (e.g., 0.0.123456): ');
|
|
275
|
+
if (/^0\.0\.\d+$/.test(input)) {
|
|
276
|
+
accountId = input;
|
|
277
|
+
} else {
|
|
278
|
+
console.log(colors.red('Invalid format. Use 0.0.XXXXXX'));
|
|
279
|
+
}
|
|
116
280
|
}
|
|
117
|
-
}
|
|
118
281
|
|
|
119
|
-
|
|
120
|
-
|
|
282
|
+
console.log(colors.green('✓') + ` Account ID: ${accountId}`);
|
|
283
|
+
console.log('');
|
|
121
284
|
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
285
|
+
// Private key input
|
|
286
|
+
console.log(colors.bold('3. Private Key'));
|
|
287
|
+
console.log('');
|
|
288
|
+
console.log(colors.dim('Your private key is stored locally and never transmitted.'));
|
|
289
|
+
console.log('');
|
|
127
290
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
291
|
+
while (!privateKey) {
|
|
292
|
+
const input = await prompt('Private Key (hex or DER format): ');
|
|
293
|
+
if (input.length >= 32) {
|
|
294
|
+
privateKey = input;
|
|
295
|
+
} else {
|
|
296
|
+
console.log(colors.red('Private key seems too short.'));
|
|
297
|
+
}
|
|
135
298
|
}
|
|
136
|
-
}
|
|
137
299
|
|
|
138
|
-
|
|
139
|
-
|
|
300
|
+
console.log(colors.green('✓') + ' Private key received');
|
|
301
|
+
console.log('');
|
|
302
|
+
}
|
|
140
303
|
|
|
141
304
|
// Topic ID (optional)
|
|
142
305
|
console.log(colors.bold('4. Topic ID (optional)'));
|