apow-cli 0.1.4 → 0.3.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/.env.example +6 -1
- package/README.md +21 -5
- package/dist/abi/MiningAgent.json +17 -0
- package/dist/bridge/debridge.js +128 -0
- package/dist/bridge/solana.js +129 -0
- package/dist/bridge/squid.js +128 -0
- package/dist/dashboard-html.js +321 -0
- package/dist/dashboard.js +424 -0
- package/dist/fund.js +313 -0
- package/dist/index.js +204 -0
- package/dist/smhl.js +1 -1
- package/package.json +3 -1
- package/skill.md +106 -18
package/dist/fund.js
ADDED
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// Fund command — bridge SOL → ETH on Base so Solana users can start mining.
|
|
3
|
+
// Three paths:
|
|
4
|
+
// Option A: Direct Solana signing via deBridge DLN (~20s)
|
|
5
|
+
// Option B: Deposit address + QR via Squid Router (~1-3 min)
|
|
6
|
+
// Option C: Manual send (just show Base address)
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.runFundFlow = runFundFlow;
|
|
42
|
+
const viem_1 = require("viem");
|
|
43
|
+
const wallet_1 = require("./wallet");
|
|
44
|
+
const ui = __importStar(require("./ui"));
|
|
45
|
+
async function fetchPrices() {
|
|
46
|
+
const res = await fetch("https://api.coingecko.com/api/v3/simple/price?ids=solana,ethereum&vs_currencies=usd");
|
|
47
|
+
if (!res.ok)
|
|
48
|
+
throw new Error("Failed to fetch prices from CoinGecko");
|
|
49
|
+
const data = (await res.json());
|
|
50
|
+
const ethUsd = data.ethereum.usd;
|
|
51
|
+
const solUsd = data.solana.usd;
|
|
52
|
+
return { solPerEth: ethUsd / solUsd, ethPriceUsd: ethUsd, solPriceUsd: solUsd };
|
|
53
|
+
}
|
|
54
|
+
/** SOL needed for target ETH, with 10% buffer for bridge fees + slippage. */
|
|
55
|
+
function solNeededForEth(targetEth, solPerEth) {
|
|
56
|
+
return targetEth * solPerEth * 1.1;
|
|
57
|
+
}
|
|
58
|
+
// ---------------------------------------------------------------------------
|
|
59
|
+
// QR code helper
|
|
60
|
+
// ---------------------------------------------------------------------------
|
|
61
|
+
async function showQrCode(text) {
|
|
62
|
+
try {
|
|
63
|
+
const mod = await Promise.resolve().then(() => __importStar(require("qrcode-terminal")));
|
|
64
|
+
const qrcode = mod.default || mod;
|
|
65
|
+
await new Promise((resolve) => {
|
|
66
|
+
qrcode.generate(text, { small: true }, (qr) => {
|
|
67
|
+
for (const line of qr.split("\n")) {
|
|
68
|
+
if (line)
|
|
69
|
+
console.log(` ${line}`);
|
|
70
|
+
}
|
|
71
|
+
resolve();
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
// QR is nice-to-have, not critical
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
// ---------------------------------------------------------------------------
|
|
80
|
+
// Option A — Direct signing via deBridge
|
|
81
|
+
// ---------------------------------------------------------------------------
|
|
82
|
+
async function runDirectBridge(solanaKeyInput, baseAddress, targetEth) {
|
|
83
|
+
const solanaSpinner = ui.spinner("Checking Solana balance...");
|
|
84
|
+
const solana = await Promise.resolve().then(() => __importStar(require("./bridge/solana")));
|
|
85
|
+
const debridge = await Promise.resolve().then(() => __importStar(require("./bridge/debridge")));
|
|
86
|
+
let kp;
|
|
87
|
+
try {
|
|
88
|
+
kp = await solana.parseSolanaKey(solanaKeyInput);
|
|
89
|
+
}
|
|
90
|
+
catch (err) {
|
|
91
|
+
solanaSpinner.fail("Invalid Solana key");
|
|
92
|
+
throw err;
|
|
93
|
+
}
|
|
94
|
+
const balance = await solana.getSolanaBalance(kp.publicKey);
|
|
95
|
+
solanaSpinner.stop(`Solana balance: ${balance.toFixed(4)} SOL`);
|
|
96
|
+
// Prices
|
|
97
|
+
const priceSpinner = ui.spinner("Fetching prices...");
|
|
98
|
+
const prices = await fetchPrices();
|
|
99
|
+
const solAmount = solNeededForEth(targetEth, prices.solPerEth);
|
|
100
|
+
priceSpinner.stop(`SOL/ETH rate: ${prices.solPerEth.toFixed(1)} SOL = 1 ETH`);
|
|
101
|
+
if (balance < solAmount) {
|
|
102
|
+
ui.error(`Insufficient SOL. Need ~${solAmount.toFixed(4)} SOL, have ${balance.toFixed(4)} SOL.`);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
console.log("");
|
|
106
|
+
ui.table([
|
|
107
|
+
["Bridging", `${solAmount.toFixed(4)} SOL → ~${targetEth.toFixed(4)} ETH on Base`],
|
|
108
|
+
["Via", "deBridge DLN (~20 seconds)"],
|
|
109
|
+
[
|
|
110
|
+
"From",
|
|
111
|
+
`${kp.publicKey.slice(0, 4)}...${kp.publicKey.slice(-4)}`,
|
|
112
|
+
],
|
|
113
|
+
["To", `${baseAddress.slice(0, 6)}...${baseAddress.slice(-4)}`],
|
|
114
|
+
]);
|
|
115
|
+
console.log("");
|
|
116
|
+
const proceed = await ui.confirm("Confirm bridge?");
|
|
117
|
+
if (!proceed) {
|
|
118
|
+
console.log(" Cancelled.");
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
const bridgeSpinner = ui.spinner("Signing bridge transaction...");
|
|
122
|
+
const result = await debridge.bridgeViaDeBridge(kp.keypair, baseAddress, solAmount);
|
|
123
|
+
bridgeSpinner.stop(`Submitted! Order: ${result.orderId.slice(0, 12)}...`);
|
|
124
|
+
const pollSpinner = ui.spinner("Waiting for bridge fulfillment... (~20s)");
|
|
125
|
+
const fulfillment = await debridge.pollOrderStatus(result.orderId, (status) => pollSpinner.update(`Bridge status: ${status}`));
|
|
126
|
+
const received = fulfillment.ethReceived || `~${targetEth.toFixed(4)}`;
|
|
127
|
+
pollSpinner.stop(`Bridge complete! ${received} ETH arrived on Base`);
|
|
128
|
+
console.log("");
|
|
129
|
+
console.log(` ${ui.green("Your wallet is funded!")} Next: ${ui.cyan("apow mint")}`);
|
|
130
|
+
console.log("");
|
|
131
|
+
}
|
|
132
|
+
// ---------------------------------------------------------------------------
|
|
133
|
+
// Options B+C — Deposit address via Squid
|
|
134
|
+
// ---------------------------------------------------------------------------
|
|
135
|
+
async function runDepositBridge(baseAddress, targetEth) {
|
|
136
|
+
const priceSpinner = ui.spinner("Fetching prices...");
|
|
137
|
+
const prices = await fetchPrices();
|
|
138
|
+
const solAmount = solNeededForEth(targetEth, prices.solPerEth);
|
|
139
|
+
priceSpinner.stop(`SOL/ETH rate: ${prices.solPerEth.toFixed(1)} SOL = 1 ETH`);
|
|
140
|
+
const addrSpinner = ui.spinner("Generating deposit address...");
|
|
141
|
+
const squid = await Promise.resolve().then(() => __importStar(require("./bridge/squid")));
|
|
142
|
+
const solana = await Promise.resolve().then(() => __importStar(require("./bridge/solana")));
|
|
143
|
+
let deposit;
|
|
144
|
+
try {
|
|
145
|
+
deposit = await squid.getDepositAddress(baseAddress, solAmount);
|
|
146
|
+
}
|
|
147
|
+
catch (err) {
|
|
148
|
+
addrSpinner.fail("Failed to get deposit address");
|
|
149
|
+
throw err;
|
|
150
|
+
}
|
|
151
|
+
addrSpinner.stop("Deposit address ready");
|
|
152
|
+
// Display deposit info
|
|
153
|
+
console.log("");
|
|
154
|
+
console.log(` ${ui.bold("Send SOL to this address:")}`);
|
|
155
|
+
console.log("");
|
|
156
|
+
console.log(` ${ui.cyan(deposit.depositAddress)}`);
|
|
157
|
+
console.log("");
|
|
158
|
+
await showQrCode(deposit.depositAddress);
|
|
159
|
+
console.log("");
|
|
160
|
+
ui.table([
|
|
161
|
+
[
|
|
162
|
+
"Amount",
|
|
163
|
+
`~${solAmount.toFixed(4)} SOL (~$${(solAmount * prices.solPriceUsd).toFixed(2)})`,
|
|
164
|
+
],
|
|
165
|
+
["You'll receive", `~${deposit.expectedReceive} ETH on Base`],
|
|
166
|
+
["Bridge", "Squid Router (Chainflip)"],
|
|
167
|
+
["Time", "~1-3 minutes"],
|
|
168
|
+
]);
|
|
169
|
+
console.log("");
|
|
170
|
+
if (deposit.expiresAt) {
|
|
171
|
+
ui.warn(`Deposit address expires: ${deposit.expiresAt}`);
|
|
172
|
+
console.log("");
|
|
173
|
+
}
|
|
174
|
+
// Poll for SOL deposit
|
|
175
|
+
const depositSpinner = ui.spinner("Waiting for SOL deposit... (Ctrl+C to cancel)");
|
|
176
|
+
const initialBalance = await solana.getAddressBalance(deposit.depositAddress);
|
|
177
|
+
let depositDetected = false;
|
|
178
|
+
const depositDeadline = Date.now() + 600_000; // 10 min
|
|
179
|
+
while (!depositDetected && Date.now() < depositDeadline) {
|
|
180
|
+
await new Promise((r) => setTimeout(r, 3000));
|
|
181
|
+
try {
|
|
182
|
+
const currentBalance = await solana.getAddressBalance(deposit.depositAddress);
|
|
183
|
+
if (currentBalance > initialBalance + 0.001) {
|
|
184
|
+
depositDetected = true;
|
|
185
|
+
depositSpinner.stop(`Deposit received! ${(currentBalance - initialBalance).toFixed(4)} SOL`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// Transient RPC error — keep polling
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
if (!depositDetected) {
|
|
193
|
+
depositSpinner.fail("No deposit detected after 10 minutes");
|
|
194
|
+
ui.hint("If you sent SOL, check: https://explorer.squidrouter.com");
|
|
195
|
+
return;
|
|
196
|
+
}
|
|
197
|
+
// Poll for bridge completion
|
|
198
|
+
const bridgeSpinner = ui.spinner("Bridging SOL → ETH on Base... (~1-3 min)");
|
|
199
|
+
const result = await squid.pollBridgeStatus(deposit.requestId, (status) => bridgeSpinner.update(`Bridge status: ${status}`));
|
|
200
|
+
const received = result.ethReceived || deposit.expectedReceive;
|
|
201
|
+
bridgeSpinner.stop(`Bridge complete! ${received} ETH arrived`);
|
|
202
|
+
console.log("");
|
|
203
|
+
console.log(` ${ui.green("Your wallet is funded!")} Next: ${ui.cyan("apow mint")}`);
|
|
204
|
+
console.log("");
|
|
205
|
+
}
|
|
206
|
+
// ---------------------------------------------------------------------------
|
|
207
|
+
// Option C — Manual Base address
|
|
208
|
+
// ---------------------------------------------------------------------------
|
|
209
|
+
async function runManualFund(baseAddress) {
|
|
210
|
+
console.log("");
|
|
211
|
+
console.log(` ${ui.bold("Send ETH on Base to this address:")}`);
|
|
212
|
+
console.log("");
|
|
213
|
+
console.log(` ${ui.cyan(baseAddress)}`);
|
|
214
|
+
console.log("");
|
|
215
|
+
await showQrCode(baseAddress);
|
|
216
|
+
console.log("");
|
|
217
|
+
console.log(` ${ui.dim("Send from any wallet — Coinbase, MetaMask, Phantom, etc.")}`);
|
|
218
|
+
console.log(` ${ui.dim("Need ~0.005 ETH to start mining.")}`);
|
|
219
|
+
console.log(` ${ui.dim("After sending, run:")} ${ui.cyan("apow mint")}`);
|
|
220
|
+
console.log("");
|
|
221
|
+
}
|
|
222
|
+
// ---------------------------------------------------------------------------
|
|
223
|
+
// Main entry point
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
async function runFundFlow(options) {
|
|
226
|
+
if (!wallet_1.account) {
|
|
227
|
+
ui.error("No wallet configured. Run `apow setup` first.");
|
|
228
|
+
process.exit(1);
|
|
229
|
+
}
|
|
230
|
+
const baseAddress = wallet_1.account.address;
|
|
231
|
+
const balance = await (0, wallet_1.getEthBalance)();
|
|
232
|
+
const ethBalance = Number((0, viem_1.formatEther)(balance));
|
|
233
|
+
console.log("");
|
|
234
|
+
ui.banner(["Fund Your Mining Wallet"]);
|
|
235
|
+
console.log("");
|
|
236
|
+
ui.table([
|
|
237
|
+
[
|
|
238
|
+
"Your Base wallet",
|
|
239
|
+
`${baseAddress.slice(0, 6)}...${baseAddress.slice(-4)}`,
|
|
240
|
+
],
|
|
241
|
+
[
|
|
242
|
+
"Balance",
|
|
243
|
+
`${ethBalance.toFixed(6)} ETH${ethBalance < 0.005 ? " (need ~0.005 ETH to start)" : ""}`,
|
|
244
|
+
],
|
|
245
|
+
]);
|
|
246
|
+
console.log("");
|
|
247
|
+
// Parse target ETH
|
|
248
|
+
const targetEth = options.amount ? parseFloat(options.amount) : 0.005;
|
|
249
|
+
if (isNaN(targetEth) || targetEth <= 0) {
|
|
250
|
+
ui.error("Invalid amount. Specify ETH target (e.g., --amount 0.005).");
|
|
251
|
+
return;
|
|
252
|
+
}
|
|
253
|
+
// --key flag: direct bridge immediately
|
|
254
|
+
if (options.key) {
|
|
255
|
+
await runDirectBridge(options.key, baseAddress, targetEth);
|
|
256
|
+
return;
|
|
257
|
+
}
|
|
258
|
+
// --solana flag: ask about key, then bridge
|
|
259
|
+
if (options.solana) {
|
|
260
|
+
const hasKey = await ui.confirm("Do you have your Solana private key?");
|
|
261
|
+
if (hasKey) {
|
|
262
|
+
const key = await ui.promptSecret("Solana private key (base58)");
|
|
263
|
+
if (!key) {
|
|
264
|
+
ui.error("No key provided.");
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
await runDirectBridge(key, baseAddress, targetEth);
|
|
268
|
+
}
|
|
269
|
+
else {
|
|
270
|
+
await runDepositBridge(baseAddress, targetEth);
|
|
271
|
+
}
|
|
272
|
+
return;
|
|
273
|
+
}
|
|
274
|
+
// Interactive menu
|
|
275
|
+
console.log(" How do you want to fund?");
|
|
276
|
+
console.log(` ${ui.cyan("1.")} Bridge from Solana (SOL → ETH on Base)`);
|
|
277
|
+
console.log(` ${ui.cyan("2.")} Send ETH on Base directly (from another wallet)`);
|
|
278
|
+
console.log(` ${ui.cyan("3.")} Copy address and fund manually`);
|
|
279
|
+
console.log("");
|
|
280
|
+
const choice = await ui.prompt("Choice", "1");
|
|
281
|
+
switch (choice) {
|
|
282
|
+
case "1": {
|
|
283
|
+
const hasKey = await ui.confirm("Do you have your Solana private key?");
|
|
284
|
+
if (hasKey) {
|
|
285
|
+
const key = await ui.promptSecret("Solana private key (base58)");
|
|
286
|
+
if (!key) {
|
|
287
|
+
ui.error("No key provided.");
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
290
|
+
await runDirectBridge(key, baseAddress, targetEth);
|
|
291
|
+
}
|
|
292
|
+
else {
|
|
293
|
+
await runDepositBridge(baseAddress, targetEth);
|
|
294
|
+
}
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
297
|
+
case "2": {
|
|
298
|
+
console.log("");
|
|
299
|
+
console.log(` ${ui.bold("Send ETH on Base to:")}`);
|
|
300
|
+
console.log(` ${ui.cyan(baseAddress)}`);
|
|
301
|
+
console.log("");
|
|
302
|
+
console.log(` ${ui.dim("Need ~0.005 ETH to start mining.")}`);
|
|
303
|
+
console.log(` ${ui.dim("After sending, run:")} ${ui.cyan("apow mint")}`);
|
|
304
|
+
console.log("");
|
|
305
|
+
break;
|
|
306
|
+
}
|
|
307
|
+
case "3":
|
|
308
|
+
default: {
|
|
309
|
+
await runManualFund(baseAddress);
|
|
310
|
+
break;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
package/dist/index.js
CHANGED
|
@@ -39,12 +39,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
40
|
const node_fs_1 = require("node:fs");
|
|
41
41
|
const node_path_1 = require("node:path");
|
|
42
|
+
const node_child_process_1 = require("node:child_process");
|
|
42
43
|
const commander_1 = require("commander");
|
|
43
44
|
const viem_1 = require("viem");
|
|
44
45
|
const MiningAgent_json_1 = __importDefault(require("./abi/MiningAgent.json"));
|
|
45
46
|
const config_1 = require("./config");
|
|
46
47
|
const detect_1 = require("./detect");
|
|
47
48
|
const explorer_1 = require("./explorer");
|
|
49
|
+
const fund_1 = require("./fund");
|
|
48
50
|
const mint_1 = require("./mint");
|
|
49
51
|
const miner_1 = require("./miner");
|
|
50
52
|
const preflight_1 = require("./preflight");
|
|
@@ -271,6 +273,18 @@ async function main() {
|
|
|
271
273
|
.action(async () => {
|
|
272
274
|
await setupWizard();
|
|
273
275
|
});
|
|
276
|
+
program
|
|
277
|
+
.command("fund")
|
|
278
|
+
.description("Fund your wallet — bridge SOL → ETH on Base, or show deposit address")
|
|
279
|
+
.option("--solana", "Bridge from Solana")
|
|
280
|
+
.option("--key <base58>", "Solana private key for direct signing")
|
|
281
|
+
.option("--amount <eth>", "Target ETH amount (default: 0.005)")
|
|
282
|
+
.hook("preAction", async () => {
|
|
283
|
+
await (0, preflight_1.runPreflight)("readonly");
|
|
284
|
+
})
|
|
285
|
+
.action(async (opts) => {
|
|
286
|
+
await (0, fund_1.runFundFlow)(opts);
|
|
287
|
+
});
|
|
274
288
|
program
|
|
275
289
|
.command("mint")
|
|
276
290
|
.description("Mint a new miner NFT")
|
|
@@ -457,8 +471,198 @@ async function main() {
|
|
|
457
471
|
console.log(` Tx: ${ui.dim((0, explorer_1.txUrl)(receipt.transactionHash))}`);
|
|
458
472
|
console.log("");
|
|
459
473
|
});
|
|
474
|
+
// --- Dashboard commands ---
|
|
475
|
+
const dashboardCmd = program
|
|
476
|
+
.command("dashboard")
|
|
477
|
+
.description("Multi-wallet mining dashboard");
|
|
478
|
+
dashboardCmd
|
|
479
|
+
.command("start", { isDefault: true })
|
|
480
|
+
.description("Launch the dashboard web UI")
|
|
481
|
+
.action(async () => {
|
|
482
|
+
const walletsPath = getWalletsPath();
|
|
483
|
+
// Seed wallets.json if it doesn't exist
|
|
484
|
+
if (!(0, node_fs_1.existsSync)(walletsPath)) {
|
|
485
|
+
const walletsDir = (0, node_path_1.join)(process.env.HOME ?? "", ".apow");
|
|
486
|
+
if (!(0, node_fs_1.existsSync)(walletsDir))
|
|
487
|
+
(0, node_fs_1.mkdirSync)(walletsDir, { recursive: true });
|
|
488
|
+
const initial = wallet_1.account ? [wallet_1.account.address] : [];
|
|
489
|
+
(0, node_fs_1.writeFileSync)(walletsPath, JSON.stringify(initial, null, 2), "utf8");
|
|
490
|
+
if (wallet_1.account) {
|
|
491
|
+
ui.ok(`Seeded ${walletsPath} with ${wallet_1.account.address.slice(0, 6)}...${wallet_1.account.address.slice(-4)}`);
|
|
492
|
+
}
|
|
493
|
+
else {
|
|
494
|
+
ui.ok(`Created ${walletsPath} (empty — add wallets with: apow dashboard add <address>)`);
|
|
495
|
+
}
|
|
496
|
+
}
|
|
497
|
+
// Auto-detect wallets from CWD
|
|
498
|
+
const { addresses, newCount } = detectWallets(process.cwd());
|
|
499
|
+
if (newCount > 0) {
|
|
500
|
+
ui.ok(`Detected ${addresses.length} wallets (${newCount} new)`);
|
|
501
|
+
}
|
|
502
|
+
else if (addresses.length > 0) {
|
|
503
|
+
console.log(` ${ui.dim(`${addresses.length} wallets loaded`)}`);
|
|
504
|
+
}
|
|
505
|
+
const { startDashboardServer } = await Promise.resolve().then(() => __importStar(require("./dashboard")));
|
|
506
|
+
console.log("");
|
|
507
|
+
console.log(` ${ui.bold("APoW Dashboard")} starting on http://localhost:3847`);
|
|
508
|
+
console.log(` ${ui.dim("Press Ctrl+C to stop")}`);
|
|
509
|
+
console.log("");
|
|
510
|
+
const server = startDashboardServer({
|
|
511
|
+
port: 3847,
|
|
512
|
+
walletsPath,
|
|
513
|
+
rpcUrl: config_1.config.rpcUrl,
|
|
514
|
+
miningAgentAddress: config_1.config.miningAgentAddress,
|
|
515
|
+
agentCoinAddress: config_1.config.agentCoinAddress,
|
|
516
|
+
});
|
|
517
|
+
// Open browser after short delay (server starts instantly)
|
|
518
|
+
setTimeout(() => {
|
|
519
|
+
const openCmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
520
|
+
(0, node_child_process_1.spawn)(openCmd, ["http://localhost:3847"], { stdio: "ignore" });
|
|
521
|
+
}, 500);
|
|
522
|
+
// Wait for SIGINT
|
|
523
|
+
await new Promise((resolve) => {
|
|
524
|
+
process.on("SIGINT", () => {
|
|
525
|
+
server.close();
|
|
526
|
+
resolve();
|
|
527
|
+
});
|
|
528
|
+
});
|
|
529
|
+
});
|
|
530
|
+
dashboardCmd
|
|
531
|
+
.command("add <address>")
|
|
532
|
+
.description("Add a wallet address to monitor")
|
|
533
|
+
.action((address) => {
|
|
534
|
+
if (!/^0x[0-9a-fA-F]{40}$/.test(address)) {
|
|
535
|
+
ui.error("Invalid address. Must be 0x + 40 hex characters.");
|
|
536
|
+
return;
|
|
537
|
+
}
|
|
538
|
+
const walletsPath = getWalletsPath();
|
|
539
|
+
const wallets = loadWallets(walletsPath);
|
|
540
|
+
const lower = address.toLowerCase();
|
|
541
|
+
if (wallets.some((w) => w.toLowerCase() === lower)) {
|
|
542
|
+
ui.warn("Address already monitored.");
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
wallets.push(address);
|
|
546
|
+
saveWallets(walletsPath, wallets);
|
|
547
|
+
ui.ok(`Added ${address.slice(0, 6)}...${address.slice(-4)} (${wallets.length} wallets total)`);
|
|
548
|
+
});
|
|
549
|
+
dashboardCmd
|
|
550
|
+
.command("remove <address>")
|
|
551
|
+
.description("Remove a wallet address from monitoring")
|
|
552
|
+
.action((address) => {
|
|
553
|
+
const walletsPath = getWalletsPath();
|
|
554
|
+
const wallets = loadWallets(walletsPath);
|
|
555
|
+
const lower = address.toLowerCase();
|
|
556
|
+
const filtered = wallets.filter((w) => w.toLowerCase() !== lower);
|
|
557
|
+
if (filtered.length === wallets.length) {
|
|
558
|
+
ui.warn("Address not found in wallet list.");
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
saveWallets(walletsPath, filtered);
|
|
562
|
+
ui.ok(`Removed ${address.slice(0, 6)}...${address.slice(-4)} (${filtered.length} wallets remaining)`);
|
|
563
|
+
});
|
|
564
|
+
dashboardCmd
|
|
565
|
+
.command("scan [dir]")
|
|
566
|
+
.description("Auto-detect wallets from wallet-0x*.txt files in a directory")
|
|
567
|
+
.action((dir) => {
|
|
568
|
+
const scanDir = dir ?? process.cwd();
|
|
569
|
+
const { addresses, newCount } = detectWallets(scanDir);
|
|
570
|
+
console.log("");
|
|
571
|
+
if (addresses.length === 0) {
|
|
572
|
+
console.log(` No wallets found in ${scanDir}`);
|
|
573
|
+
console.log(` ${ui.dim("Expected files named wallet-0x<address>.txt")}`);
|
|
574
|
+
}
|
|
575
|
+
else {
|
|
576
|
+
console.log(` ${ui.bold("Detected Wallets")} (${newCount} new, ${addresses.length} total)`);
|
|
577
|
+
console.log("");
|
|
578
|
+
for (const addr of addresses) {
|
|
579
|
+
console.log(` ${addr}`);
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
console.log("");
|
|
583
|
+
});
|
|
584
|
+
dashboardCmd
|
|
585
|
+
.command("wallets")
|
|
586
|
+
.description("List monitored wallet addresses")
|
|
587
|
+
.action(() => {
|
|
588
|
+
const walletsPath = getWalletsPath();
|
|
589
|
+
const wallets = loadWallets(walletsPath);
|
|
590
|
+
if (wallets.length === 0) {
|
|
591
|
+
console.log(" No wallets configured. Run: apow dashboard add <address>");
|
|
592
|
+
return;
|
|
593
|
+
}
|
|
594
|
+
console.log("");
|
|
595
|
+
console.log(` ${ui.bold("Monitored Wallets")} (${wallets.length})`);
|
|
596
|
+
console.log("");
|
|
597
|
+
for (const w of wallets) {
|
|
598
|
+
console.log(` ${w}`);
|
|
599
|
+
}
|
|
600
|
+
console.log("");
|
|
601
|
+
});
|
|
460
602
|
await program.parseAsync(process.argv);
|
|
461
603
|
}
|
|
604
|
+
function getWalletsPath() {
|
|
605
|
+
return (0, node_path_1.join)(process.env.HOME ?? "", ".apow", "wallets.json");
|
|
606
|
+
}
|
|
607
|
+
function loadWallets(path) {
|
|
608
|
+
try {
|
|
609
|
+
const raw = (0, node_fs_1.readFileSync)(path, "utf8");
|
|
610
|
+
const data = JSON.parse(raw);
|
|
611
|
+
return Array.isArray(data) ? data.filter((a) => typeof a === "string") : [];
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
return [];
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
function saveWallets(path, wallets) {
|
|
618
|
+
const dir = (0, node_path_1.join)(process.env.HOME ?? "", ".apow");
|
|
619
|
+
if (!(0, node_fs_1.existsSync)(dir))
|
|
620
|
+
(0, node_fs_1.mkdirSync)(dir, { recursive: true });
|
|
621
|
+
(0, node_fs_1.writeFileSync)(path, JSON.stringify(wallets, null, 2), "utf8");
|
|
622
|
+
}
|
|
623
|
+
function detectWallets(scanDir) {
|
|
624
|
+
const walletsPath = getWalletsPath();
|
|
625
|
+
const existing = loadWallets(walletsPath);
|
|
626
|
+
const seen = new Set(existing.map((a) => a.toLowerCase()));
|
|
627
|
+
const detected = [];
|
|
628
|
+
// Scan scanDir for wallet-0x*.txt files
|
|
629
|
+
try {
|
|
630
|
+
const entries = (0, node_fs_1.readdirSync)(scanDir, { withFileTypes: true });
|
|
631
|
+
for (const entry of entries) {
|
|
632
|
+
if (entry.isFile()) {
|
|
633
|
+
const match = entry.name.match(/^wallet-(0x[0-9a-fA-F]{40})\.txt$/);
|
|
634
|
+
if (match && !seen.has(match[1].toLowerCase())) {
|
|
635
|
+
detected.push(match[1]);
|
|
636
|
+
seen.add(match[1].toLowerCase());
|
|
637
|
+
}
|
|
638
|
+
}
|
|
639
|
+
// Scan rig*/wallet-0x*.txt subdirectories
|
|
640
|
+
if (entry.isDirectory() && entry.name.startsWith("rig")) {
|
|
641
|
+
try {
|
|
642
|
+
const rigFiles = (0, node_fs_1.readdirSync)((0, node_path_1.join)(scanDir, entry.name));
|
|
643
|
+
for (const file of rigFiles) {
|
|
644
|
+
const m = file.match(/^wallet-(0x[0-9a-fA-F]{40})\.txt$/);
|
|
645
|
+
if (m && !seen.has(m[1].toLowerCase())) {
|
|
646
|
+
detected.push(m[1]);
|
|
647
|
+
seen.add(m[1].toLowerCase());
|
|
648
|
+
}
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
catch {
|
|
652
|
+
// rig dir not readable — skip
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
catch {
|
|
658
|
+
// scanDir not readable
|
|
659
|
+
}
|
|
660
|
+
const merged = [...existing, ...detected];
|
|
661
|
+
if (detected.length > 0) {
|
|
662
|
+
saveWallets(walletsPath, merged);
|
|
663
|
+
}
|
|
664
|
+
return { addresses: merged, newCount: detected.length };
|
|
665
|
+
}
|
|
462
666
|
main().catch((error) => {
|
|
463
667
|
const message = error instanceof Error ? error.message : String(error);
|
|
464
668
|
ui.error(message);
|
package/dist/smhl.js
CHANGED
|
@@ -234,7 +234,7 @@ async function requestGeminiSolution(prompt) {
|
|
|
234
234
|
parts: [{ text: "You generate short lowercase word sequences that match exact constraints. Return only the words separated by spaces. Nothing else." }],
|
|
235
235
|
},
|
|
236
236
|
contents: [{ parts: [{ text: prompt }] }],
|
|
237
|
-
generationConfig: { temperature: 0.7 },
|
|
237
|
+
generationConfig: { temperature: 0.7, thinkingConfig: { thinkingBudget: 0 } },
|
|
238
238
|
}),
|
|
239
239
|
});
|
|
240
240
|
if (response.status === 429) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "apow-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "Mine AGENT tokens on Base L2 with AI-powered proof of work",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"apow",
|
|
@@ -39,9 +39,11 @@
|
|
|
39
39
|
"prepublishOnly": "npm run build"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
+
"@solana/web3.js": "^1.98.0",
|
|
42
43
|
"commander": "^14.0.0",
|
|
43
44
|
"dotenv": "^17.2.3",
|
|
44
45
|
"openai": "^6.6.0",
|
|
46
|
+
"qrcode-terminal": "^0.12.0",
|
|
45
47
|
"viem": "^2.38.2"
|
|
46
48
|
},
|
|
47
49
|
"devDependencies": {
|