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.
@@ -2,71 +2,86 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.inviteAction = inviteAction;
4
4
  const client_1 = require("@mysten/sui/client");
5
- const ui_1 = require("../lib/ui");
6
- const repo_1 = require("../lib/repo");
7
- const walrus_1 = require("../lib/walrus");
8
5
  const keys_1 = require("../lib/keys");
6
+ const walrus_1 = require("../lib/walrus");
9
7
  const suiRepo_1 = require("../lib/suiRepo");
10
- async function inviteAction(address) {
11
- if (!address) {
12
- throw new Error('Usage: wit invite <address>');
13
- }
14
- // eslint-disable-next-line no-console
15
- console.log(ui_1.colors.header('Adding collaborator...'));
16
- let witPath;
8
+ const evmRepo_1 = require("../lib/evmRepo");
9
+ const evmProvider_1 = require("../lib/evmProvider");
10
+ const repo_1 = require("../lib/repo");
11
+ const ui_1 = require("../lib/ui");
12
+ async function inviteAction(address, options) {
17
13
  try {
18
- witPath = await (0, repo_1.requireWitDir)();
14
+ const witPath = await (0, repo_1.requireWitDir)();
15
+ const repoCfg = await (0, repo_1.readRepoConfig)(witPath);
16
+ if (repoCfg.chain !== 'mantle') {
17
+ // SUI Logic (Default)
18
+ return inviteActionSui(address, options, repoCfg);
19
+ }
20
+ // Mantle Logic
21
+ let repoIdStr = options.repo || repoCfg.repo_id;
22
+ if (!repoIdStr) {
23
+ // eslint-disable-next-line no-console
24
+ console.error(ui_1.colors.red('Error: Repository ID is required. Run inside a wit repo or use --repo <id>.'));
25
+ process.exit(1);
26
+ }
27
+ if (repoCfg.isPrivate === false) {
28
+ console.log(ui_1.colors.yellow('Warning: This repository is public. Collaborators are not needed for read access (but may be needed for write access).'));
29
+ }
30
+ if (repoIdStr.startsWith('mantle:')) {
31
+ repoIdStr = repoIdStr.split(':').pop();
32
+ }
33
+ const repoId = BigInt(repoIdStr);
34
+ // 2. Connect to Mantle
35
+ const signerCtx = await (0, evmProvider_1.loadMantleSigner)();
36
+ const repoService = new evmRepo_1.EvmRepoService(signerCtx);
37
+ // 3. Add Collaborator
38
+ await repoService.addCollaborator(repoId, address);
19
39
  }
20
40
  catch (err) {
21
- // eslint-disable-next-line no-console
22
- console.log(ui_1.colors.red(err?.message || 'Not a wit repository. Run `wit init` first.'));
23
- return;
41
+ console.error(ui_1.colors.red(`Command failed: ${err.message}`));
42
+ process.exit(1);
24
43
  }
25
- const repoCfg = await (0, repo_1.readRepoConfig)(witPath);
44
+ }
45
+ async function inviteActionSui(address, options, repoCfg) {
26
46
  if (!repoCfg.repo_id) {
27
- throw new Error('Missing repo_id. Run `wit push` once to create the remote repository.');
47
+ throw new Error('Repository not initialized on chain. Run `wit push` first.');
28
48
  }
29
- const signerInfo = await (0, keys_1.loadSigner)();
30
- const resolved = await (0, walrus_1.resolveWalrusConfig)(process.cwd());
31
- const suiClient = new client_1.SuiClient({ url: resolved.suiRpcUrl });
32
- // Check if repo is private by fetching on-chain state
33
- // We could rely on local config, but on-chain is truth.
49
+ const signer = await (0, keys_1.loadSigner)();
50
+ const signerAddr = signer.address;
51
+ console.log(ui_1.colors.header(`Adding collaborator ${address} to ${repoCfg.repo_id}...`));
52
+ const res = await (0, keys_1.checkResources)(signerAddr);
53
+ if (res.hasMinSui === false) {
54
+ throw new Error(`Insufficient SUI balance. Need at least ${res.minSui} MIST.`);
55
+ }
56
+ const config = await (0, walrus_1.resolveWalrusConfig)();
57
+ const client = new client_1.SuiClient({ url: config.suiRpcUrl });
34
58
  let whitelistId;
35
- try {
36
- const state = await (0, suiRepo_1.fetchRepositoryState)(suiClient, repoCfg.repo_id);
37
- if (state.sealPolicyId) {
38
- whitelistId = state.sealPolicyId;
39
- // Update local config if missing
40
- if (repoCfg.seal_policy_id !== whitelistId) {
41
- repoCfg.seal_policy_id = whitelistId;
42
- await (0, repo_1.writeRepoConfig)(witPath, repoCfg);
59
+ // Attempt to auto-detect if private
60
+ if (repoCfg.seal_policy_id) {
61
+ whitelistId = repoCfg.seal_policy_id;
62
+ }
63
+ else {
64
+ try {
65
+ const state = await (0, suiRepo_1.fetchRepositoryState)(client, repoCfg.repo_id);
66
+ if (state.sealPolicyId) {
67
+ whitelistId = state.sealPolicyId;
43
68
  }
44
69
  }
45
- }
46
- catch (err) {
47
- // ignore fetch error, assume public or will fail later
48
- }
49
- try {
50
- await (0, suiRepo_1.addCollaborator)(suiClient, signerInfo.signer, {
51
- repoId: repoCfg.repo_id,
52
- collaborator: address,
53
- whitelistId
54
- });
55
- // eslint-disable-next-line no-console
56
- console.log(ui_1.colors.green(`Added ${ui_1.colors.hash(address)} as collaborator.`));
57
- if (whitelistId) {
58
- // eslint-disable-next-line no-console
59
- console.log(ui_1.colors.cyan(`User added to Whitelist (${whitelistId}). They can now decrypt the repository.`));
70
+ catch (err) {
71
+ // ignore
60
72
  }
61
73
  }
62
- catch (err) {
63
- const msg = err?.message || String(err);
64
- if (msg.includes('ENotAuthorized') || msg.includes('NotAuthorized')) {
65
- // eslint-disable-next-line no-console
66
- console.log(ui_1.colors.red('Add failed: current account is not authorized (owner or collaborator required).'));
67
- return;
68
- }
69
- // eslint-disable-next-line no-console
70
- console.log(ui_1.colors.red(`Add collaborator failed: ${msg}`));
74
+ // Allow override
75
+ if (options.sealPolicy) {
76
+ whitelistId = options.sealPolicy;
77
+ }
78
+ await (0, suiRepo_1.addCollaborator)(client, signer.signer, {
79
+ repoId: repoCfg.repo_id,
80
+ collaborator: address,
81
+ whitelistId
82
+ });
83
+ console.log(ui_1.colors.green(`Collaborator ${address} added successfully.`));
84
+ if (whitelistId) {
85
+ console.log(ui_1.colors.gray(`(Added to private whitelist ${whitelistId})`));
71
86
  }
72
87
  }
@@ -0,0 +1,98 @@
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.carPackAction = carPackAction;
7
+ exports.carUnpackAction = carUnpackAction;
8
+ exports.carMapAction = carMapAction;
9
+ const promises_1 = __importDefault(require("fs/promises"));
10
+ const path_1 = __importDefault(require("path"));
11
+ const ui_1 = require("../lib/ui");
12
+ const ipfsCar_1 = require("../lib/ipfsCar");
13
+ async function carPackAction(input, opts) {
14
+ try {
15
+ if (!input) {
16
+ printError('Input path is required. Usage: wit car-pack <input> --out <file.car>');
17
+ return;
18
+ }
19
+ const absInput = path_1.default.resolve(input);
20
+ const output = resolveOutputPath(absInput, opts.out);
21
+ const result = await (0, ipfsCar_1.packCar)(absInput, output, { wrap: opts.wrap !== false });
22
+ if (opts.rootOut) {
23
+ await promises_1.default.writeFile(path_1.default.resolve(opts.rootOut), `${result.root}\n`, 'utf8');
24
+ }
25
+ // eslint-disable-next-line no-console
26
+ console.log(ui_1.colors.green('CAR snapshot created.'));
27
+ // eslint-disable-next-line no-console
28
+ console.log(` root: ${ui_1.colors.hash(result.root)}`);
29
+ // eslint-disable-next-line no-console
30
+ console.log(` output: ${result.output}`);
31
+ }
32
+ catch (err) {
33
+ printError(errorMessage(err));
34
+ }
35
+ }
36
+ async function carUnpackAction(carFile, outDir) {
37
+ try {
38
+ if (!carFile || !outDir) {
39
+ printError('CAR file and output directory are required. Usage: wit car-unpack <file.car> <out_dir>');
40
+ return;
41
+ }
42
+ await (0, ipfsCar_1.unpackCar)(carFile, outDir);
43
+ // eslint-disable-next-line no-console
44
+ console.log(ui_1.colors.green('CAR snapshot unpacked.'));
45
+ // eslint-disable-next-line no-console
46
+ console.log(` input: ${path_1.default.resolve(carFile)}`);
47
+ // eslint-disable-next-line no-console
48
+ console.log(` output: ${path_1.default.resolve(outDir)}`);
49
+ }
50
+ catch (err) {
51
+ printError(errorMessage(err));
52
+ }
53
+ }
54
+ async function carMapAction(carFile, opts) {
55
+ try {
56
+ if (!carFile) {
57
+ printError('CAR file is required. Usage: wit car-map <file.car> --out <path>');
58
+ return;
59
+ }
60
+ const result = await (0, ipfsCar_1.mapCarPaths)(carFile, { stripRoot: opts.stripRoot !== false });
61
+ const payload = JSON.stringify(result.map, null, 2) + '\n';
62
+ if (opts.out) {
63
+ const outPath = path_1.default.resolve(opts.out);
64
+ await promises_1.default.writeFile(outPath, payload, 'utf8');
65
+ // eslint-disable-next-line no-console
66
+ console.log(ui_1.colors.green('CAR path map written.'));
67
+ // eslint-disable-next-line no-console
68
+ console.log(` root: ${ui_1.colors.hash(result.root)}`);
69
+ // eslint-disable-next-line no-console
70
+ console.log(` output: ${outPath}`);
71
+ // eslint-disable-next-line no-console
72
+ console.log(` entries: ${Object.keys(result.map).length}`);
73
+ }
74
+ else {
75
+ // eslint-disable-next-line no-console
76
+ console.log(payload.trimEnd());
77
+ }
78
+ }
79
+ catch (err) {
80
+ printError(errorMessage(err));
81
+ }
82
+ }
83
+ function resolveOutputPath(input, out) {
84
+ if (out)
85
+ return path_1.default.resolve(out);
86
+ const base = path_1.default.basename(input === '.' ? process.cwd() : input);
87
+ return path_1.default.resolve(`${base}.car`);
88
+ }
89
+ function printError(message) {
90
+ // eslint-disable-next-line no-console
91
+ console.error(ui_1.colors.red(message));
92
+ process.exitCode = 1;
93
+ }
94
+ function errorMessage(err) {
95
+ if (err instanceof Error && err.message)
96
+ return err.message;
97
+ return String(err);
98
+ }
@@ -0,0 +1,97 @@
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.lighthouseUploadAction = lighthouseUploadAction;
7
+ const path_1 = __importDefault(require("path"));
8
+ const ui_1 = require("../lib/ui");
9
+ const lighthouse_1 = require("../lib/lighthouse");
10
+ async function lighthouseUploadAction(filePath, opts) {
11
+ try {
12
+ if (!filePath) {
13
+ printError('File path is required. Usage: wit lighthouse-upload <file>');
14
+ return;
15
+ }
16
+ const apiKey = (0, lighthouse_1.resolveLighthouseApiKey)();
17
+ if (!apiKey) {
18
+ printError('Missing LIGHTHOUSE_API_KEY. Set it in .env, ~/.witconfig, or export it before running this command.');
19
+ return;
20
+ }
21
+ const cidVersion = parseCidVersion(opts.cidVersion);
22
+ const retries = parseOptionalNumber(opts.retries, 3);
23
+ const retryDelay = parseOptionalNumber(opts.retryDelay, 1000);
24
+ const absPath = path_1.default.resolve(filePath);
25
+ const progress = opts.progress ? createProgressReporter() : undefined;
26
+ const totalAttempts = retries + 1;
27
+ const result = await (0, lighthouse_1.uploadFileToLighthouse)(absPath, {
28
+ apiKey,
29
+ cidVersion,
30
+ onProgress: progress,
31
+ useCache: opts.cache !== false,
32
+ retries,
33
+ retryDelayMs: retryDelay,
34
+ onRetry: (attempt, err, delayMs) => {
35
+ const message = err?.message || String(err);
36
+ // eslint-disable-next-line no-console
37
+ console.warn(ui_1.colors.yellow(`Upload attempt ${attempt}/${totalAttempts} failed: ${message}. Retrying in ${delayMs}ms...`));
38
+ },
39
+ });
40
+ if (progress && !result.fromCache)
41
+ progress(100);
42
+ // eslint-disable-next-line no-console
43
+ console.log(ui_1.colors.green(result.fromCache ? 'Cache hit. Using existing Lighthouse CID.' : 'Uploaded to Lighthouse.'));
44
+ // eslint-disable-next-line no-console
45
+ console.log(` cid: ${ui_1.colors.hash(result.cid)}`);
46
+ // eslint-disable-next-line no-console
47
+ console.log(` name: ${result.name ?? path_1.default.basename(absPath)}`);
48
+ if (result.size) {
49
+ // eslint-disable-next-line no-console
50
+ console.log(` size: ${result.size}`);
51
+ }
52
+ }
53
+ catch (err) {
54
+ printError(errorMessage(err));
55
+ }
56
+ }
57
+ function parseCidVersion(value) {
58
+ if (value === undefined || value === null)
59
+ return 1;
60
+ if (typeof value === 'number')
61
+ return Number.isNaN(value) ? 1 : value;
62
+ const parsed = Number.parseInt(value, 10);
63
+ return Number.isNaN(parsed) ? 1 : parsed;
64
+ }
65
+ function parseOptionalNumber(value, fallback) {
66
+ if (value === undefined || value === null)
67
+ return fallback;
68
+ if (typeof value === 'number')
69
+ return Number.isNaN(value) ? fallback : value;
70
+ const parsed = Number.parseInt(value, 10);
71
+ return Number.isNaN(parsed) ? fallback : parsed;
72
+ }
73
+ function createProgressReporter() {
74
+ let last = -1;
75
+ return (progress) => {
76
+ const value = Math.max(0, Math.min(100, Math.round(progress)));
77
+ if (value === last)
78
+ return;
79
+ last = value;
80
+ // eslint-disable-next-line no-console
81
+ process.stdout.write(`\rUploading... ${value}%`);
82
+ if (value === 100) {
83
+ // eslint-disable-next-line no-console
84
+ process.stdout.write('\n');
85
+ }
86
+ };
87
+ }
88
+ function printError(message) {
89
+ // eslint-disable-next-line no-console
90
+ console.error(ui_1.colors.red(message));
91
+ process.exitCode = 1;
92
+ }
93
+ function errorMessage(err) {
94
+ if (err instanceof Error && err.message)
95
+ return err.message;
96
+ return String(err);
97
+ }
@@ -0,0 +1,58 @@
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.lighthouseDownloadAction = lighthouseDownloadAction;
7
+ const promises_1 = __importDefault(require("fs/promises"));
8
+ const path_1 = __importDefault(require("path"));
9
+ const ui_1 = require("../lib/ui");
10
+ const lighthouse_1 = require("../lib/lighthouse");
11
+ async function lighthouseDownloadAction(cid, opts) {
12
+ try {
13
+ if (!cid) {
14
+ printError('CID is required. Usage: wit lighthouse-download <cid> --out <path>');
15
+ return;
16
+ }
17
+ const format = opts.car ? 'car' : 'raw';
18
+ const output = resolveOutputPath(cid, format, opts.out);
19
+ const gateway = opts.gateway || (0, lighthouse_1.resolveLighthouseGatewayUrl)();
20
+ const result = await (0, lighthouse_1.downloadFromLighthouseGateway)(cid, {
21
+ format,
22
+ verify: opts.verify !== false,
23
+ retries: opts.retries,
24
+ retryDelayMs: opts.retryDelay,
25
+ timeoutMs: opts.timeout,
26
+ gatewayUrl: gateway,
27
+ });
28
+ await promises_1.default.mkdir(path_1.default.dirname(output), { recursive: true });
29
+ await promises_1.default.writeFile(output, result.bytes);
30
+ // eslint-disable-next-line no-console
31
+ console.log(ui_1.colors.green('Downloaded from Lighthouse.'));
32
+ // eslint-disable-next-line no-console
33
+ console.log(` cid: ${ui_1.colors.hash(cid)}`);
34
+ // eslint-disable-next-line no-console
35
+ console.log(` gateway: ${result.gateway}`);
36
+ // eslint-disable-next-line no-console
37
+ console.log(` output: ${output}`);
38
+ }
39
+ catch (err) {
40
+ printError(errorMessage(err));
41
+ }
42
+ }
43
+ function resolveOutputPath(cid, format, out) {
44
+ if (out)
45
+ return path_1.default.resolve(out);
46
+ const suffix = format === 'car' ? '.car' : '';
47
+ return path_1.default.resolve(`${cid}${suffix}`);
48
+ }
49
+ function printError(message) {
50
+ // eslint-disable-next-line no-console
51
+ console.error(ui_1.colors.red(message));
52
+ process.exitCode = 1;
53
+ }
54
+ function errorMessage(err) {
55
+ if (err instanceof Error && err.message)
56
+ return err.message;
57
+ return String(err);
58
+ }
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.lighthousePinAction = lighthousePinAction;
4
+ const ui_1 = require("../lib/ui");
5
+ const lighthouse_1 = require("../lib/lighthouse");
6
+ async function lighthousePinAction(cid, opts) {
7
+ try {
8
+ if (!cid) {
9
+ printError('CID is required. Usage: wit lighthouse-pin <cid>');
10
+ return;
11
+ }
12
+ const apiKey = (0, lighthouse_1.resolveLighthouseApiKey)();
13
+ if (!apiKey) {
14
+ printError('Missing LIGHTHOUSE_API_KEY. Set it in .env, ~/.witconfig, or export it before running this command.');
15
+ return;
16
+ }
17
+ const pinUrl = opts.pinUrl || (0, lighthouse_1.resolveLighthousePinUrl)() || undefined;
18
+ const retries = parseOptionalNumber(opts.retries, 3);
19
+ const retryDelay = parseOptionalNumber(opts.retryDelay, 1000);
20
+ const timeout = parseOptionalNumber(opts.timeout, 30000);
21
+ const totalAttempts = retries + 1;
22
+ const result = await (0, lighthouse_1.pinCidWithLighthouse)(cid, {
23
+ apiKey,
24
+ pinUrl,
25
+ retries,
26
+ retryDelayMs: retryDelay,
27
+ timeoutMs: timeout,
28
+ onRetry: (attempt, err, delayMs) => {
29
+ const message = err?.message || String(err);
30
+ // eslint-disable-next-line no-console
31
+ console.warn(ui_1.colors.yellow(`Pin attempt ${attempt}/${totalAttempts} failed: ${message}. Retrying in ${delayMs}ms...`));
32
+ },
33
+ });
34
+ // eslint-disable-next-line no-console
35
+ console.log(ui_1.colors.green('Pinned on Lighthouse.'));
36
+ // eslint-disable-next-line no-console
37
+ console.log(` cid: ${ui_1.colors.hash(result.cid)}`);
38
+ // eslint-disable-next-line no-console
39
+ console.log(` pin url: ${result.pinUrl}`);
40
+ }
41
+ catch (err) {
42
+ printError(errorMessage(err));
43
+ }
44
+ }
45
+ function parseOptionalNumber(value, fallback) {
46
+ if (value === undefined || value === null)
47
+ return fallback;
48
+ if (typeof value === 'number')
49
+ return Number.isNaN(value) ? fallback : value;
50
+ const parsed = Number.parseInt(value, 10);
51
+ return Number.isNaN(parsed) ? fallback : parsed;
52
+ }
53
+ function printError(message) {
54
+ // eslint-disable-next-line no-console
55
+ console.error(ui_1.colors.red(message));
56
+ process.exitCode = 1;
57
+ }
58
+ function errorMessage(err) {
59
+ if (err instanceof Error && err.message)
60
+ return err.message;
61
+ return String(err);
62
+ }
@@ -14,6 +14,7 @@ const state_1 = require("../lib/state");
14
14
  const fs_1 = require("../lib/fs");
15
15
  const schema_1 = require("../lib/schema");
16
16
  const manifest_1 = require("../lib/manifest");
17
+ const serialize_1 = require("../lib/serialize");
17
18
  const walrus_1 = require("../lib/walrus");
18
19
  async function pullAction() {
19
20
  // eslint-disable-next-line no-console
@@ -156,5 +157,5 @@ function manifestIdToFile(id) {
156
157
  return id.replace(/\//g, '_').replace(/\+/g, '-');
157
158
  }
158
159
  function canonicalManifest(m) {
159
- return JSON.stringify(m, null, 2) + '\n';
160
+ return (0, serialize_1.canonicalStringify)(m);
160
161
  }