@hardkas/cli 0.3.0-alpha → 0.5.0-alpha
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/bridge-local-runner-E3LICYYX.js +174 -0
- package/dist/dev-doctor-runner-LTSENPQ3.js +145 -0
- package/dist/dev-server-runner-IM5DCDD3.js +45 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +168 -27
- package/dist/kaspa-doctor-runner-F2ISPABM.js +81 -0
- package/dist/kaspa-wallet-runner-ORJ5YFKU.js +179 -0
- package/dist/local-wizard-runner-GCAWGZ6D.js +144 -0
- package/dist/metamask-runner-PRIOW4J3.js +132 -0
- package/dist/public.d.ts +9 -0
- package/dist/public.js +7 -0
- package/dist/session-runner-LDSGDP47.js +118 -0
- package/package.json +28 -17
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import {
|
|
2
|
+
handleError
|
|
3
|
+
} from "./chunk-K7XPWWIO.js";
|
|
4
|
+
|
|
5
|
+
// src/runners/kaspa-doctor-runner.ts
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
async function runKaspaDoctor(options) {
|
|
8
|
+
const checks = [];
|
|
9
|
+
let finalStatus = "ready";
|
|
10
|
+
try {
|
|
11
|
+
const { JsonWrpcKaspaClient } = await import("@hardkas/kaspa-rpc");
|
|
12
|
+
const client = new JsonWrpcKaspaClient({ rpcUrl: options.rpcUrl, timeoutMs: 3e3 });
|
|
13
|
+
try {
|
|
14
|
+
const info = await client.getInfo();
|
|
15
|
+
checks.push({ name: "RPC Reachability", status: "success", message: `Connected to kaspad ${info.serverVersion || ""}` });
|
|
16
|
+
if (info.isSynced) {
|
|
17
|
+
checks.push({ name: "Sync Status", status: "success", message: "Node is synced" });
|
|
18
|
+
} else {
|
|
19
|
+
checks.push({ name: "Sync Status", status: "warning", message: "Node is still syncing" });
|
|
20
|
+
if (finalStatus === "ready") finalStatus = "warning";
|
|
21
|
+
}
|
|
22
|
+
if (info.isUtxoIndexed) {
|
|
23
|
+
checks.push({ name: "UTXO Index", status: "success", message: "Indexed and searchable" });
|
|
24
|
+
} else {
|
|
25
|
+
checks.push({ name: "UTXO Index", status: "error", message: "UTXO index disabled (required for wallet balance/send)" });
|
|
26
|
+
finalStatus = "failed";
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
const dag = await client.getBlockDagInfo();
|
|
30
|
+
checks.push({ name: "DAG Status", status: "success", message: `Network: ${dag.networkId}, DAA Score: ${dag.virtualDaaScore}` });
|
|
31
|
+
} catch (e) {
|
|
32
|
+
checks.push({ name: "DAG Status", status: "error", message: `Failed to fetch DAG info: ${e.message}` });
|
|
33
|
+
finalStatus = "failed";
|
|
34
|
+
}
|
|
35
|
+
try {
|
|
36
|
+
const mempoolSize = info.mempoolSize ?? 0;
|
|
37
|
+
checks.push({ name: "Mempool", status: "success", message: `${mempoolSize} transactions pending` });
|
|
38
|
+
} catch (e) {
|
|
39
|
+
}
|
|
40
|
+
} catch (e) {
|
|
41
|
+
checks.push({ name: "RPC Reachability", status: "error", message: `Failed to connect: ${e.message}` });
|
|
42
|
+
finalStatus = "failed";
|
|
43
|
+
}
|
|
44
|
+
if (options.json) {
|
|
45
|
+
const result = {
|
|
46
|
+
schema: "hardkas.kaspaDoctor.v1",
|
|
47
|
+
status: finalStatus,
|
|
48
|
+
checks
|
|
49
|
+
};
|
|
50
|
+
console.log(JSON.stringify(result, null, 2));
|
|
51
|
+
if (finalStatus === "failed") process.exitCode = 1;
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
console.log(pc.bold("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
55
|
+
console.log(pc.bold(`HardKAS \u2022 Kaspa Doctor (L1)`));
|
|
56
|
+
console.log(pc.bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
57
|
+
for (const check of checks) {
|
|
58
|
+
const icon = check.status === "success" ? pc.green("\u2713") : check.status === "warning" ? pc.yellow("\u26A0") : pc.red("\u2717");
|
|
59
|
+
console.log(`${icon} ${pc.bold(check.name)}: ${check.message}`);
|
|
60
|
+
}
|
|
61
|
+
console.log(pc.bold("\nStatus: ") + (finalStatus === "ready" ? pc.green("READY") : finalStatus === "warning" ? pc.yellow("WARNING") : pc.red("FAILED")));
|
|
62
|
+
if (finalStatus === "failed") {
|
|
63
|
+
console.log(`
|
|
64
|
+
${pc.red("Fix recommendations:")}`);
|
|
65
|
+
if (checks.some((c) => c.name === "RPC Reachability" && c.status === "error")) {
|
|
66
|
+
console.log(` - Ensure kaspad is running with ${pc.white("--rpclisten-json")}.`);
|
|
67
|
+
console.log(` - Check if the port ${pc.white("16110")} is open.`);
|
|
68
|
+
}
|
|
69
|
+
if (checks.some((c) => c.name === "UTXO Index" && c.status === "error")) {
|
|
70
|
+
console.log(` - Start kaspad with ${pc.white("--utxoindex")}.`);
|
|
71
|
+
}
|
|
72
|
+
process.exitCode = 1;
|
|
73
|
+
}
|
|
74
|
+
} catch (e) {
|
|
75
|
+
process.exitCode = 1;
|
|
76
|
+
handleError(e);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export {
|
|
80
|
+
runKaspaDoctor
|
|
81
|
+
};
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UI,
|
|
3
|
+
handleError
|
|
4
|
+
} from "./chunk-K7XPWWIO.js";
|
|
5
|
+
|
|
6
|
+
// src/runners/kaspa-wallet-runner.ts
|
|
7
|
+
import pc from "picocolors";
|
|
8
|
+
import { loadHardkasConfig } from "@hardkas/config";
|
|
9
|
+
async function runKaspaWalletCreate(name, options) {
|
|
10
|
+
try {
|
|
11
|
+
const { createLocalKaspaWallet } = await import("@hardkas/accounts");
|
|
12
|
+
console.log(pc.bold("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
13
|
+
console.log(pc.bold(`HardKAS \u2022 Kaspa Wallet Creation`));
|
|
14
|
+
console.log(pc.bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
15
|
+
const wallet = await createLocalKaspaWallet({ networkId: options.network });
|
|
16
|
+
console.log(` ${pc.green("\u2713")} New Kaspa L1 wallet generated:`);
|
|
17
|
+
console.log(` Name: ${pc.white(name)}`);
|
|
18
|
+
console.log(` Address: ${pc.white(wallet.address)}`);
|
|
19
|
+
console.log(` Network: ${pc.white(options.network)}`);
|
|
20
|
+
console.log(`
|
|
21
|
+
${pc.yellow("Action Required:")} Add this to your ${pc.white("hardkas.config.ts")}:`);
|
|
22
|
+
console.log(pc.gray(" ----------------------------------------"));
|
|
23
|
+
console.log(pc.white(` accounts: {`));
|
|
24
|
+
console.log(pc.white(` ${name}: {`));
|
|
25
|
+
console.log(pc.white(` kind: "kaspa-private-key",`));
|
|
26
|
+
console.log(pc.white(` address: "${wallet.address}",`));
|
|
27
|
+
console.log(pc.white(` privateKeyEnv: "${name.toUpperCase()}_PRIVATE_KEY"`));
|
|
28
|
+
console.log(pc.white(` }`));
|
|
29
|
+
console.log(pc.white(` }`));
|
|
30
|
+
console.log(pc.gray(" ----------------------------------------"));
|
|
31
|
+
console.log(`
|
|
32
|
+
${pc.dim("Set your private key in .env:")}`);
|
|
33
|
+
console.log(` ${name.toUpperCase()}_PRIVATE_KEY=${wallet.privateKey}
|
|
34
|
+
`);
|
|
35
|
+
console.log(`${pc.dim("HardKAS never auto-writes secrets for your protection.")}`);
|
|
36
|
+
} catch (e) {
|
|
37
|
+
handleError(e);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
async function runKaspaWalletList(options) {
|
|
41
|
+
try {
|
|
42
|
+
const config = await loadHardkasConfig();
|
|
43
|
+
const { listHardkasAccounts } = await import("@hardkas/accounts");
|
|
44
|
+
const accounts = listHardkasAccounts(config.config).filter((a) => a.kind === "kaspa-private-key" || a.kind === "simulated");
|
|
45
|
+
if (options.json) {
|
|
46
|
+
console.log(JSON.stringify(accounts, null, 2));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
console.log(pc.bold("\nLocal Kaspa Wallets"));
|
|
50
|
+
console.log(pc.dim("----------------------------------------"));
|
|
51
|
+
for (const acc of accounts) {
|
|
52
|
+
console.log(`${pc.white(acc.name.padEnd(12))} ${pc.cyan(acc.address?.padEnd(40))} ${pc.dim(`(${acc.kind})`)}`);
|
|
53
|
+
}
|
|
54
|
+
console.log("");
|
|
55
|
+
} catch (e) {
|
|
56
|
+
handleError(e);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
async function runKaspaWalletAddress(name) {
|
|
60
|
+
try {
|
|
61
|
+
const config = await loadHardkasConfig();
|
|
62
|
+
const { resolveHardkasAccountAddress } = await import("@hardkas/accounts");
|
|
63
|
+
const address = resolveHardkasAccountAddress(name, config.config);
|
|
64
|
+
console.log(address);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
handleError(e);
|
|
67
|
+
process.exitCode = 1;
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async function runKaspaWalletBalance(name, options) {
|
|
71
|
+
try {
|
|
72
|
+
const config = await loadHardkasConfig();
|
|
73
|
+
const { resolveHardkasAccountAddress } = await import("@hardkas/accounts");
|
|
74
|
+
const { JsonWrpcKaspaClient } = await import("@hardkas/kaspa-rpc");
|
|
75
|
+
const address = resolveHardkasAccountAddress(name, config.config);
|
|
76
|
+
const client = new JsonWrpcKaspaClient({ rpcUrl: options.rpcUrl });
|
|
77
|
+
const balance = await client.getBalanceByAddress(address);
|
|
78
|
+
if (options.json) {
|
|
79
|
+
console.log(JSON.stringify(balance, (k, v) => typeof v === "bigint" ? v.toString() : v, 2));
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
console.log(`
|
|
83
|
+
Wallet: ${pc.white(name)}`);
|
|
84
|
+
console.log(`Address: ${pc.dim(address)}`);
|
|
85
|
+
console.log(`Balance: ${pc.green(Number(balance.balanceSompi) / 1e8)} KAS
|
|
86
|
+
`);
|
|
87
|
+
} catch (e) {
|
|
88
|
+
handleError(e);
|
|
89
|
+
process.exitCode = 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
async function runKaspaWalletSend(from, to, options) {
|
|
93
|
+
try {
|
|
94
|
+
const config = await loadHardkasConfig();
|
|
95
|
+
const { resolveHardkasAccount, resolveHardkasAccountAddress } = await import("@hardkas/accounts");
|
|
96
|
+
const { JsonWrpcKaspaClient } = await import("@hardkas/kaspa-rpc");
|
|
97
|
+
const { buildPaymentPlan } = await import("@hardkas/tx-builder");
|
|
98
|
+
const { signTxPlanArtifact } = await import("@hardkas/accounts");
|
|
99
|
+
const { HARDKAS_VERSION } = await import("@hardkas/artifacts");
|
|
100
|
+
const sender = resolveHardkasAccount({ nameOrAddress: from, config: config.config });
|
|
101
|
+
const targetAddress = resolveHardkasAccountAddress(to, config.config);
|
|
102
|
+
const amountSompi = BigInt(Math.floor(parseFloat(options.amount) * 1e8));
|
|
103
|
+
const client = new JsonWrpcKaspaClient({ rpcUrl: options.rpcUrl });
|
|
104
|
+
const utxos = await client.getUtxosByAddress(sender.address);
|
|
105
|
+
const plan = buildPaymentPlan({
|
|
106
|
+
fromAddress: sender.address,
|
|
107
|
+
outputs: [{ address: targetAddress, amountSompi }],
|
|
108
|
+
availableUtxos: utxos.map((u) => ({
|
|
109
|
+
outpoint: u.outpoint,
|
|
110
|
+
address: u.address,
|
|
111
|
+
amountSompi: u.amountSompi,
|
|
112
|
+
scriptPublicKey: u.scriptPublicKey || ""
|
|
113
|
+
})),
|
|
114
|
+
feeRateSompiPerMass: 1n,
|
|
115
|
+
// Default 1 sompi/mass
|
|
116
|
+
changeAddress: sender.address
|
|
117
|
+
});
|
|
118
|
+
console.log(pc.bold("\nTransaction Plan"));
|
|
119
|
+
console.log(pc.dim("----------------------------------------"));
|
|
120
|
+
console.log(`From: ${pc.white(from)} (${sender.address})`);
|
|
121
|
+
console.log(`To: ${pc.white(to)} (${targetAddress})`);
|
|
122
|
+
console.log(`Amount: ${pc.green(Number(amountSompi) / 1e8)} KAS`);
|
|
123
|
+
console.log(`Fee: ${pc.yellow(Number(plan.estimatedFeeSompi) / 1e8)} KAS`);
|
|
124
|
+
console.log(`Mass: ${plan.estimatedMass}`);
|
|
125
|
+
console.log(`Inputs: ${plan.inputs.length} UTXOs`);
|
|
126
|
+
console.log(pc.dim("----------------------------------------\n"));
|
|
127
|
+
if (options.dryRun) {
|
|
128
|
+
console.log(pc.blue("Dry-run mode: Transaction NOT signed or broadcast.\n"));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
const confirm = await UI.confirm(`Proceed with signing and broadcasting this transaction?`);
|
|
132
|
+
if (!confirm) {
|
|
133
|
+
console.log(pc.red("Cancelled.\n"));
|
|
134
|
+
return;
|
|
135
|
+
}
|
|
136
|
+
const planArtifact = {
|
|
137
|
+
schema: "hardkas.txPlan",
|
|
138
|
+
planId: `plan-${Date.now()}`,
|
|
139
|
+
hardkasVersion: HARDKAS_VERSION,
|
|
140
|
+
version: "1.0.0-alpha",
|
|
141
|
+
networkId: config.config.networkId || config.config.defaultNetwork || "simnet",
|
|
142
|
+
mode: sender.kind === "simulated" ? "simulated" : "real",
|
|
143
|
+
from: { address: sender.address },
|
|
144
|
+
to: { address: targetAddress },
|
|
145
|
+
amountSompi,
|
|
146
|
+
inputs: plan.inputs,
|
|
147
|
+
outputs: plan.outputs,
|
|
148
|
+
change: plan.change,
|
|
149
|
+
estimatedFeeSompi: plan.estimatedFeeSompi
|
|
150
|
+
};
|
|
151
|
+
const signedArtifact = await signTxPlanArtifact({
|
|
152
|
+
planArtifact,
|
|
153
|
+
account: sender,
|
|
154
|
+
config: config.config
|
|
155
|
+
});
|
|
156
|
+
console.log(` ${pc.green("\u2713")} Transaction signed.`);
|
|
157
|
+
const submitResult = await client.submitTransaction(signedArtifact.signedTransaction.payload);
|
|
158
|
+
if (submitResult.accepted) {
|
|
159
|
+
console.log(` ${pc.green("\u2713")} Transaction accepted by node.`);
|
|
160
|
+
console.log(` TXID: ${pc.bold(pc.white(submitResult.transactionId))}
|
|
161
|
+
`);
|
|
162
|
+
} else {
|
|
163
|
+
console.log(` ${pc.red("\u2717")} Transaction rejected by node.`);
|
|
164
|
+
console.log(` Details: ${JSON.stringify(submitResult.raw)}
|
|
165
|
+
`);
|
|
166
|
+
process.exitCode = 1;
|
|
167
|
+
}
|
|
168
|
+
} catch (e) {
|
|
169
|
+
handleError(e);
|
|
170
|
+
process.exitCode = 1;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
export {
|
|
174
|
+
runKaspaWalletAddress,
|
|
175
|
+
runKaspaWalletBalance,
|
|
176
|
+
runKaspaWalletCreate,
|
|
177
|
+
runKaspaWalletList,
|
|
178
|
+
runKaspaWalletSend
|
|
179
|
+
};
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import {
|
|
2
|
+
UI,
|
|
3
|
+
handleError
|
|
4
|
+
} from "./chunk-K7XPWWIO.js";
|
|
5
|
+
|
|
6
|
+
// src/runners/local-wizard-runner.ts
|
|
7
|
+
import pc from "picocolors";
|
|
8
|
+
import { loadHardkasConfig } from "@hardkas/config";
|
|
9
|
+
async function runLocalWizard(options) {
|
|
10
|
+
try {
|
|
11
|
+
const config = await loadHardkasConfig();
|
|
12
|
+
const networkId = config.config.networkId || config.config.defaultNetwork || "simnet";
|
|
13
|
+
const { getL2NetworkProfile, EvmJsonRpcClient } = await import("@hardkas/l2");
|
|
14
|
+
const { listHardkasAccounts } = await import("@hardkas/accounts");
|
|
15
|
+
if (!options.json) {
|
|
16
|
+
console.log(pc.bold("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
17
|
+
console.log(pc.bold(`HardKAS \u2022 Local Dev Wizard`));
|
|
18
|
+
console.log(pc.bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
19
|
+
}
|
|
20
|
+
const result = {
|
|
21
|
+
schema: "hardkas.localWizard.v1",
|
|
22
|
+
step: "preflight",
|
|
23
|
+
status: "pending"
|
|
24
|
+
};
|
|
25
|
+
if (!options.json) console.log(`${pc.cyan(pc.bold("1. Checking Environment"))}`);
|
|
26
|
+
let profile;
|
|
27
|
+
try {
|
|
28
|
+
profile = await getL2NetworkProfile({
|
|
29
|
+
name: options.profile,
|
|
30
|
+
userProfiles: config.config.l2?.networks,
|
|
31
|
+
cliOverrides: {
|
|
32
|
+
rpcUrl: options.rpcUrl || (networkId === "simnet" || networkId === "localnet" ? "http://127.0.0.1:8545" : void 0),
|
|
33
|
+
chainId: networkId === "simnet" || networkId === "localnet" ? 19416 : void 0
|
|
34
|
+
}
|
|
35
|
+
});
|
|
36
|
+
const client2 = new EvmJsonRpcClient({ url: profile.rpcUrl, timeoutMs: 2e3 });
|
|
37
|
+
await client2.getBlockNumber();
|
|
38
|
+
if (!options.json) console.log(` ${pc.green("\u2713")} Igra RPC is reachable (${profile.rpcUrl})`);
|
|
39
|
+
} catch (e) {
|
|
40
|
+
if (!options.json) {
|
|
41
|
+
console.log(` ${pc.red("\u2717")} Igra RPC is unreachable.`);
|
|
42
|
+
console.log(` ${pc.dim("Please start your local node first.")}`);
|
|
43
|
+
}
|
|
44
|
+
result.status = "failed";
|
|
45
|
+
result.suggestion = "Start local Igra node";
|
|
46
|
+
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
result.step = "accounts";
|
|
50
|
+
if (!options.json) console.log(`
|
|
51
|
+
${pc.cyan(pc.bold("2. Checking Accounts"))}`);
|
|
52
|
+
const accounts = listHardkasAccounts(config.config);
|
|
53
|
+
let targetAccount = accounts.find((a) => a.name === options.account && a.kind === "evm-private-key");
|
|
54
|
+
if (!targetAccount) {
|
|
55
|
+
if (options.nonInteractive) {
|
|
56
|
+
result.status = "failed";
|
|
57
|
+
result.suggestion = `Account "${options.account}" missing. Run in interactive mode to generate.`;
|
|
58
|
+
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
59
|
+
else console.log(` ${pc.red("\u2717")} Account missing (non-interactive mode).`);
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
if (!options.json) console.log(` ${pc.yellow("\u26A0")} No EVM account named "${options.account}" found.`);
|
|
63
|
+
const confirm = await UI.confirm(`Would you like to generate a new local EVM account for "${options.account}"?`);
|
|
64
|
+
if (confirm) {
|
|
65
|
+
const { generatePrivateKey, privateKeyToAccount } = await import("viem/accounts");
|
|
66
|
+
const privKey = generatePrivateKey();
|
|
67
|
+
const account = privateKeyToAccount(privKey);
|
|
68
|
+
if (!options.json) {
|
|
69
|
+
console.log(`
|
|
70
|
+
${pc.bgYellow(pc.black(pc.bold(" SECURITY ACTION: PRIVATE KEY GENERATED ")))}`);
|
|
71
|
+
console.log(` ${pc.yellow("This key is for LOCAL DEVELOPMENT ONLY. Never use it for real assets.")}
|
|
72
|
+
`);
|
|
73
|
+
console.log(` ${pc.green("\u2713")} New account generated:`);
|
|
74
|
+
console.log(` Name: ${pc.white(options.account)}`);
|
|
75
|
+
console.log(` Address: ${pc.white(account.address)}`);
|
|
76
|
+
console.log(`
|
|
77
|
+
${pc.yellow("Action Required:")} Add this to your ${pc.white("hardkas.config.ts")}:`);
|
|
78
|
+
console.log(pc.gray(" ----------------------------------------"));
|
|
79
|
+
console.log(pc.white(` accounts: {`));
|
|
80
|
+
console.log(pc.white(` ${options.account}: {`));
|
|
81
|
+
console.log(pc.white(` kind: "evm-private-key",`));
|
|
82
|
+
console.log(pc.white(` address: "${account.address}",`));
|
|
83
|
+
console.log(pc.white(` privateKey: "${privKey}"`));
|
|
84
|
+
console.log(pc.white(` }`));
|
|
85
|
+
console.log(pc.white(` }`));
|
|
86
|
+
console.log(pc.gray(" ----------------------------------------"));
|
|
87
|
+
console.log(`
|
|
88
|
+
${pc.dim("HardKAS does not automatically write secrets to your config for safety.")}`);
|
|
89
|
+
console.log(` ${pc.dim("Please update your config and run the wizard again to finish setup.")}`);
|
|
90
|
+
}
|
|
91
|
+
result.status = "pending";
|
|
92
|
+
result.accountCreated = true;
|
|
93
|
+
result.suggestion = "Update hardkas.config.ts with generated key";
|
|
94
|
+
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
95
|
+
return;
|
|
96
|
+
} else {
|
|
97
|
+
if (!options.json) console.log(` ${pc.red("\u2717")} Setup cancelled.`);
|
|
98
|
+
result.status = "failed";
|
|
99
|
+
if (options.json) console.log(JSON.stringify(result, null, 2));
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
} else {
|
|
103
|
+
if (!options.json) console.log(` ${pc.green("\u2713")} Account "${targetAccount.name}" found (${targetAccount.address})`);
|
|
104
|
+
}
|
|
105
|
+
result.step = "balance";
|
|
106
|
+
if (!options.json) console.log(`
|
|
107
|
+
${pc.cyan(pc.bold("3. Checking Balance"))}`);
|
|
108
|
+
const client = new EvmJsonRpcClient({ url: profile.rpcUrl });
|
|
109
|
+
const balance = await client.getBalanceWei(targetAccount.address);
|
|
110
|
+
const kasBalance = Number(balance) / 1e18;
|
|
111
|
+
if (kasBalance === 0) {
|
|
112
|
+
if (!options.json) {
|
|
113
|
+
console.log(` ${pc.yellow("\u26A0")} Account has 0 iKAS.`);
|
|
114
|
+
console.log(` ${pc.dim("Please fund your account manually or via local faucet.")}`);
|
|
115
|
+
}
|
|
116
|
+
result.status = "failed";
|
|
117
|
+
result.suggestion = "Fund local account";
|
|
118
|
+
} else {
|
|
119
|
+
if (!options.json) console.log(` ${pc.green("\u2713")} Balance: ${kasBalance} iKAS`);
|
|
120
|
+
}
|
|
121
|
+
result.status = kasBalance > 0 ? "success" : "failed";
|
|
122
|
+
if (options.json) {
|
|
123
|
+
console.log(JSON.stringify(result, null, 2));
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
if (result.status === "success") {
|
|
127
|
+
console.log(`
|
|
128
|
+
${pc.cyan(pc.bold("4. MetaMask Onboarding"))}`);
|
|
129
|
+
console.log(` ${pc.dim("To add this network to MetaMask, run:")}`);
|
|
130
|
+
console.log(` ${pc.white("hardkas metamask snippet")}`);
|
|
131
|
+
console.log(`
|
|
132
|
+
${pc.bold(pc.green("READY!"))} Local environment is set up.`);
|
|
133
|
+
console.log(`
|
|
134
|
+
${pc.cyan("Final step:")} Import your account into MetaMask:`);
|
|
135
|
+
console.log(` hardkas metamask account ${pc.white(options.account)} --show-private-key
|
|
136
|
+
`);
|
|
137
|
+
}
|
|
138
|
+
} catch (e) {
|
|
139
|
+
handleError(e);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
export {
|
|
143
|
+
runLocalWizard
|
|
144
|
+
};
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import {
|
|
2
|
+
handleError
|
|
3
|
+
} from "./chunk-K7XPWWIO.js";
|
|
4
|
+
|
|
5
|
+
// src/runners/metamask-runner.ts
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
import { loadHardkasConfig } from "@hardkas/config";
|
|
8
|
+
async function runMetamaskNetwork(options) {
|
|
9
|
+
try {
|
|
10
|
+
const config = await loadHardkasConfig();
|
|
11
|
+
const networkId = config.config.networkId || config.config.defaultNetwork || "simnet";
|
|
12
|
+
if (networkId === "mainnet" || networkId.startsWith("testnet")) {
|
|
13
|
+
throw new Error(`MetaMask onboarding is only allowed on local development networks (simnet, localnet). Current network: ${networkId}`);
|
|
14
|
+
}
|
|
15
|
+
const { getL2NetworkProfile } = await import("@hardkas/l2");
|
|
16
|
+
const { generateAddEthereumChainPayload } = await import("@hardkas/l2");
|
|
17
|
+
const profile = await getL2NetworkProfile({
|
|
18
|
+
name: options.profile,
|
|
19
|
+
userProfiles: config.config.l2?.networks,
|
|
20
|
+
cliOverrides: {
|
|
21
|
+
rpcUrl: networkId === "simnet" || networkId === "localnet" ? "http://127.0.0.1:8545" : void 0,
|
|
22
|
+
chainId: networkId === "simnet" || networkId === "localnet" ? 19416 : void 0
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
const payload = generateAddEthereumChainPayload(profile);
|
|
26
|
+
if (options.json) {
|
|
27
|
+
console.log(JSON.stringify(payload, null, 2));
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
console.log(pc.bold("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
31
|
+
console.log(pc.bold(`HardKAS \u2022 MetaMask Network`));
|
|
32
|
+
console.log(pc.bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
33
|
+
console.log(`${pc.cyan(pc.bold("Network Details"))}`);
|
|
34
|
+
console.log(` Name: ${pc.white(payload.chainName)}`);
|
|
35
|
+
console.log(` RPC URL: ${pc.white(payload.rpcUrls[0])}`);
|
|
36
|
+
console.log(` Chain ID: ${pc.white(parseInt(payload.chainId, 16))} (${payload.chainId})`);
|
|
37
|
+
console.log(` Currency: ${pc.white(payload.nativeCurrency.symbol)} (${payload.nativeCurrency.name})`);
|
|
38
|
+
if (payload.blockExplorerUrls) {
|
|
39
|
+
console.log(` Explorer: ${pc.white(payload.blockExplorerUrls[0])}`);
|
|
40
|
+
}
|
|
41
|
+
console.log(`
|
|
42
|
+
${pc.yellow("To add this network to MetaMask:")}`);
|
|
43
|
+
console.log(` 1. Open MetaMask settings`);
|
|
44
|
+
console.log(` 2. Go to Networks -> Add a network -> Add a network manually`);
|
|
45
|
+
console.log(` 3. Fill in the details above.
|
|
46
|
+
`);
|
|
47
|
+
console.log(`${pc.dim("Alternatively, run: ")} ${pc.cyan("hardkas metamask snippet")}
|
|
48
|
+
`);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
handleError(e);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
async function runMetamaskSnippet(options) {
|
|
54
|
+
try {
|
|
55
|
+
const config = await loadHardkasConfig();
|
|
56
|
+
const networkId = config.config.networkId || config.config.defaultNetwork || "simnet";
|
|
57
|
+
if (networkId === "mainnet" || networkId.startsWith("testnet")) {
|
|
58
|
+
throw new Error(`MetaMask onboarding is only allowed on local development networks (simnet, localnet).`);
|
|
59
|
+
}
|
|
60
|
+
const { getL2NetworkProfile } = await import("@hardkas/l2");
|
|
61
|
+
const { generateMetaMaskSnippet } = await import("@hardkas/l2");
|
|
62
|
+
const profile = await getL2NetworkProfile({
|
|
63
|
+
name: options.profile,
|
|
64
|
+
userProfiles: config.config.l2?.networks,
|
|
65
|
+
cliOverrides: {
|
|
66
|
+
rpcUrl: networkId === "simnet" || networkId === "localnet" ? "http://127.0.0.1:8545" : void 0,
|
|
67
|
+
chainId: networkId === "simnet" || networkId === "localnet" ? 19416 : void 0
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
const snippet = generateMetaMaskSnippet(profile);
|
|
71
|
+
console.log(pc.bold("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
72
|
+
console.log(pc.bold(`HardKAS \u2022 MetaMask Snippet`));
|
|
73
|
+
console.log(pc.bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
74
|
+
console.log(`${pc.yellow("Copy and paste this snippet into your browser console while MetaMask is open:")}
|
|
75
|
+
`);
|
|
76
|
+
console.log(pc.gray("```javascript"));
|
|
77
|
+
console.log(pc.white(snippet));
|
|
78
|
+
console.log(pc.gray("```\n"));
|
|
79
|
+
} catch (e) {
|
|
80
|
+
handleError(e);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
async function runMetamaskAccount(name, options) {
|
|
84
|
+
try {
|
|
85
|
+
const config = await loadHardkasConfig();
|
|
86
|
+
const { resolveHardkasAccount, prepareEvmAccountExport } = await import("@hardkas/accounts");
|
|
87
|
+
const account = resolveHardkasAccount({ nameOrAddress: name, config: config.config });
|
|
88
|
+
const networkId = config.config.networkId || config.config.defaultNetwork || "simnet";
|
|
89
|
+
const includeSecretRequested = options.json ? options.includeSecret && options.showPrivateKey : options.showPrivateKey;
|
|
90
|
+
const exportData = await prepareEvmAccountExport(account, networkId, {
|
|
91
|
+
includeSecret: includeSecretRequested
|
|
92
|
+
});
|
|
93
|
+
if (options.json) {
|
|
94
|
+
if (!options.includeSecret) {
|
|
95
|
+
delete exportData.privateKey;
|
|
96
|
+
}
|
|
97
|
+
console.log(JSON.stringify(exportData, null, 2));
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
console.log(pc.bold("\n\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501"));
|
|
101
|
+
console.log(pc.bold(`HardKAS \u2022 MetaMask Export`));
|
|
102
|
+
console.log(pc.bold("\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\n"));
|
|
103
|
+
console.log(`${pc.cyan(pc.bold("Account"))}`);
|
|
104
|
+
console.log(` Name: ${pc.white(name)}`);
|
|
105
|
+
console.log(` Address: ${pc.white(exportData.address)}`);
|
|
106
|
+
if (options.showPrivateKey && exportData.privateKey) {
|
|
107
|
+
console.log(`
|
|
108
|
+
${pc.bgRed(pc.white(pc.bold(" SECURITY WARNING ")))}`);
|
|
109
|
+
console.log(` ${pc.red("This is a LOCAL DEV ONLY account. Never share private keys.")}
|
|
110
|
+
`);
|
|
111
|
+
console.log(` Private Key: ${pc.white(exportData.privateKey)}`);
|
|
112
|
+
} else {
|
|
113
|
+
console.log(`
|
|
114
|
+
Private Key: ${pc.dim("[HIDDEN]")}`);
|
|
115
|
+
console.log(` ${pc.dim("Run with ")} ${pc.cyan("--show-private-key")} ${pc.dim("to reveal.")}`);
|
|
116
|
+
}
|
|
117
|
+
console.log(`
|
|
118
|
+
${pc.yellow("To import this account into MetaMask:")}`);
|
|
119
|
+
console.log(` 1. Open MetaMask`);
|
|
120
|
+
console.log(` 2. Click the Account menu (top right)`);
|
|
121
|
+
console.log(` 3. Click "Import Account"`);
|
|
122
|
+
console.log(` 4. Paste the private key above.
|
|
123
|
+
`);
|
|
124
|
+
} catch (e) {
|
|
125
|
+
handleError(e);
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
export {
|
|
129
|
+
runMetamaskAccount,
|
|
130
|
+
runMetamaskNetwork,
|
|
131
|
+
runMetamaskSnippet
|
|
132
|
+
};
|
package/dist/public.d.ts
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { HardkasConfig } from '@hardkas/config';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Type-safe config helper for hardkas.config.ts files.
|
|
5
|
+
* This is a pass-through function that provides autocompletion.
|
|
6
|
+
*/
|
|
7
|
+
declare function defineConfig(config: Partial<HardkasConfig>): Partial<HardkasConfig>;
|
|
8
|
+
|
|
9
|
+
export { defineConfig };
|
package/dist/public.js
ADDED
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
import {
|
|
2
|
+
handleError
|
|
3
|
+
} from "./chunk-K7XPWWIO.js";
|
|
4
|
+
|
|
5
|
+
// src/runners/session-runner.ts
|
|
6
|
+
import pc from "picocolors";
|
|
7
|
+
import { loadHardkasConfig } from "@hardkas/config";
|
|
8
|
+
async function runSessionCreate(name, options) {
|
|
9
|
+
try {
|
|
10
|
+
const config = await loadHardkasConfig();
|
|
11
|
+
const { createSession } = await import("@hardkas/sessions");
|
|
12
|
+
const { resolveHardkasAccountAddress } = await import("@hardkas/accounts");
|
|
13
|
+
const l1Address = resolveHardkasAccountAddress(options.l1, config.config);
|
|
14
|
+
const l2Address = resolveHardkasAccountAddress(options.l2, config.config);
|
|
15
|
+
await createSession({
|
|
16
|
+
schema: "hardkas.session.v1",
|
|
17
|
+
name,
|
|
18
|
+
l1: { wallet: options.l1, address: l1Address },
|
|
19
|
+
l2: { account: options.l2, address: l2Address },
|
|
20
|
+
bridge: { mode: "local-simulated" },
|
|
21
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
22
|
+
});
|
|
23
|
+
console.log(`
|
|
24
|
+
${pc.green("\u2713")} Session "${pc.white(name)}" created.`);
|
|
25
|
+
console.log(` L1: ${pc.cyan(options.l1)} (${l1Address})`);
|
|
26
|
+
console.log(` L2: ${pc.cyan(options.l2)} (${l2Address})
|
|
27
|
+
`);
|
|
28
|
+
} catch (e) {
|
|
29
|
+
handleError(e);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
async function runSessionList() {
|
|
33
|
+
try {
|
|
34
|
+
const { loadSessionStoreWithDiagnostics } = await import("@hardkas/sessions");
|
|
35
|
+
const { store, diagnostics } = loadSessionStoreWithDiagnostics();
|
|
36
|
+
if (diagnostics.length > 0) {
|
|
37
|
+
console.log(pc.yellow(`
|
|
38
|
+
\u26A0\uFE0F Session Store Warnings:`));
|
|
39
|
+
for (const d of diagnostics) {
|
|
40
|
+
console.log(pc.yellow(` - ${d}`));
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
const sessions = Object.values(store.sessions);
|
|
44
|
+
if (sessions.length === 0) {
|
|
45
|
+
console.log(`
|
|
46
|
+
No sessions found. Create one with:`);
|
|
47
|
+
console.log(`hardkas session create dev-alice --l1 alice --l2 dev_alice
|
|
48
|
+
`);
|
|
49
|
+
return;
|
|
50
|
+
}
|
|
51
|
+
console.log(pc.bold("\nHardKAS Developer Sessions"));
|
|
52
|
+
console.log(pc.dim("----------------------------------------"));
|
|
53
|
+
for (const s of sessions) {
|
|
54
|
+
const active = store.activeSession === s.name ? pc.green("\u25CF") : " ";
|
|
55
|
+
console.log(`${active} ${pc.white(s.name.padEnd(12))} ${pc.dim(`[L1: ${s.l1.wallet} | L2: ${s.l2.account}]`)}`);
|
|
56
|
+
}
|
|
57
|
+
console.log("");
|
|
58
|
+
} catch (e) {
|
|
59
|
+
handleError(e);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async function runSessionStatus() {
|
|
63
|
+
try {
|
|
64
|
+
const config = await loadHardkasConfig();
|
|
65
|
+
const { loadSessionStoreWithDiagnostics } = await import("@hardkas/sessions");
|
|
66
|
+
const { listHardkasAccounts } = await import("@hardkas/accounts");
|
|
67
|
+
const { store, diagnostics } = loadSessionStoreWithDiagnostics();
|
|
68
|
+
if (diagnostics.length > 0) {
|
|
69
|
+
console.log(pc.yellow(`
|
|
70
|
+
\u26A0\uFE0F Session Store Warnings:`));
|
|
71
|
+
for (const d of diagnostics) {
|
|
72
|
+
console.log(pc.yellow(` - ${d}`));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
const session = store.activeSession ? store.sessions[store.activeSession] : null;
|
|
76
|
+
if (!session) {
|
|
77
|
+
console.log(pc.yellow("\nNo active session."));
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
const accounts = listHardkasAccounts(config.config);
|
|
81
|
+
const l1Found = accounts.some((a) => a.name === session.l1.wallet);
|
|
82
|
+
const l2Found = accounts.some((a) => a.name === session.l2.account);
|
|
83
|
+
console.log(pc.bold(`
|
|
84
|
+
Active Session: ${pc.white(session.name)}`));
|
|
85
|
+
console.log(pc.dim("----------------------------------------"));
|
|
86
|
+
const l1Status = l1Found ? pc.green("\u2713") : pc.red("\u2717 MISSING");
|
|
87
|
+
console.log(`${l1Status} L1 Wallet: ${pc.cyan(session.l1.wallet)} (${session.l1.address})`);
|
|
88
|
+
const l2Status = l2Found ? pc.green("\u2713") : pc.red("\u2717 MISSING");
|
|
89
|
+
console.log(`${l2Status} L2 Account: ${pc.cyan(session.l2.account)} (${session.l2.address})`);
|
|
90
|
+
console.log(` Bridge: ${pc.white(session.bridge.mode)}`);
|
|
91
|
+
console.log(pc.dim("----------------------------------------"));
|
|
92
|
+
if (!l1Found || !l2Found) {
|
|
93
|
+
console.log(`
|
|
94
|
+
${pc.red(pc.bold("\u26A0 WARNING:"))} One or more accounts linked to this session are missing from your configuration.`);
|
|
95
|
+
console.log(`${pc.dim("Please update your hardkas.config.ts or recreate the session.")}`);
|
|
96
|
+
}
|
|
97
|
+
console.log("");
|
|
98
|
+
} catch (e) {
|
|
99
|
+
handleError(e);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
async function runSessionUse(name) {
|
|
103
|
+
try {
|
|
104
|
+
const { setActiveSession } = await import("@hardkas/sessions");
|
|
105
|
+
await setActiveSession(name);
|
|
106
|
+
console.log(`
|
|
107
|
+
${pc.green("\u2713")} Active session set to "${pc.white(name)}".
|
|
108
|
+
`);
|
|
109
|
+
} catch (e) {
|
|
110
|
+
handleError(e);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
export {
|
|
114
|
+
runSessionCreate,
|
|
115
|
+
runSessionList,
|
|
116
|
+
runSessionStatus,
|
|
117
|
+
runSessionUse
|
|
118
|
+
};
|