withub-cli 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/README.md +74 -26
- package/dist/commands/account.js +262 -63
- package/dist/commands/chain.js +35 -0
- package/dist/commands/checkout.js +137 -16
- package/dist/commands/clone.js +64 -7
- package/dist/commands/commit.js +4 -16
- package/dist/commands/fetch.js +76 -4
- package/dist/commands/init.js +75 -13
- package/dist/commands/invite.js +68 -53
- package/dist/commands/ipfsCar.js +98 -0
- package/dist/commands/lighthouse.js +97 -0
- package/dist/commands/lighthouseDownload.js +58 -0
- package/dist/commands/lighthousePin.js +62 -0
- package/dist/commands/pull.js +2 -1
- package/dist/commands/push.js +224 -8
- package/dist/commands/registerCommands.js +108 -2
- package/dist/commands/remove-user.js +46 -0
- package/dist/commands/removeUser.js +30 -1
- package/dist/index.js +15 -0
- package/dist/lib/chain.js +72 -0
- package/dist/lib/crypto.js +62 -0
- package/dist/lib/evmClone.js +255 -0
- package/dist/lib/evmKeys.js +218 -0
- package/dist/lib/evmProvider.js +88 -0
- package/dist/lib/evmRepo.js +192 -0
- package/dist/lib/ipfsCar.js +132 -0
- package/dist/lib/keccak.js +125 -0
- package/dist/lib/keys.js +102 -37
- package/dist/lib/lighthouse.js +661 -0
- package/dist/lib/lit.js +165 -0
- package/dist/lib/manifest.js +22 -4
- package/dist/lib/repo.js +94 -0
- package/dist/lib/schema.js +26 -6
- package/dist/lib/walrus.js +11 -1
- package/package.json +17 -2
package/README.md
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
# wit CLI
|
|
2
2
|
|
|
3
|
-
Wit with Withub: A private, decentralized alternative to Git with GitHub
|
|
3
|
+
Wit with Withub: A private, decentralized alternative to Git with GitHub.
|
|
4
|
+
Now supporting multi-chain architecture:
|
|
5
|
+
- **Mantle**: Powered by IPFS (Lighthouse) + Lit Protocol for privacy/encryption (Mainnet).
|
|
6
|
+
- **Sui**: Powered by Walrus decentralized storage (Testnet).
|
|
7
|
+
|
|
8
|
+
The CLI routes commands to the appropriate chain and storage backend based on your configuration.
|
|
4
9
|
|
|
5
10
|
## Requirements
|
|
6
11
|
- Node.js >= 18.
|
|
7
|
-
-
|
|
12
|
+
- **For Mantle**: A Lighthouse API Key (get one at [Lighthouse Storage](https://lighthouse.storage)).
|
|
13
|
+
- **For Sui**: Access to a Walrus relay (configured by default).
|
|
8
14
|
|
|
9
15
|
## Install & Run
|
|
10
16
|
- Global install: `npm install -g withub-cli` then `wit --help`.
|
|
@@ -12,30 +18,72 @@ Wit with Withub: A private, decentralized alternative to Git with GitHub, powere
|
|
|
12
18
|
- Upgrade: `npm install -g withub-cli@latest`. Uninstall: `npm uninstall -g withub-cli`.
|
|
13
19
|
|
|
14
20
|
## Quickstart
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
|
|
22
|
+
### 1. Configure Environment (For Mantle)
|
|
23
|
+
For a smoother experience with Mantle/IPFS, set your Lighthouse API key in your shell:
|
|
24
|
+
```bash
|
|
25
|
+
export WIT_LIGHTHOUSE_API_KEY="your-api-key"
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
### 2. Choose Your Chain
|
|
29
|
+
Wit supports multiple chains. Select your active chain globally:
|
|
30
|
+
```bash
|
|
31
|
+
wit chain list # List available chains (sui, mantle)
|
|
32
|
+
wit chain use mantle # Switch to Mantle Mainnet
|
|
33
|
+
wit chain current # Show current active chain
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### 3. Identity & Accounts
|
|
37
|
+
Manage your EVM (Mantle) or Sui identity:
|
|
38
|
+
```bash
|
|
39
|
+
# For Mantle
|
|
40
|
+
wit account generate # Generate a new random EVM wallet
|
|
41
|
+
wit account import <key> # Import an existing private key
|
|
42
|
+
wit account list # List managed accounts
|
|
43
|
+
wit account use <address> # Select active account
|
|
44
|
+
wit account balance # Check MNT balance
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### 4. Initialize & Version Control
|
|
48
|
+
Standard VC workflow works across chains:
|
|
49
|
+
```bash
|
|
50
|
+
mkdir my-repo && cd my-repo
|
|
51
|
+
wit init # Initialize repo on the active chain
|
|
52
|
+
wit status
|
|
53
|
+
wit add .
|
|
54
|
+
wit commit -m "First commit"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### 5. Remote Operations
|
|
58
|
+
Push and pull from decentralized storage.
|
|
59
|
+
- **Mantle**: Pushes to IPFS (via Lighthouse), encrypts content with a session key, and manages access via Lit Protocol.
|
|
60
|
+
- **Sui**: Pushes to Walrus.
|
|
61
|
+
|
|
62
|
+
```bash
|
|
63
|
+
wit push # Push to configured chain storage
|
|
64
|
+
wit fetch # Fetch updates
|
|
65
|
+
wit pull # Pull and checkout
|
|
66
|
+
wit clone <repo-id> # Clone an existing repository
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 6. Privacy & Access Control (Mantle Only)
|
|
70
|
+
Manage collaborators for your private repository using Lit Protocol:
|
|
71
|
+
```bash
|
|
72
|
+
wit invite <address> # Grant access to a collaborator
|
|
73
|
+
wit remove-user <address> # Revoke access
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
## Storage & Implementation Details
|
|
77
|
+
- **Mantle**:
|
|
78
|
+
- **Storage**: IPFS (CAR format).
|
|
79
|
+
- **Privacy**: AES-256-GCM encryption. Session keys are encrypted via Lit Protocol and stored in IPFS metadata. Access control is enforced by a smart contract on Mantle Mainnet.
|
|
80
|
+
- **Access Control**: Lit Protocol.
|
|
81
|
+
- **Sui**:
|
|
82
|
+
- **Storage**: Walrus (Blob/Quilt).
|
|
83
|
+
- **Privacy**: Seal.
|
|
23
84
|
|
|
24
85
|
## Developer Scripts
|
|
25
|
-
- `npm ci`: install dependencies
|
|
26
|
-
- `npm run build`: compile to `dist
|
|
27
|
-
- `npm start`: run
|
|
86
|
+
- `npm ci`: install dependencies.
|
|
87
|
+
- `npm run build`: compile to `dist/`.
|
|
88
|
+
- `npm start`: run locally.
|
|
28
89
|
- `npm run test:smoke`: minimal smoke test.
|
|
29
|
-
- `prepublishOnly`: runs `npm run build && npm run test:smoke` to guard against unbuilt or untested publishes.
|
|
30
|
-
|
|
31
|
-
## Publish Checklist (manual)
|
|
32
|
-
1) `npm ci && npm run build`.
|
|
33
|
-
2) `npm run test:smoke`.
|
|
34
|
-
3) `npm pack` and inspect the tarball (should contain `dist/**`, `README.md`, `LICENSE`, `package.json`; `bin` points to `dist/index.js`).
|
|
35
|
-
4) `npm publish` (scope is already `access: public`; for prerelease use `npm publish --tag next`).
|
|
36
|
-
5) Verify: `npm info withub-cli version`, `npx withub-cli --version`, optionally `npm install -g withub-cli && wit --help` for global smoke.
|
|
37
|
-
|
|
38
|
-
## Current Scope
|
|
39
|
-
- Local VC core: `init`, `status`, `add`, `reset`, `restore`, `commit`, `log`, `diff`, `checkout`.
|
|
40
|
-
- Walrus storage flows: quilt/blob push/pull commands plus legacy archive fallback.
|
|
41
|
-
- Remote/contract/Web flows are under active development; CLI surfaces will expand alongside Sui state and privacy (Seal) integration.
|
package/dist/commands/account.js
CHANGED
|
@@ -6,74 +6,235 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
6
6
|
exports.accountListAction = accountListAction;
|
|
7
7
|
exports.accountUseAction = accountUseAction;
|
|
8
8
|
exports.accountGenerateAction = accountGenerateAction;
|
|
9
|
+
exports.accountImportAction = accountImportAction;
|
|
9
10
|
exports.accountBalanceAction = accountBalanceAction;
|
|
10
11
|
const promises_1 = __importDefault(require("fs/promises"));
|
|
12
|
+
const os_1 = __importDefault(require("os"));
|
|
11
13
|
const path_1 = __importDefault(require("path"));
|
|
14
|
+
const ethers_1 = require("ethers");
|
|
12
15
|
const ui_1 = require("../lib/ui");
|
|
16
|
+
const chain_1 = require("../lib/chain");
|
|
13
17
|
const keys_1 = require("../lib/keys");
|
|
18
|
+
const evmKeys_1 = require("../lib/evmKeys");
|
|
19
|
+
const evmProvider_1 = require("../lib/evmProvider");
|
|
20
|
+
const repo_1 = require("../lib/repo");
|
|
14
21
|
async function accountListAction() {
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
try {
|
|
23
|
+
const activeChain = await (0, chain_1.readActiveChain)();
|
|
24
|
+
if (activeChain === 'mantle') {
|
|
25
|
+
const active = await (0, evmKeys_1.readActiveEvmAddress)();
|
|
26
|
+
const keys = await (0, evmKeys_1.listEvmKeys)();
|
|
27
|
+
if (!keys.length) {
|
|
28
|
+
// eslint-disable-next-line no-console
|
|
29
|
+
console.log('No keys found. Generate one with `wit account generate`.');
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
for (const key of keys) {
|
|
33
|
+
const marker = active && key.address === active ? ui_1.colors.green('*') : ' ';
|
|
34
|
+
const alias = key.alias ? ` (${key.alias})` : '';
|
|
35
|
+
const created = key.createdAt ? ` ${ui_1.colors.gray(`[${key.createdAt}]`)}` : '';
|
|
36
|
+
// eslint-disable-next-line no-console
|
|
37
|
+
console.log(`${marker} ${ui_1.colors.hash(key.address)}${alias}${created}`);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
else if (activeChain === 'sui') {
|
|
41
|
+
const active = await (0, keys_1.readActiveAddress)();
|
|
42
|
+
const keys = await (0, keys_1.listStoredKeys)();
|
|
43
|
+
if (!keys.length) {
|
|
44
|
+
// eslint-disable-next-line no-console
|
|
45
|
+
console.log('No keys found. Generate one with `wit account generate`.');
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
for (const key of keys) {
|
|
49
|
+
const marker = active && key.address === active ? ui_1.colors.green('*') : ' ';
|
|
50
|
+
const alias = key.alias ? ` (${key.alias})` : '';
|
|
51
|
+
const created = key.createdAt ? ` ${ui_1.colors.gray(`[${key.createdAt}]`)}` : '';
|
|
52
|
+
// eslint-disable-next-line no-console
|
|
53
|
+
console.log(`${marker} ${ui_1.colors.hash(key.address)}${alias}${created}`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
else {
|
|
57
|
+
printError(`Unsupported chain "${activeChain}".`);
|
|
58
|
+
}
|
|
21
59
|
}
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
const alias = key.alias ? ` (${key.alias})` : '';
|
|
25
|
-
const created = key.createdAt ? ` ${ui_1.colors.gray(`[${key.createdAt}]`)}` : '';
|
|
26
|
-
// eslint-disable-next-line no-console
|
|
27
|
-
console.log(`${marker} ${ui_1.colors.hash(key.address)}${alias}${created}`);
|
|
60
|
+
catch (err) {
|
|
61
|
+
printError(errorMessage(err));
|
|
28
62
|
}
|
|
29
63
|
}
|
|
30
64
|
async function accountUseAction(address) {
|
|
31
|
-
|
|
32
|
-
|
|
65
|
+
try {
|
|
66
|
+
if (!address) {
|
|
67
|
+
printError('Address is required. Usage: wit account use <address>');
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
const activeChain = await (0, chain_1.readActiveChain)();
|
|
71
|
+
if (activeChain === 'mantle') {
|
|
72
|
+
const target = (0, evmKeys_1.normalizeEvmAddress)(address);
|
|
73
|
+
const keys = await (0, evmKeys_1.listEvmKeys)();
|
|
74
|
+
const match = keys.find((k) => k.address === target);
|
|
75
|
+
if (!match) {
|
|
76
|
+
printError(`Key not found for address ${target}. Generate one with "wit account generate".`);
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await (0, evmKeys_1.setActiveEvmAddress)(target, { alias: match.alias, updateAuthorIfUnknown: true });
|
|
80
|
+
await maybeUpdateRepoAuthor(target, activeChain);
|
|
81
|
+
// eslint-disable-next-line no-console
|
|
82
|
+
console.log(`Switched active account to ${ui_1.colors.hash(target)}${match.alias ? ` (${match.alias})` : ''}`);
|
|
83
|
+
}
|
|
84
|
+
else if (activeChain === 'sui') {
|
|
85
|
+
const target = (0, keys_1.normalizeAddress)(address);
|
|
86
|
+
const keys = await (0, keys_1.listStoredKeys)();
|
|
87
|
+
const match = keys.find((k) => k.address === target);
|
|
88
|
+
if (!match) {
|
|
89
|
+
printError(`Key not found for address ${target}. Generate one with "wit account generate".`);
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
await (0, keys_1.setActiveAddress)(target, { alias: match.alias, updateAuthorIfUnknown: true });
|
|
93
|
+
await maybeUpdateRepoAuthor(target, activeChain);
|
|
94
|
+
// eslint-disable-next-line no-console
|
|
95
|
+
console.log(`Switched active account to ${ui_1.colors.hash(target)}${match.alias ? ` (${match.alias})` : ''}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
printError(`Unsupported chain "${activeChain}".`);
|
|
99
|
+
}
|
|
33
100
|
}
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
const match = keys.find((k) => k.address === target);
|
|
37
|
-
if (!match) {
|
|
38
|
-
throw new Error(`Key not found for address ${target}. Generate one with "wit account generate".`);
|
|
101
|
+
catch (err) {
|
|
102
|
+
printError(errorMessage(err));
|
|
39
103
|
}
|
|
40
|
-
await (0, keys_1.setActiveAddress)(target, { alias: match.alias, updateAuthorIfUnknown: true });
|
|
41
|
-
await maybeUpdateRepoAuthor(target);
|
|
42
|
-
// eslint-disable-next-line no-console
|
|
43
|
-
console.log(`Switched active account to ${ui_1.colors.hash(target)}${match.alias ? ` (${match.alias})` : ''}`);
|
|
44
104
|
}
|
|
45
105
|
async function accountGenerateAction(opts) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
106
|
+
try {
|
|
107
|
+
const alias = opts.alias?.trim() || 'default';
|
|
108
|
+
const activeChain = await (0, chain_1.readActiveChain)();
|
|
109
|
+
if (activeChain === 'mantle') {
|
|
110
|
+
const { address } = await (0, evmKeys_1.createEvmKey)(alias);
|
|
111
|
+
await maybeUpdateRepoAuthor(address, activeChain);
|
|
112
|
+
// eslint-disable-next-line no-console
|
|
113
|
+
console.log(`Generated new account ${ui_1.colors.hash(address)} (${alias}) and set as active.`);
|
|
114
|
+
// eslint-disable-next-line no-console
|
|
115
|
+
console.log(ui_1.colors.yellow(`\n👉 Important: This is a Mantle Mainnet wallet.`));
|
|
116
|
+
// eslint-disable-next-line no-console
|
|
117
|
+
console.log(ui_1.colors.yellow(` You MUST fund this address with MNT tokens to perform ANY operations (push, create-repo, etc).`));
|
|
118
|
+
// eslint-disable-next-line no-console
|
|
119
|
+
console.log(ui_1.colors.yellow(` Address: ${address}`));
|
|
120
|
+
}
|
|
121
|
+
else if (activeChain === 'sui') {
|
|
122
|
+
const { address } = await (0, keys_1.createSigner)(alias);
|
|
123
|
+
await maybeUpdateRepoAuthor(address, activeChain);
|
|
124
|
+
// eslint-disable-next-line no-console
|
|
125
|
+
console.log(`Generated new account ${ui_1.colors.hash(address)} (${alias}) and set as active.`);
|
|
126
|
+
}
|
|
127
|
+
else {
|
|
128
|
+
printError(`Unsupported chain "${activeChain}".`);
|
|
129
|
+
}
|
|
56
130
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
console.log(ui_1.colors.header(`Account ${ui_1.colors.hash(target)}`));
|
|
60
|
-
if (res.error) {
|
|
61
|
-
// eslint-disable-next-line no-console
|
|
62
|
-
console.log(`SUI: ${ui_1.colors.red('error')} ${res.error}`);
|
|
131
|
+
catch (err) {
|
|
132
|
+
printError(errorMessage(err));
|
|
63
133
|
}
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
134
|
+
}
|
|
135
|
+
async function accountImportAction(privateKey, opts) {
|
|
136
|
+
try {
|
|
137
|
+
if (!privateKey) {
|
|
138
|
+
printError('Private key is required. Usage: wit account import <private_key>');
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
const alias = opts.alias?.trim() || 'default';
|
|
142
|
+
const activeChain = await (0, chain_1.readActiveChain)();
|
|
143
|
+
if (activeChain === 'mantle') {
|
|
144
|
+
const { address } = await (0, evmKeys_1.importEvmKey)(privateKey, alias);
|
|
145
|
+
await maybeUpdateRepoAuthor(address, activeChain);
|
|
146
|
+
// eslint-disable-next-line no-console
|
|
147
|
+
console.log(`Imported account ${ui_1.colors.hash(address)} (${alias}) and set as active.`);
|
|
148
|
+
}
|
|
149
|
+
else if (activeChain === 'sui') {
|
|
150
|
+
printError('Sui key import is not supported yet.');
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
printError(`Unsupported chain "${activeChain}".`);
|
|
154
|
+
}
|
|
68
155
|
}
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
console.log(`WAL: ${ui_1.colors.red('error')} ${res.walError}`);
|
|
156
|
+
catch (err) {
|
|
157
|
+
printError(errorMessage(err));
|
|
72
158
|
}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
159
|
+
}
|
|
160
|
+
async function accountBalanceAction(addressArg) {
|
|
161
|
+
try {
|
|
162
|
+
const activeChain = await (0, chain_1.readActiveChain)();
|
|
163
|
+
if (activeChain === 'mantle') {
|
|
164
|
+
const address = addressArg ? (0, evmKeys_1.normalizeEvmAddress)(addressArg) : await (0, evmKeys_1.readActiveEvmAddress)();
|
|
165
|
+
if (!address) {
|
|
166
|
+
printError('No address provided and no active address configured. Use `wit account generate` or `wit account use <address>` first.');
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
const networkHint = await readMantleNetworkHint();
|
|
170
|
+
const config = (0, evmProvider_1.resolveMantleConfig)(networkHint);
|
|
171
|
+
const provider = (0, evmProvider_1.createMantleProvider)(config.network);
|
|
172
|
+
const balance = await provider.getBalance(address);
|
|
173
|
+
const txContext = await (0, evmProvider_1.resolveEvmTxContext)(provider, address, { to: address, value: 0n });
|
|
174
|
+
// eslint-disable-next-line no-console
|
|
175
|
+
console.log(ui_1.colors.header(`Account ${ui_1.colors.hash(address)}`));
|
|
176
|
+
// eslint-disable-next-line no-console
|
|
177
|
+
console.log(`Network: ${config.chainName} (${config.chainId}, ${config.network})`);
|
|
178
|
+
// eslint-disable-next-line no-console
|
|
179
|
+
console.log(`RPC: ${config.rpcUrl}`);
|
|
180
|
+
// eslint-disable-next-line no-console
|
|
181
|
+
console.log(`MNT: ${(0, ethers_1.formatUnits)(balance, 18)} MNT`);
|
|
182
|
+
const gasLimit = txContext.gasLimit;
|
|
183
|
+
const feeData = txContext.feeData;
|
|
184
|
+
const gasPrice = feeData.maxFeePerGas ?? feeData.gasPrice;
|
|
185
|
+
const gasCost = gasLimit && gasPrice ? gasLimit * gasPrice : null;
|
|
186
|
+
const gasLabel = gasLimit ? gasLimit.toString() : 'n/a';
|
|
187
|
+
const maxFee = feeData.maxFeePerGas ? `${(0, ethers_1.formatUnits)(feeData.maxFeePerGas, 9)} gwei` : 'n/a';
|
|
188
|
+
const maxPriority = feeData.maxPriorityFeePerGas ? `${(0, ethers_1.formatUnits)(feeData.maxPriorityFeePerGas, 9)} gwei` : 'n/a';
|
|
189
|
+
const gasPriceLabel = feeData.gasPrice ? `${(0, ethers_1.formatUnits)(feeData.gasPrice, 9)} gwei` : 'n/a';
|
|
190
|
+
const gasCostLabel = gasCost ? `${(0, ethers_1.formatUnits)(gasCost, 18)} MNT` : 'n/a';
|
|
191
|
+
// eslint-disable-next-line no-console
|
|
192
|
+
console.log(ui_1.colors.cyan('Gas estimate (simple transfer):'));
|
|
193
|
+
// eslint-disable-next-line no-console
|
|
194
|
+
console.log(` gasLimit: ${gasLabel}`);
|
|
195
|
+
// eslint-disable-next-line no-console
|
|
196
|
+
console.log(` maxFeePerGas: ${maxFee}`);
|
|
197
|
+
// eslint-disable-next-line no-console
|
|
198
|
+
console.log(` maxPriorityFeePerGas: ${maxPriority}`);
|
|
199
|
+
// eslint-disable-next-line no-console
|
|
200
|
+
console.log(` gasPrice: ${gasPriceLabel}`);
|
|
201
|
+
// eslint-disable-next-line no-console
|
|
202
|
+
console.log(` estCost: ${gasCostLabel}`);
|
|
203
|
+
}
|
|
204
|
+
else if (activeChain === 'sui') {
|
|
205
|
+
const target = addressArg ? (0, keys_1.normalizeAddress)(addressArg) : await (0, keys_1.readActiveAddress)();
|
|
206
|
+
if (!target) {
|
|
207
|
+
printError('No address provided and no active address configured. Use `wit account generate` or `wit account use <address>` first.');
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
const res = await (0, keys_1.checkResources)(target);
|
|
211
|
+
// eslint-disable-next-line no-console
|
|
212
|
+
console.log(ui_1.colors.header(`Account ${ui_1.colors.hash(target)}`));
|
|
213
|
+
if (res.error) {
|
|
214
|
+
// eslint-disable-next-line no-console
|
|
215
|
+
console.log(`SUI: ${ui_1.colors.red('error')} ${res.error}`);
|
|
216
|
+
}
|
|
217
|
+
else {
|
|
218
|
+
const badgeSui = res.hasMinSui === false ? ui_1.colors.red('(low)') : res.hasMinSui ? ui_1.colors.green('(ok)') : ui_1.colors.yellow('(unknown)');
|
|
219
|
+
// eslint-disable-next-line no-console
|
|
220
|
+
console.log(`SUI: ${formatBalance(res.suiBalance ?? 0n, 'SUI')} ${badgeSui}`);
|
|
221
|
+
}
|
|
222
|
+
if (res.walError) {
|
|
223
|
+
// eslint-disable-next-line no-console
|
|
224
|
+
console.log(`WAL: ${ui_1.colors.red('error')} ${res.walError}`);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
const badgeWal = res.hasMinWal === false ? ui_1.colors.red('(low)') : res.hasMinWal ? ui_1.colors.green('(ok)') : ui_1.colors.yellow('(unknown)');
|
|
228
|
+
// eslint-disable-next-line no-console
|
|
229
|
+
console.log(`WAL: ${formatBalance(res.walBalance ?? 0n, 'WAL')} ${badgeWal}`);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
else {
|
|
233
|
+
printError(`Unsupported chain "${activeChain}".`);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
catch (err) {
|
|
237
|
+
printError(errorMessage(err));
|
|
77
238
|
}
|
|
78
239
|
}
|
|
79
240
|
function formatBalance(amount, symbol) {
|
|
@@ -81,29 +242,67 @@ function formatBalance(amount, symbol) {
|
|
|
81
242
|
const frac = amount % 1000000000n;
|
|
82
243
|
return `${whole}.${frac.toString().padStart(9, '0')} ${symbol}`;
|
|
83
244
|
}
|
|
84
|
-
async function maybeUpdateRepoAuthor(address) {
|
|
85
|
-
|
|
86
|
-
const repoCfgPath = path_1.default.join(witDir, 'config.json');
|
|
245
|
+
async function maybeUpdateRepoAuthor(address, chain) {
|
|
246
|
+
let witDir;
|
|
87
247
|
try {
|
|
88
|
-
await
|
|
248
|
+
witDir = await (0, repo_1.requireWitDir)();
|
|
89
249
|
}
|
|
90
250
|
catch (err) {
|
|
91
|
-
if (err?.
|
|
251
|
+
if (err?.message?.includes('Not a wit repository'))
|
|
92
252
|
return;
|
|
93
253
|
throw err;
|
|
94
254
|
}
|
|
95
255
|
try {
|
|
96
|
-
const
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
256
|
+
const cfg = await (0, repo_1.readRepoConfig)(witDir);
|
|
257
|
+
if (cfg.chain && cfg.chain !== chain)
|
|
258
|
+
return;
|
|
259
|
+
const current = cfg.chains?.[chain]?.author;
|
|
260
|
+
if (current && current !== 'unknown')
|
|
261
|
+
return;
|
|
262
|
+
(0, repo_1.setChainAuthor)(cfg, chain, address);
|
|
263
|
+
await (0, repo_1.writeRepoConfig)(witDir, cfg);
|
|
264
|
+
// eslint-disable-next-line no-console
|
|
265
|
+
console.log(ui_1.colors.green(`Updated .wit/config.json author to ${address}`));
|
|
104
266
|
}
|
|
105
267
|
catch (err) {
|
|
106
268
|
// eslint-disable-next-line no-console
|
|
107
269
|
console.warn(`Warning: could not update .wit/config.json author: ${err.message}`);
|
|
108
270
|
}
|
|
109
271
|
}
|
|
272
|
+
function printError(message) {
|
|
273
|
+
// eslint-disable-next-line no-console
|
|
274
|
+
console.error(ui_1.colors.red(message));
|
|
275
|
+
process.exitCode = 1;
|
|
276
|
+
}
|
|
277
|
+
function errorMessage(err) {
|
|
278
|
+
if (err instanceof Error && err.message)
|
|
279
|
+
return err.message;
|
|
280
|
+
return String(err);
|
|
281
|
+
}
|
|
282
|
+
async function readMantleNetworkHint() {
|
|
283
|
+
try {
|
|
284
|
+
const witDir = await (0, repo_1.requireWitDir)();
|
|
285
|
+
const repoCfg = await (0, repo_1.readRepoConfig)(witDir);
|
|
286
|
+
if (repoCfg.network)
|
|
287
|
+
return repoCfg.network;
|
|
288
|
+
}
|
|
289
|
+
catch (err) {
|
|
290
|
+
if (err?.message?.includes('Not a wit repository')) {
|
|
291
|
+
// ignore
|
|
292
|
+
}
|
|
293
|
+
else {
|
|
294
|
+
throw err;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
const configPath = path_1.default.join(os_1.default.homedir(), '.witconfig');
|
|
298
|
+
try {
|
|
299
|
+
const raw = await promises_1.default.readFile(configPath, 'utf8');
|
|
300
|
+
const cfg = JSON.parse(raw);
|
|
301
|
+
return cfg.network ?? null;
|
|
302
|
+
}
|
|
303
|
+
catch (err) {
|
|
304
|
+
if (err?.code === 'ENOENT')
|
|
305
|
+
return null;
|
|
306
|
+
throw err;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.chainListAction = chainListAction;
|
|
4
|
+
exports.chainUseAction = chainUseAction;
|
|
5
|
+
exports.chainCurrentAction = chainCurrentAction;
|
|
6
|
+
const ui_1 = require("../lib/ui");
|
|
7
|
+
const chain_1 = require("../lib/chain");
|
|
8
|
+
async function chainListAction() {
|
|
9
|
+
const active = await (0, chain_1.readActiveChain)();
|
|
10
|
+
const chains = (0, chain_1.listSupportedChains)();
|
|
11
|
+
// eslint-disable-next-line no-console
|
|
12
|
+
console.log(ui_1.colors.header(`Active chain: ${active}`));
|
|
13
|
+
for (const chain of chains) {
|
|
14
|
+
const marker = chain.id === active ? ui_1.colors.green('*') : ' ';
|
|
15
|
+
const label = chain.label ? ` ${ui_1.colors.gray(`(${chain.label})`)}` : '';
|
|
16
|
+
// eslint-disable-next-line no-console
|
|
17
|
+
console.log(`${marker} ${chain.id}${label}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
async function chainUseAction(chainArg) {
|
|
21
|
+
if (!chainArg) {
|
|
22
|
+
throw new Error('Chain is required. Usage: wit chain use <chain>');
|
|
23
|
+
}
|
|
24
|
+
const chain = (0, chain_1.normalizeChain)(chainArg);
|
|
25
|
+
await (0, chain_1.setActiveChain)(chain);
|
|
26
|
+
// eslint-disable-next-line no-console
|
|
27
|
+
console.log(`Active chain set to ${ui_1.colors.green(chain)}.`);
|
|
28
|
+
}
|
|
29
|
+
async function chainCurrentAction() {
|
|
30
|
+
const active = await (0, chain_1.readActiveChain)();
|
|
31
|
+
const info = (0, chain_1.listSupportedChains)().find((chain) => chain.id === active);
|
|
32
|
+
const label = info?.label ? ` (${info.label})` : '';
|
|
33
|
+
// eslint-disable-next-line no-console
|
|
34
|
+
console.log(`${active}${label}`);
|
|
35
|
+
}
|