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/dist/commands/push.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
65
|
-
|
|
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
|
|
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 = (
|
|
346
|
+
const current = (0, repo_1.resolveChainAuthor)(repoCfg).trim().toLowerCase();
|
|
329
347
|
const signer = signerAddress.toLowerCase();
|
|
330
348
|
if (!current || current === 'unknown') {
|
|
331
|
-
|
|
332
|
-
await (0, repo_1.writeRepoConfig)(witPath,
|
|
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
|
|
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
|
-
.
|
|
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
|
-
|
|
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
|
+
}
|