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 CHANGED
@@ -1,10 +1,16 @@
1
1
  # wit CLI
2
2
 
3
- Wit with Withub: A private, decentralized alternative to Git with GitHub, powered by Walrus. This CLI (Node.js/TypeScript, Commander + Ink) talks directly to Walrus decentralized storage instead of centralized hosts, targeting a single-branch, verifiable repository model with Sui state integration.
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
- - Access to a Walrus relay (testnet by default); Sui RPC endpoints are configurable as the state layer evolves.
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
- - Initialize a repo scaffold in the current directory:
16
- ```bash
17
- wit init <repo-name>
18
- ```
19
- This creates `.wit/`, writes `config.json` with sensible defaults (Walrus testnet relay, author/key placeholders), and ensures `.gitignore` / `.witignore` include `.wit/` and key paths.
20
- - Local VC workflow: `wit status`, `wit add`, `wit reset` / `wit restore --staged`, `wit restore <paths>`, `wit commit`, `wit log`, `wit diff`, `wit checkout`.
21
- - Storage experiments: `wit push-blob` / `pull-blob`; `wit push-quilt` / `pull-quilt` / `quilt-cat` / `quilt-ls` / `quilt-cat-id`; `push-quilt-legacy` / `pull-quilt-legacy` as archive fallback.
22
- - Colorized output can be toggled via `--color` / `--no-color` or env vars `NO_COLOR` / `WIT_NO_COLOR`.
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 deterministically.
26
- - `npm run build`: compile to `dist/` (entry `dist/index.js` ships with shebang for npm shims).
27
- - `npm start`: run the compiled CLI locally.
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.
@@ -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
- const active = await (0, keys_1.readActiveAddress)();
16
- const keys = await (0, keys_1.listStoredKeys)();
17
- if (!keys.length) {
18
- // eslint-disable-next-line no-console
19
- console.log('No keys found. Generate one with `wit account generate`.');
20
- return;
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
- for (const key of keys) {
23
- const marker = active && key.address === active ? ui_1.colors.green('*') : ' ';
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
- if (!address) {
32
- throw new Error('Address is required. Usage: wit account use <address>');
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
- const target = (0, keys_1.normalizeAddress)(address);
35
- const keys = await (0, keys_1.listStoredKeys)();
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
- const alias = opts.alias?.trim() || 'default';
47
- const { address } = await (0, keys_1.createSigner)(alias);
48
- await maybeUpdateRepoAuthor(address);
49
- // eslint-disable-next-line no-console
50
- console.log(`Generated new account ${ui_1.colors.hash(address)} (${alias}) and set as active.`);
51
- }
52
- async function accountBalanceAction(addressArg) {
53
- const target = addressArg ? (0, keys_1.normalizeAddress)(addressArg) : await (0, keys_1.readActiveAddress)();
54
- if (!target) {
55
- throw new Error('No address provided and no active address configured. Use `wit account generate` or `wit account use <address>` first.');
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
- const res = await (0, keys_1.checkResources)(target);
58
- // eslint-disable-next-line no-console
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
- else {
65
- const badgeSui = res.hasMinSui === false ? ui_1.colors.red('(low)') : res.hasMinSui ? ui_1.colors.green('(ok)') : ui_1.colors.yellow('(unknown)');
66
- // eslint-disable-next-line no-console
67
- console.log(`SUI: ${formatBalance(res.suiBalance ?? 0n, 'SUI')} ${badgeSui}`);
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
- if (res.walError) {
70
- // eslint-disable-next-line no-console
71
- console.log(`WAL: ${ui_1.colors.red('error')} ${res.walError}`);
156
+ catch (err) {
157
+ printError(errorMessage(err));
72
158
  }
73
- else {
74
- const badgeWal = res.hasMinWal === false ? ui_1.colors.red('(low)') : res.hasMinWal ? ui_1.colors.green('(ok)') : ui_1.colors.yellow('(unknown)');
75
- // eslint-disable-next-line no-console
76
- console.log(`WAL: ${formatBalance(res.walBalance ?? 0n, 'WAL')} ${badgeWal}`);
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
- const witDir = path_1.default.join(process.cwd(), '.wit');
86
- const repoCfgPath = path_1.default.join(witDir, 'config.json');
245
+ async function maybeUpdateRepoAuthor(address, chain) {
246
+ let witDir;
87
247
  try {
88
- await promises_1.default.access(repoCfgPath);
248
+ witDir = await (0, repo_1.requireWitDir)();
89
249
  }
90
250
  catch (err) {
91
- if (err?.code === 'ENOENT')
251
+ if (err?.message?.includes('Not a wit repository'))
92
252
  return;
93
253
  throw err;
94
254
  }
95
255
  try {
96
- const raw = await promises_1.default.readFile(repoCfgPath, 'utf8');
97
- const cfg = JSON.parse(raw);
98
- if (!cfg.author || cfg.author === 'unknown') {
99
- cfg.author = address;
100
- await promises_1.default.writeFile(repoCfgPath, JSON.stringify(cfg, null, 2) + '\n', 'utf8');
101
- // eslint-disable-next-line no-console
102
- console.log(ui_1.colors.green(`Updated .wit/config.json author to ${address}`));
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
+ }