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.
@@ -20,7 +20,22 @@ const suiRepo_1 = require("../lib/suiRepo");
20
20
  const schema_1 = require("../lib/schema");
21
21
  const seal_1 = require("../lib/seal");
22
22
  const constants_1 = require("../lib/constants");
23
+ const evmRepo_1 = require("../lib/evmRepo");
24
+ const evmProvider_1 = require("../lib/evmProvider");
25
+ const lit_1 = require("../lib/lit");
26
+ const lighthouse_1 = require("../lib/lighthouse");
27
+ const crypto_1 = require("../lib/crypto");
23
28
  async function pushAction() {
29
+ const witPath = await (0, repo_1.requireWitDir)();
30
+ const repoCfg = await (0, repo_1.readRepoConfig)(witPath);
31
+ if (repoCfg.chain === 'mantle') {
32
+ return mantlePushAction(witPath, repoCfg);
33
+ }
34
+ else {
35
+ return suiPushAction();
36
+ }
37
+ }
38
+ async function suiPushAction() {
24
39
  // eslint-disable-next-line no-console
25
40
  console.log(ui_1.colors.header('Starting push...'));
26
41
  const witPath = await (0, repo_1.requireWitDir)();
@@ -47,7 +62,8 @@ async function pushAction() {
47
62
  let createdRepo = false;
48
63
  let repoId = repoCfg.repo_id;
49
64
  if (!repoId) {
50
- const isPrivate = repoCfg.seal_policy_id === 'pending' || !!repoCfg.seal_policy_id;
65
+ const sealPolicyId = (0, repo_1.resolveSuiSealPolicyId)(repoCfg);
66
+ const isPrivate = sealPolicyId === 'pending' || !!sealPolicyId;
51
67
  repoId = await (0, suiRepo_1.createRepository)(suiClient, signerInfo.signer, {
52
68
  name: repoCfg.repo_name,
53
69
  description: repoCfg.repo_name,
@@ -61,13 +77,15 @@ async function pushAction() {
61
77
  }
62
78
  const onchainState = await (0, suiRepo_1.fetchRepositoryStateWithRetry)(suiClient, repoId);
63
79
  // Update local seal policy ID if it was pending or changed
64
- if (onchainState.sealPolicyId && repoCfg.seal_policy_id !== onchainState.sealPolicyId) {
65
- repoCfg.seal_policy_id = onchainState.sealPolicyId;
80
+ const localPolicyId = (0, repo_1.resolveSuiSealPolicyId)(repoCfg);
81
+ if (onchainState.sealPolicyId && onchainState.sealPolicyId !== localPolicyId) {
82
+ (0, repo_1.setSuiSealPolicyId)(repoCfg, onchainState.sealPolicyId);
66
83
  await (0, repo_1.writeRepoConfig)(witPath, repoCfg);
67
84
  // eslint-disable-next-line no-console
68
85
  console.log(ui_1.colors.cyan(`Seal policy updated from chain: ${onchainState.sealPolicyId}`));
69
86
  }
70
- const currentPolicyId = repoCfg.seal_policy_id === 'pending' ? null : repoCfg.seal_policy_id;
87
+ const updatedPolicyId = (0, repo_1.resolveSuiSealPolicyId)(repoCfg);
88
+ const currentPolicyId = updatedPolicyId === 'pending' ? null : updatedPolicyId;
71
89
  if (onchainState.headCommit && baseRemoteId !== onchainState.headCommit) {
72
90
  throw new Error('Remote head diverges from local history; run `wit pull`/`fetch` or reset first.');
73
91
  }
@@ -325,18 +343,18 @@ async function cacheJson(filePath, content) {
325
343
  await promises_1.default.writeFile(filePath, content, 'utf8');
326
344
  }
327
345
  async function ensureAuthorOrSetDefault(witPath, repoCfg, signerAddress) {
328
- const current = (repoCfg.author || '').trim().toLowerCase();
346
+ const current = (0, repo_1.resolveChainAuthor)(repoCfg).trim().toLowerCase();
329
347
  const signer = signerAddress.toLowerCase();
330
348
  if (!current || current === 'unknown') {
331
- const next = { ...repoCfg, author: signerAddress };
332
- await (0, repo_1.writeRepoConfig)(witPath, next);
349
+ (0, repo_1.setChainAuthor)(repoCfg, repoCfg.chain, signerAddress);
350
+ await (0, repo_1.writeRepoConfig)(witPath, repoCfg);
333
351
  // eslint-disable-next-line no-console
334
352
  console.log(ui_1.colors.cyan(`Author set to active address ${signerAddress}`));
335
353
  return;
336
354
  }
337
355
  if (current !== signer) {
338
356
  // eslint-disable-next-line no-console
339
- console.warn(`Warning: author (${repoCfg.author}) differs from signer (${signerAddress}). Push will use signer.`);
357
+ console.warn(`Warning: author (${(0, repo_1.resolveChainAuthor)(repoCfg)}) differs from signer (${signerAddress}). Push will use signer.`);
340
358
  }
341
359
  }
342
360
  async function assertResourcesOk(address) {
@@ -369,3 +387,201 @@ async function assertResourcesOk(address) {
369
387
  }
370
388
  return true;
371
389
  }
390
+ async function mantlePushAction(witPath, repoCfg) {
391
+ // eslint-disable-next-line no-console
392
+ console.log(ui_1.colors.header('Starting push (Mantle Mainnet)...'));
393
+ // 1. Prepare environment
394
+ const headRefPath = await (0, state_1.readHeadRefPath)(witPath);
395
+ const headId = await (0, state_1.readRef)(headRefPath);
396
+ if (!headId) {
397
+ throw new Error('No commits to push. Run `wit commit` first.');
398
+ }
399
+ const signerInfo = await (0, evmProvider_1.loadMantleSigner)(repoCfg.network);
400
+ const evmRepo = new evmRepo_1.EvmRepoService(signerInfo);
401
+ // eslint-disable-next-line no-console
402
+ console.log(ui_1.colors.cyan(`Using account ${signerInfo.address}`));
403
+ // 2. Check/Create Repo
404
+ let repoIdStr = repoCfg.repo_id;
405
+ let createdRepo = false;
406
+ let onchainState;
407
+ let repoId = BigInt(0);
408
+ if (repoIdStr && (/^\d+$/.test(repoIdStr) || /^0x[0-9a-fA-F]+$/.test(repoIdStr))) {
409
+ repoId = BigInt(repoIdStr);
410
+ try {
411
+ onchainState = await evmRepo.getRepoState(repoId);
412
+ }
413
+ catch (e) {
414
+ throw e; // Repository not found
415
+ }
416
+ }
417
+ else {
418
+ // Create new repo
419
+ const isPrivate = repoCfg.isPrivate === true;
420
+ repoId = await evmRepo.createRepo(repoCfg.repo_name || 'WitRepo', repoCfg.repo_name || 'WitRepo', isPrivate);
421
+ // Store as formatted hex string
422
+ repoCfg.repo_id = (0, evmRepo_1.formatRepoId)(repoId);
423
+ await (0, repo_1.writeRepoConfig)(witPath, repoCfg);
424
+ createdRepo = true;
425
+ // eslint-disable-next-line no-console
426
+ console.log(ui_1.colors.green(`Created on-chain repository ${repoCfg.repo_id} (Mantle Mainnet)`));
427
+ onchainState = {
428
+ headCommit: '',
429
+ version: 0n,
430
+ isPrivate,
431
+ headManifest: '',
432
+ headSnapshot: '',
433
+ rootHash: '',
434
+ parentCommit: '',
435
+ id: repoId,
436
+ name: repoCfg.repo_name || '',
437
+ description: '',
438
+ owner: signerInfo.address,
439
+ };
440
+ }
441
+ // 3. Collect Commits
442
+ const commitMap = await (0, state_1.readCommitIdMap)(witPath);
443
+ const { chain, baseRemoteId } = await collectChain(witPath, headId, commitMap);
444
+ if (onchainState.headCommit && onchainState.headCommit !== '' && baseRemoteId !== onchainState.headCommit) {
445
+ throw new Error('Remote head diverges from local history; run `wit pull`/`fetch` or reset first.');
446
+ }
447
+ if (commitMap[headId] && onchainState.headCommit === commitMap[headId]) {
448
+ // eslint-disable-next-line no-console
449
+ console.log(ui_1.colors.green('Remote already up to date.'));
450
+ return;
451
+ }
452
+ // 4. Lit + Upload Loop
453
+ // eslint-disable-next-line no-console
454
+ console.log(ui_1.colors.cyan(`Found ${chain.length} commit(s) to push.`));
455
+ let parentRemoteId = onchainState.headCommit || null;
456
+ if (parentRemoteId === '')
457
+ parentRemoteId = null;
458
+ let lastManifestId = null;
459
+ let lastCommitRemoteId = null;
460
+ let lastRootHash = '';
461
+ const nextMap = { ...commitMap };
462
+ // Initialize Lit Service
463
+ const litService = new lit_1.LitService();
464
+ const contractAddress = evmRepo.getAddress();
465
+ // Use user-provided CID for Lit Action (optimization)
466
+ const litActionCid = lit_1.LIT_ACTION_CID;
467
+ // eslint-disable-next-line no-console
468
+ console.log(ui_1.colors.gray(` Using Lit Action CID: ${litActionCid}`));
469
+ for (let i = 0; i < chain.length; i += 1) {
470
+ const item = chain[i];
471
+ // eslint-disable-next-line no-console
472
+ console.log(ui_1.colors.cyan(`Uploading commit ${i + 1}/${chain.length}: ${item.id}`));
473
+ // Upload logic
474
+ const entries = Object.entries(item.commit.tree.files).sort((a, b) => a[0].localeCompare(b[0]));
475
+ const filesStats = [];
476
+ // Process files (Encryption + Upload)
477
+ const isPrivateRepo = onchainState.isPrivate || repoCfg.isPrivate;
478
+ if (isPrivateRepo) {
479
+ // eslint-disable-next-line no-console
480
+ console.log(ui_1.colors.gray(` Encrypting ${entries.length} files (AES-256-GCM) & sealing with Lit Protocol...`));
481
+ }
482
+ else {
483
+ // eslint-disable-next-line no-console
484
+ console.log(ui_1.colors.gray(` Uploading ${entries.length} files (Public, no encryption)...`));
485
+ }
486
+ for (const [rel, meta] of entries) {
487
+ const buf = await (0, fs_1.readBlob)(witPath, meta.hash);
488
+ if (!buf)
489
+ throw new Error(`Missing blob for ${rel}`);
490
+ // Encryption
491
+ let contentToUpload = buf;
492
+ let encMetadata = undefined;
493
+ if (isPrivateRepo) {
494
+ // Generate Session Key
495
+ const sessionKey = (0, crypto_1.generateSessionKey)();
496
+ // Encrypt Content
497
+ const { ciphertext, iv, authTag } = (0, crypto_1.encryptBuffer)(buf, sessionKey);
498
+ contentToUpload = ciphertext;
499
+ // Lit Encrypt Session Key
500
+ const acc = litService.getAccessControlConditions(repoId.toString(), contractAddress);
501
+ const { ciphertext: litKey, dataToEncryptHash } = await litService.encryptSessionKey(sessionKey, acc);
502
+ encMetadata = {
503
+ alg: 'lit-aes-256-gcm',
504
+ lit_encrypted_key: litKey,
505
+ unified_access_control_conditions: acc,
506
+ lit_chain: 'mantle',
507
+ iv: iv.toString('hex'),
508
+ tag: authTag.toString('hex'),
509
+ lit_hash: dataToEncryptHash
510
+ };
511
+ }
512
+ // Upload to Lighthouse
513
+ const uploadRes = await (0, lighthouse_1.uploadBufferToLighthouse)(contentToUpload);
514
+ const cid = uploadRes.cid;
515
+ filesStats.push({
516
+ rel,
517
+ meta,
518
+ cid,
519
+ enc: encMetadata,
520
+ size: contentToUpload.length
521
+ });
522
+ }
523
+ const rootHash = (0, manifest_1.computeRootHash)(item.commit.tree.files);
524
+ lastRootHash = rootHash;
525
+ // Construct Manifest
526
+ const manifestFiles = {};
527
+ filesStats.forEach(f => {
528
+ manifestFiles[f.rel] = {
529
+ ...f.meta,
530
+ cid: f.cid,
531
+ ...(f.enc ? { enc: f.enc } : {})
532
+ };
533
+ });
534
+ const manifest = schema_1.ManifestSchema.parse({
535
+ version: 1,
536
+ root_hash: rootHash,
537
+ files: manifestFiles,
538
+ });
539
+ const manifestJson = (0, serialize_1.canonicalStringify)(manifest);
540
+ const manifestUpload = await (0, lighthouse_1.uploadTextToLighthouse)(manifestJson, `manifest-${item.id}`);
541
+ lastManifestId = manifestUpload.cid;
542
+ // Cache Manifest
543
+ await cacheJson(path_1.default.join(witPath, 'objects', 'manifests', `${(0, state_1.idToFileName)(lastManifestId)}.json`), manifestJson);
544
+ // Construct Remote Commit
545
+ const remoteCommit = {
546
+ tree: {
547
+ root_hash: rootHash,
548
+ manifest_cid: lastManifestId,
549
+ },
550
+ parent: parentRemoteId,
551
+ author: item.commit.author,
552
+ message: item.commit.message,
553
+ timestamp: item.commit.timestamp,
554
+ extras: { ...item.commit.extras }
555
+ };
556
+ const remoteJson = (0, serialize_1.canonicalStringify)(remoteCommit);
557
+ const commitUpload = await (0, lighthouse_1.uploadTextToLighthouse)(remoteJson, `commit-${item.id}`);
558
+ lastCommitRemoteId = commitUpload.cid;
559
+ // Cache Commit
560
+ await cacheJson(path_1.default.join(witPath, 'objects', 'commits', `${(0, state_1.idToFileName)(lastCommitRemoteId)}.json`), remoteJson);
561
+ // Update local map
562
+ nextMap[item.id] = lastCommitRemoteId;
563
+ parentRemoteId = lastCommitRemoteId;
564
+ // eslint-disable-next-line no-console
565
+ console.log(ui_1.colors.green(`Uploaded commit ${item.id} -> ${lastCommitRemoteId}`));
566
+ }
567
+ if (!lastCommitRemoteId || !lastManifestId) {
568
+ throw new Error("Push produced no commits.");
569
+ }
570
+ // 5. Update Contract
571
+ await (0, state_1.writeCommitIdMap)(witPath, nextMap);
572
+ await evmRepo.updateHead(repoId, lastCommitRemoteId, lastManifestId, '', lastRootHash, onchainState.version, onchainState.headCommit || '');
573
+ // eslint-disable-next-line no-console
574
+ console.log(ui_1.colors.cyan('On-chain head updated'));
575
+ const updatedRemote = {
576
+ repo_id: repoId.toString(),
577
+ head_commit: lastCommitRemoteId,
578
+ head_manifest: lastManifestId,
579
+ head_quilt: '',
580
+ version: Number(onchainState.version) + 1,
581
+ };
582
+ await (0, repo_1.writeRemoteState)(witPath, updatedRemote);
583
+ await (0, repo_1.writeRemoteRef)(witPath, lastCommitRemoteId);
584
+ // eslint-disable-next-line no-console
585
+ console.log(ui_1.colors.green('Push (Mantle) complete.'));
586
+ await litService.disconnect();
587
+ }
@@ -18,6 +18,55 @@ const walrusQuilt_1 = require("./walrusQuilt");
18
18
  const list_1 = require("./list");
19
19
  const transfer_1 = require("./transfer");
20
20
  const removeUser_1 = require("./removeUser");
21
+ const chain_1 = require("./chain");
22
+ const chain_2 = require("../lib/chain");
23
+ const repo_1 = require("../lib/repo");
24
+ const ipfsCar_1 = require("./ipfsCar");
25
+ const lighthouse_1 = require("./lighthouse");
26
+ const lighthouseDownload_1 = require("./lighthouseDownload");
27
+ const lighthousePin_1 = require("./lighthousePin");
28
+ function shouldSkipChainCheck(cmd) {
29
+ const parent = cmd.parent;
30
+ if (!parent)
31
+ return false;
32
+ if (parent.name() !== 'chain')
33
+ return false;
34
+ const name = cmd.name();
35
+ return name === 'list' || name === 'use' || name === 'current';
36
+ }
37
+ async function enforceRepoChain(cmd) {
38
+ if (shouldSkipChainCheck(cmd))
39
+ return;
40
+ let witPath;
41
+ try {
42
+ witPath = await (0, repo_1.requireWitDir)();
43
+ }
44
+ catch (err) {
45
+ if (err?.message?.includes('Not a wit repository'))
46
+ return;
47
+ throw err;
48
+ }
49
+ let repoCfg;
50
+ try {
51
+ repoCfg = await (0, repo_1.readRepoConfig)(witPath);
52
+ }
53
+ catch (err) {
54
+ if (err?.code === 'ENOENT')
55
+ return;
56
+ throw err;
57
+ }
58
+ const mismatch = await (0, repo_1.getRepoChainMismatch)(repoCfg);
59
+ if (!mismatch)
60
+ return;
61
+ if ('error' in mismatch) {
62
+ // eslint-disable-next-line no-console
63
+ console.error(ui_1.colors.red(mismatch.error));
64
+ process.exit(1);
65
+ }
66
+ // eslint-disable-next-line no-console
67
+ console.error(ui_1.colors.red((0, chain_2.formatChainMismatchMessage)(mismatch.repoChain, mismatch.activeChain)));
68
+ process.exit(1);
69
+ }
21
70
  function registerCommands(program) {
22
71
  // Global options (propagate to subcommands)
23
72
  program.option('--color', 'force color output').option('--no-color', 'disable color output');
@@ -94,7 +143,8 @@ function registerCommands(program) {
94
143
  .description('Add a collaborator to the repository')
95
144
  .option('--seal-policy <id>', 'Seal policy id to apply (defaults to repo config)')
96
145
  .option('--seal-secret <secret>', 'Seal secret to save locally when setting policy')
97
- .action((address) => (0, invite_1.inviteAction)(address));
146
+ .option('-r, --repo <repoId>', 'Repository ID (Mantle)')
147
+ .action(invite_1.inviteAction);
98
148
  program
99
149
  .command('transfer <new_owner>')
100
150
  .description('Transfer repository ownership to a new address')
@@ -102,6 +152,7 @@ function registerCommands(program) {
102
152
  program
103
153
  .command('remove-user <address>')
104
154
  .description('Remove a collaborator from the repository')
155
+ .option('-r, --repo <repoId>', 'Repository ID (Mantle)')
105
156
  .action(removeUser_1.removeUserAction);
106
157
  program
107
158
  .command('push-blob <path>')
@@ -150,12 +201,61 @@ function registerCommands(program) {
150
201
  .command('pull-quilt-legacy <blob_id> <out_dir>')
151
202
  .description('Download legacy archive and restore files (hash/root_hash verified)')
152
203
  .action((blobId, outDir) => (0, walrusQuilt_1.pullQuiltLegacyAction)(blobId, outDir));
204
+ program
205
+ .command('car-pack <input>')
206
+ .description('Pack a directory or file into a CAR snapshot')
207
+ .option('-o, --out <path>', 'output CAR file path')
208
+ .option('--no-wrap', 'do not wrap input with a directory')
209
+ .option('--root-out <path>', 'write root CID to a file')
210
+ .action((input, opts) => (0, ipfsCar_1.carPackAction)(input, opts));
211
+ program
212
+ .command('car-unpack <car_file> <out_dir>')
213
+ .description('Unpack a CAR snapshot to a directory')
214
+ .action((carFile, outDir) => (0, ipfsCar_1.carUnpackAction)(carFile, outDir));
215
+ program
216
+ .command('car-map <car_file>')
217
+ .description('Generate a path -> CID map from a CAR snapshot')
218
+ .option('-o, --out <path>', 'output JSON path (prints to stdout if omitted)')
219
+ .option('--no-strip-root', 'keep the top-level CAR root segment in paths')
220
+ .action((carFile, opts) => (0, ipfsCar_1.carMapAction)(carFile, opts));
221
+ program
222
+ .command('lighthouse-upload <file>')
223
+ .description('Upload a file to Lighthouse and print CID')
224
+ .option('--cid-version <n>', 'CID version (default 1)', (v) => parseInt(v, 10), 1)
225
+ .option('--progress', 'show upload progress')
226
+ .option('--retries <n>', 'retry attempts (default 3)', (v) => parseInt(v, 10), 3)
227
+ .option('--retry-delay <ms>', 'base retry delay in ms (default 1000)', (v) => parseInt(v, 10), 1000)
228
+ .option('--no-cache', 'disable local upload cache')
229
+ .action((file, opts) => (0, lighthouse_1.lighthouseUploadAction)(file, opts));
230
+ program
231
+ .command('lighthouse-download <cid>')
232
+ .description('Download a CID from Lighthouse gateway')
233
+ .option('-o, --out <path>', 'output file path (defaults to <cid>[.car])')
234
+ .option('--car', 'download as CAR (format=car)')
235
+ .option('--no-verify', 'disable CID verification (default true)')
236
+ .option('--retries <n>', 'retry attempts (default 3)', (v) => parseInt(v, 10), 3)
237
+ .option('--retry-delay <ms>', 'base retry delay in ms (default 500)', (v) => parseInt(v, 10), 500)
238
+ .option('--timeout <ms>', 'request timeout in ms (default 30000)', (v) => parseInt(v, 10), 30000)
239
+ .option('--gateway <url>', 'override gateway URL')
240
+ .action((cid, opts) => (0, lighthouseDownload_1.lighthouseDownloadAction)(cid, opts));
241
+ program
242
+ .command('lighthouse-pin <cid>')
243
+ .description('Pin a CID using Lighthouse API')
244
+ .option('--pin-url <url>', 'override Lighthouse pin URL')
245
+ .option('--retries <n>', 'retry attempts (default 3)', (v) => parseInt(v, 10), 3)
246
+ .option('--retry-delay <ms>', 'base retry delay in ms (default 1000)', (v) => parseInt(v, 10), 1000)
247
+ .option('--timeout <ms>', 'request timeout in ms (default 30000)', (v) => parseInt(v, 10), 30000)
248
+ .action((cid, opts) => (0, lighthousePin_1.lighthousePinAction)(cid, opts));
153
249
  program
154
250
  .command('list')
155
251
  .description('List repositories you own or collaborate on')
156
252
  .option('--owned', 'Show only owned repositories')
157
253
  .option('--collaborated', 'Show only collaborated repositories')
158
254
  .action(list_1.listAction);
255
+ const chain = program.command('chain').description('Manage active chain');
256
+ chain.command('list').description('List supported chains').action(chain_1.chainListAction);
257
+ chain.command('use <chain>').description('Set active chain').action(chain_1.chainUseAction);
258
+ chain.command('current').description('Show active chain').action(chain_1.chainCurrentAction);
159
259
  const account = program.command('account').description('Manage wit accounts (keys, active address)');
160
260
  account.command('list').description('List locally stored accounts (keys) and show active').action(account_1.accountListAction);
161
261
  account.command('use <address>').description('Set active account address (updates ~/.witconfig, author if unknown)').action(account_1.accountUseAction);
@@ -164,12 +264,17 @@ function registerCommands(program) {
164
264
  .option('--alias <name>', 'alias to record in key file (defaults to "default")')
165
265
  .description('Generate a new account (keypair), set as active, and update author if unknown')
166
266
  .action(account_1.accountGenerateAction);
267
+ account
268
+ .command('import <private_key>')
269
+ .option('--alias <name>', 'alias to record in key file (defaults to "default")')
270
+ .description('Import a private key for the active chain and set as active')
271
+ .action((privateKey, opts) => (0, account_1.accountImportAction)(privateKey, opts));
167
272
  account
168
273
  .command('balance')
169
274
  .argument('[address]', 'Address to query (defaults to active)')
170
275
  .description('Show SUI/WAL balance for the address (defaults to active)')
171
276
  .action((address) => (0, account_1.accountBalanceAction)(address));
172
- program.hook('preAction', (cmd) => {
277
+ program.hook('preAction', async (cmd) => {
173
278
  const opts = cmd.optsWithGlobals ? cmd.optsWithGlobals() : program.opts();
174
279
  const envDefault = process.env.WIT_NO_COLOR === undefined &&
175
280
  process.env.NO_COLOR === undefined &&
@@ -179,5 +284,6 @@ function registerCommands(program) {
179
284
  if (!desired && (0, ui_1.colorsEnabled)()) {
180
285
  (0, ui_1.setColorsEnabled)(false);
181
286
  }
287
+ await enforceRepoChain(cmd);
182
288
  });
183
289
  }
@@ -0,0 +1,46 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.removeUserAction = removeUserAction;
4
+ const evmRepo_1 = require("../lib/evmRepo");
5
+ const evmProvider_1 = require("../lib/evmProvider");
6
+ const repo_1 = require("../lib/repo");
7
+ const ui_1 = require("../lib/ui");
8
+ async function removeUserAction(address, options) {
9
+ try {
10
+ // 1. Resolve Config
11
+ let repoIdStr = options.repo;
12
+ if (!repoIdStr) {
13
+ // Try to load from current directory
14
+ try {
15
+ const witPath = await (0, repo_1.requireWitDir)(process.cwd());
16
+ const config = await (0, repo_1.readRepoConfig)(witPath);
17
+ if (config.chain !== 'mantle') {
18
+ throw new Error('This command is only supported for Mantle repositories.');
19
+ }
20
+ repoIdStr = config.repo_id || undefined;
21
+ }
22
+ catch (e) {
23
+ // Ignore, rely on argument
24
+ }
25
+ }
26
+ if (!repoIdStr) {
27
+ // eslint-disable-next-line no-console
28
+ console.error(ui_1.colors.red('Error: Repository ID is required. Run inside a wit repo or use --repo <id>.'));
29
+ process.exit(1);
30
+ }
31
+ // Normalization
32
+ if (repoIdStr.startsWith('mantle:')) {
33
+ repoIdStr = repoIdStr.split(':').pop();
34
+ }
35
+ const repoId = BigInt(repoIdStr);
36
+ // 2. Connect to Mantle
37
+ const signerCtx = await (0, evmProvider_1.loadMantleSigner)();
38
+ const repoService = new evmRepo_1.EvmRepoService(signerCtx);
39
+ // 3. Remove Collaborator
40
+ await repoService.removeCollaborator(repoId, address);
41
+ }
42
+ catch (err) {
43
+ console.error(ui_1.colors.red(`Command failed: ${err.message}`));
44
+ process.exit(1);
45
+ }
46
+ }
@@ -7,9 +7,14 @@ const walrus_1 = require("../lib/walrus");
7
7
  const suiRepo_1 = require("../lib/suiRepo");
8
8
  const repo_1 = require("../lib/repo");
9
9
  const ui_1 = require("../lib/ui");
10
- async function removeUserAction(addressToRemove) {
10
+ const evmRepo_1 = require("../lib/evmRepo");
11
+ const evmProvider_1 = require("../lib/evmProvider");
12
+ async function removeUserAction(addressToRemove, options) {
11
13
  const witPath = await (0, repo_1.requireWitDir)();
12
14
  const repoCfg = await (0, repo_1.readRepoConfig)(witPath);
15
+ if (repoCfg.chain === 'mantle') {
16
+ return removeUserActionMantle(addressToRemove, options, repoCfg);
17
+ }
13
18
  if (!repoCfg.repo_id) {
14
19
  throw new Error('Repository not initialized on chain. Run `wit push` first.');
15
20
  }
@@ -61,3 +66,27 @@ async function removeUserAction(addressToRemove) {
61
66
  process.exit(1);
62
67
  }
63
68
  }
69
+ async function removeUserActionMantle(address, options, config) {
70
+ try {
71
+ let repoIdStr = options.repo || config.repo_id;
72
+ if (!repoIdStr) {
73
+ // eslint-disable-next-line no-console
74
+ console.error(ui_1.colors.red('Error: Repository ID is required. Run inside a wit repo or use --repo <id>.'));
75
+ process.exit(1);
76
+ }
77
+ // Normalization
78
+ if (repoIdStr.startsWith('mantle:')) {
79
+ repoIdStr = repoIdStr.split(':').pop();
80
+ }
81
+ const repoId = BigInt(repoIdStr);
82
+ // 2. Connect to Mantle
83
+ const signerCtx = await (0, evmProvider_1.loadMantleSigner)();
84
+ const repoService = new evmRepo_1.EvmRepoService(signerCtx);
85
+ // 3. Remove Collaborator
86
+ await repoService.removeCollaborator(repoId, address);
87
+ }
88
+ catch (err) {
89
+ console.error(ui_1.colors.red(`Command failed: ${err.message}`));
90
+ process.exit(1);
91
+ }
92
+ }
package/dist/index.js CHANGED
@@ -15,6 +15,21 @@ if (!Array.prototype.toReversed) {
15
15
  return [...this].reverse();
16
16
  };
17
17
  }
18
+ // Polyfill for global crypto (Node environment for Lit SDK)
19
+ const crypto_1 = __importDefault(require("crypto"));
20
+ if (!globalThis.crypto) {
21
+ globalThis.crypto = crypto_1.default;
22
+ }
23
+ // Suppress noisy Lit SDK deprecation warnings
24
+ const originalWarn = console.warn;
25
+ console.warn = (...args) => {
26
+ if (args.length > 0 &&
27
+ typeof args[0] === 'string' &&
28
+ args[0].includes('deprecated LogLevel is deprecated')) {
29
+ return;
30
+ }
31
+ originalWarn(...args);
32
+ };
18
33
  const VERSION = package_json_1.default.version || '0.0.0';
19
34
  async function run(argv = process.argv) {
20
35
  const program = new commander_1.Command();
@@ -0,0 +1,72 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.listSupportedChains = listSupportedChains;
7
+ exports.readActiveChain = readActiveChain;
8
+ exports.setActiveChain = setActiveChain;
9
+ exports.normalizeChain = normalizeChain;
10
+ exports.formatChainMismatchMessage = formatChainMismatchMessage;
11
+ const promises_1 = __importDefault(require("fs/promises"));
12
+ const os_1 = __importDefault(require("os"));
13
+ const path_1 = __importDefault(require("path"));
14
+ const CHAINS = [
15
+ { id: 'sui', label: 'Sui Testnet' },
16
+ { id: 'mantle', label: 'Mantle Mainnet' },
17
+ ];
18
+ const DEFAULT_CHAIN = 'sui';
19
+ const GLOBAL_CONFIG_PATH = path_1.default.join(os_1.default.homedir(), '.witconfig');
20
+ function listSupportedChains() {
21
+ return [...CHAINS];
22
+ }
23
+ async function readActiveChain() {
24
+ const cfg = await readGlobalConfig();
25
+ const normalized = normalizeChainMaybe(cfg.active_chain);
26
+ return normalized ?? DEFAULT_CHAIN;
27
+ }
28
+ async function setActiveChain(chain) {
29
+ await updateGlobalConfig((cfg) => ({ ...cfg, active_chain: chain }));
30
+ }
31
+ function normalizeChain(input) {
32
+ const normalized = normalizeChainMaybe(input);
33
+ if (!normalized) {
34
+ const choices = CHAINS.map((chain) => chain.id).join(', ');
35
+ throw new Error(`Unknown chain "${input}". Supported: ${choices}.`);
36
+ }
37
+ return normalized;
38
+ }
39
+ function formatChainMismatchMessage(repoChain, activeChain) {
40
+ return (`Repository chain is ${repoChain}, but active chain is ${activeChain}. ` +
41
+ `Run \`wit chain use ${repoChain}\` and retry.`);
42
+ }
43
+ function normalizeChainMaybe(input) {
44
+ if (!input)
45
+ return null;
46
+ const value = input.trim().toLowerCase();
47
+ if (value === 'sui')
48
+ return 'sui';
49
+ if (value === 'mantle')
50
+ return 'mantle';
51
+ if (value === 'mantle-testnet')
52
+ return 'mantle';
53
+ return null;
54
+ }
55
+ async function readGlobalConfig() {
56
+ try {
57
+ const raw = await promises_1.default.readFile(GLOBAL_CONFIG_PATH, 'utf8');
58
+ return JSON.parse(raw);
59
+ }
60
+ catch (err) {
61
+ if (err?.code === 'ENOENT')
62
+ return {};
63
+ // eslint-disable-next-line no-console
64
+ console.warn(`Warning: could not read ${GLOBAL_CONFIG_PATH}: ${err.message}`);
65
+ return {};
66
+ }
67
+ }
68
+ async function updateGlobalConfig(mutator) {
69
+ const cfg = await readGlobalConfig();
70
+ const next = mutator(cfg);
71
+ await promises_1.default.writeFile(GLOBAL_CONFIG_PATH, JSON.stringify(next, null, 2) + '\n', 'utf8');
72
+ }