@btc-vision/cli 1.0.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.
Files changed (110) hide show
  1. package/.gitattributes +2 -0
  2. package/.github/dependabot.yml +9 -0
  3. package/.github/workflows/ci.yml +48 -0
  4. package/.prettierrc.json +12 -0
  5. package/CONTRIBUTING.md +56 -0
  6. package/LICENSE +190 -0
  7. package/NOTICE +17 -0
  8. package/README.md +509 -0
  9. package/SECURITY.md +35 -0
  10. package/build/commands/AcceptCommand.d.ts +7 -0
  11. package/build/commands/AcceptCommand.js +110 -0
  12. package/build/commands/BaseCommand.d.ts +12 -0
  13. package/build/commands/BaseCommand.js +27 -0
  14. package/build/commands/CompileCommand.d.ts +7 -0
  15. package/build/commands/CompileCommand.js +138 -0
  16. package/build/commands/ConfigCommand.d.ts +17 -0
  17. package/build/commands/ConfigCommand.js +124 -0
  18. package/build/commands/DeprecateCommand.d.ts +7 -0
  19. package/build/commands/DeprecateCommand.js +112 -0
  20. package/build/commands/InfoCommand.d.ts +10 -0
  21. package/build/commands/InfoCommand.js +223 -0
  22. package/build/commands/InitCommand.d.ts +16 -0
  23. package/build/commands/InitCommand.js +336 -0
  24. package/build/commands/InstallCommand.d.ts +7 -0
  25. package/build/commands/InstallCommand.js +130 -0
  26. package/build/commands/KeygenCommand.d.ts +13 -0
  27. package/build/commands/KeygenCommand.js +133 -0
  28. package/build/commands/ListCommand.d.ts +7 -0
  29. package/build/commands/ListCommand.js +117 -0
  30. package/build/commands/LoginCommand.d.ts +9 -0
  31. package/build/commands/LoginCommand.js +139 -0
  32. package/build/commands/LogoutCommand.d.ts +7 -0
  33. package/build/commands/LogoutCommand.js +57 -0
  34. package/build/commands/PublishCommand.d.ts +7 -0
  35. package/build/commands/PublishCommand.js +163 -0
  36. package/build/commands/SearchCommand.d.ts +7 -0
  37. package/build/commands/SearchCommand.js +97 -0
  38. package/build/commands/SignCommand.d.ts +7 -0
  39. package/build/commands/SignCommand.js +80 -0
  40. package/build/commands/TransferCommand.d.ts +8 -0
  41. package/build/commands/TransferCommand.js +179 -0
  42. package/build/commands/UndeprecateCommand.d.ts +7 -0
  43. package/build/commands/UndeprecateCommand.js +95 -0
  44. package/build/commands/UpdateCommand.d.ts +7 -0
  45. package/build/commands/UpdateCommand.js +130 -0
  46. package/build/commands/VerifyCommand.d.ts +7 -0
  47. package/build/commands/VerifyCommand.js +167 -0
  48. package/build/commands/WhoamiCommand.d.ts +7 -0
  49. package/build/commands/WhoamiCommand.js +84 -0
  50. package/build/index.d.ts +2 -0
  51. package/build/index.js +64 -0
  52. package/build/lib/PackageRegistry.abi.d.ts +2 -0
  53. package/build/lib/PackageRegistry.abi.js +356 -0
  54. package/build/lib/binary.d.ts +16 -0
  55. package/build/lib/binary.js +165 -0
  56. package/build/lib/config.d.ts +11 -0
  57. package/build/lib/config.js +160 -0
  58. package/build/lib/credentials.d.ts +10 -0
  59. package/build/lib/credentials.js +89 -0
  60. package/build/lib/ipfs.d.ts +16 -0
  61. package/build/lib/ipfs.js +209 -0
  62. package/build/lib/manifest.d.ts +14 -0
  63. package/build/lib/manifest.js +88 -0
  64. package/build/lib/provider.d.ts +9 -0
  65. package/build/lib/provider.js +48 -0
  66. package/build/lib/registry.d.ts +58 -0
  67. package/build/lib/registry.js +197 -0
  68. package/build/lib/wallet.d.ts +32 -0
  69. package/build/lib/wallet.js +114 -0
  70. package/build/types/PackageRegistry.d.ts +177 -0
  71. package/build/types/PackageRegistry.js +1 -0
  72. package/build/types/index.d.ts +30 -0
  73. package/build/types/index.js +52 -0
  74. package/eslint.config.js +41 -0
  75. package/gulpfile.js +41 -0
  76. package/package.json +83 -0
  77. package/src/commands/AcceptCommand.ts +151 -0
  78. package/src/commands/BaseCommand.ts +59 -0
  79. package/src/commands/CompileCommand.ts +196 -0
  80. package/src/commands/ConfigCommand.ts +144 -0
  81. package/src/commands/DeprecateCommand.ts +156 -0
  82. package/src/commands/InfoCommand.ts +293 -0
  83. package/src/commands/InitCommand.ts +465 -0
  84. package/src/commands/InstallCommand.ts +179 -0
  85. package/src/commands/KeygenCommand.ts +157 -0
  86. package/src/commands/ListCommand.ts +169 -0
  87. package/src/commands/LoginCommand.ts +197 -0
  88. package/src/commands/LogoutCommand.ts +76 -0
  89. package/src/commands/PublishCommand.ts +230 -0
  90. package/src/commands/SearchCommand.ts +141 -0
  91. package/src/commands/SignCommand.ts +122 -0
  92. package/src/commands/TransferCommand.ts +235 -0
  93. package/src/commands/UndeprecateCommand.ts +134 -0
  94. package/src/commands/UpdateCommand.ts +200 -0
  95. package/src/commands/VerifyCommand.ts +228 -0
  96. package/src/commands/WhoamiCommand.ts +113 -0
  97. package/src/index.ts +86 -0
  98. package/src/lib/PackageRegistry.abi.json +765 -0
  99. package/src/lib/PackageRegistry.abi.ts +365 -0
  100. package/src/lib/binary.ts +336 -0
  101. package/src/lib/config.ts +265 -0
  102. package/src/lib/credentials.ts +176 -0
  103. package/src/lib/ipfs.ts +369 -0
  104. package/src/lib/manifest.ts +172 -0
  105. package/src/lib/provider.ts +121 -0
  106. package/src/lib/registry.ts +464 -0
  107. package/src/lib/wallet.ts +271 -0
  108. package/src/types/PackageRegistry.ts +344 -0
  109. package/src/types/index.ts +145 -0
  110. package/tsconfig.json +25 -0
@@ -0,0 +1,117 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { BaseCommand } from './BaseCommand.js';
4
+ import { parseOpnetBinary, formatFileSize } from '../lib/binary.js';
5
+ export class ListCommand extends BaseCommand {
6
+ constructor() {
7
+ super('list', 'List installed plugins');
8
+ }
9
+ configure() {
10
+ this.command
11
+ .alias('ls')
12
+ .option('-d, --dir <path>', 'Plugins directory (default: ./plugins/)')
13
+ .option('--json', 'Output as JSON')
14
+ .option('-v, --verbose', 'Show detailed information')
15
+ .action((options) => this.execute(options));
16
+ }
17
+ execute(options) {
18
+ try {
19
+ const pluginsDir = options?.dir || path.join(process.cwd(), 'plugins');
20
+ if (!fs.existsSync(pluginsDir)) {
21
+ if (options?.json) {
22
+ this.logger.log(JSON.stringify({ plugins: [], directory: pluginsDir }));
23
+ }
24
+ else {
25
+ this.logger.warn('No plugins directory found.');
26
+ this.logger.info(`Expected: ${pluginsDir}`);
27
+ }
28
+ return;
29
+ }
30
+ const files = fs.readdirSync(pluginsDir).filter((f) => f.endsWith('.opnet'));
31
+ if (files.length === 0) {
32
+ if (options?.json) {
33
+ this.logger.log(JSON.stringify({ plugins: [], directory: pluginsDir }));
34
+ }
35
+ else {
36
+ this.logger.warn('No plugins installed.');
37
+ }
38
+ return;
39
+ }
40
+ const plugins = [];
41
+ for (const file of files) {
42
+ const filePath = path.join(pluginsDir, file);
43
+ try {
44
+ const data = fs.readFileSync(filePath);
45
+ const parsed = parseOpnetBinary(data);
46
+ const isUnsigned = parsed.publicKey.every((b) => b === 0);
47
+ const mldsaLevel = [44, 65, 87][parsed.mldsaLevel];
48
+ plugins.push({
49
+ file,
50
+ name: parsed.metadata.name,
51
+ version: parsed.metadata.version,
52
+ type: parsed.metadata.pluginType,
53
+ size: data.length,
54
+ signed: !isUnsigned,
55
+ mldsaLevel,
56
+ author: parsed.metadata.author.name,
57
+ description: parsed.metadata.description,
58
+ });
59
+ }
60
+ catch {
61
+ plugins.push({
62
+ file,
63
+ name: '(invalid)',
64
+ version: '-',
65
+ type: '-',
66
+ size: fs.statSync(filePath).size,
67
+ signed: false,
68
+ mldsaLevel: 44,
69
+ author: '-',
70
+ });
71
+ }
72
+ }
73
+ plugins.sort((a, b) => a.name.localeCompare(b.name));
74
+ if (options?.json) {
75
+ this.logger.log(JSON.stringify({ plugins, directory: pluginsDir }, null, 2));
76
+ return;
77
+ }
78
+ this.logger.info('\nInstalled Plugins\n');
79
+ this.logger.info(`Directory: ${pluginsDir}`);
80
+ this.logger.log('');
81
+ if (options?.verbose) {
82
+ for (const plugin of plugins) {
83
+ this.logger.info('─'.repeat(60));
84
+ this.logger.info(`${plugin.name} @ ${plugin.version}`);
85
+ this.logger.info(` Type: ${plugin.type}`);
86
+ this.logger.info(` Size: ${formatFileSize(plugin.size)}`);
87
+ this.logger.info(` Signed: ${plugin.signed ? 'Yes' : 'No'}`);
88
+ this.logger.info(` MLDSA: ${plugin.mldsaLevel}`);
89
+ this.logger.info(` Author: ${plugin.author}`);
90
+ if (plugin.description) {
91
+ this.logger.info(` Desc: ${plugin.description}`);
92
+ }
93
+ this.logger.info(` File: ${plugin.file}`);
94
+ }
95
+ }
96
+ else {
97
+ const nameWidth = Math.max(20, ...plugins.map((p) => p.name.length)) + 2;
98
+ const versionWidth = 12;
99
+ const typeWidth = 12;
100
+ const sizeWidth = 10;
101
+ this.logger.info('Name'.padEnd(nameWidth) + 'Version'.padEnd(versionWidth) + 'Type'.padEnd(typeWidth) + 'Size'.padEnd(sizeWidth) + 'Signed');
102
+ this.logger.info('─'.repeat(nameWidth + versionWidth + typeWidth + sizeWidth + 8));
103
+ for (const plugin of plugins) {
104
+ const signedText = plugin.signed ? 'Yes' : 'No';
105
+ this.logger.info(`${plugin.name.padEnd(nameWidth)}${plugin.version.padEnd(versionWidth)}${plugin.type.padEnd(typeWidth)}${formatFileSize(plugin.size).padEnd(sizeWidth)}${signedText}`);
106
+ }
107
+ }
108
+ this.logger.log('');
109
+ this.logger.info(`Total: ${plugins.length} plugin${plugins.length === 1 ? '' : 's'}`);
110
+ this.logger.log('');
111
+ }
112
+ catch (error) {
113
+ this.exitWithError(this.formatError(error));
114
+ }
115
+ }
116
+ }
117
+ export const listCommand = new ListCommand().getCommand();
@@ -0,0 +1,9 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class LoginCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ private buildCredentials;
7
+ private interactiveLogin;
8
+ }
9
+ export declare const loginCommand: import("commander").Command;
@@ -0,0 +1,139 @@
1
+ import { select, confirm, password } from '@inquirer/prompts';
2
+ import { BaseCommand } from './BaseCommand.js';
3
+ import { saveCredentials, isValidMldsaLevel, isValidNetwork } from '../lib/credentials.js';
4
+ import { validateMnemonic, CLIWallet } from '../lib/wallet.js';
5
+ export class LoginCommand extends BaseCommand {
6
+ constructor() {
7
+ super('login', 'Configure wallet credentials for signing and publishing');
8
+ }
9
+ configure() {
10
+ this.command
11
+ .option('-m, --mnemonic <phrase>', 'BIP-39 mnemonic phrase (12 or 24 words)')
12
+ .option('--wif <key>', 'Bitcoin WIF private key (advanced)')
13
+ .option('--mldsa <key>', 'MLDSA private key hex (advanced, requires --wif)')
14
+ .option('-l, --mldsa-level <level>', 'MLDSA security level (44, 65, 87)', '44')
15
+ .option('-n, --network <network>', 'Network (mainnet, testnet, regtest)', 'mainnet')
16
+ .action((options) => this.execute(options));
17
+ }
18
+ async execute(options) {
19
+ try {
20
+ const credentials = await this.buildCredentials(options);
21
+ this.logger.info('Deriving wallet...');
22
+ const wallet = CLIWallet.fromCredentials(credentials);
23
+ this.logger.log('');
24
+ this.logger.info('Wallet Identity');
25
+ this.logger.log('─'.repeat(50));
26
+ this.logger.log(`Network: ${credentials.network}`);
27
+ this.logger.log(`MLDSA Level: MLDSA-${credentials.mldsaLevel}`);
28
+ this.logger.log(`P2TR Address: ${wallet.p2trAddress}`);
29
+ this.logger.log(`MLDSA PubKey Hash: ${wallet.mldsaPublicKeyHash.substring(0, 32)}...`);
30
+ this.logger.log('─'.repeat(50));
31
+ this.logger.log('');
32
+ this.logger.warn('Credentials will be stored at ~/.opnet/credentials.json');
33
+ this.logger.warn('with restricted permissions (owner read/write only).');
34
+ this.logger.log('');
35
+ const confirmed = await confirm({
36
+ message: 'Is this the correct wallet? Save credentials?',
37
+ default: true,
38
+ });
39
+ if (!confirmed) {
40
+ this.logger.warn('Login cancelled.');
41
+ return;
42
+ }
43
+ saveCredentials(credentials);
44
+ this.logger.log('');
45
+ this.logger.success('Credentials saved successfully!');
46
+ }
47
+ catch (error) {
48
+ if (this.isUserCancelled(error)) {
49
+ this.logger.warn('Login cancelled.');
50
+ process.exit(0);
51
+ }
52
+ this.exitWithError(this.formatError(error));
53
+ }
54
+ }
55
+ async buildCredentials(options) {
56
+ if (!isValidNetwork(options.network)) {
57
+ this.exitWithError(`Invalid network: ${options.network}. Valid: mainnet, testnet, regtest`);
58
+ throw new Error('Unreachable');
59
+ }
60
+ const mldsaLevelNum = parseInt(options.mldsaLevel, 10);
61
+ if (!isValidMldsaLevel(mldsaLevelNum)) {
62
+ this.exitWithError(`Invalid MLDSA level: ${options.mldsaLevel}. Valid: 44, 65, 87`);
63
+ }
64
+ const mldsaLevel = mldsaLevelNum;
65
+ if (options.mnemonic) {
66
+ if (!validateMnemonic(options.mnemonic)) {
67
+ this.exitWithError('Invalid mnemonic phrase');
68
+ }
69
+ return { mnemonic: options.mnemonic, mldsaLevel, network: options.network };
70
+ }
71
+ if (options.wif && options.mldsa) {
72
+ return {
73
+ wif: options.wif,
74
+ mldsaPrivateKey: options.mldsa,
75
+ mldsaLevel,
76
+ network: options.network,
77
+ };
78
+ }
79
+ return this.interactiveLogin(options.network, mldsaLevel);
80
+ }
81
+ async interactiveLogin(defaultNetwork, defaultLevel) {
82
+ this.logger.info('OPNet Wallet Configuration\n');
83
+ const loginMethod = await select({
84
+ message: 'How would you like to authenticate?',
85
+ choices: [
86
+ {
87
+ name: 'Mnemonic phrase (recommended)',
88
+ value: 'mnemonic',
89
+ description: '12 or 24-word BIP-39 phrase for full key derivation',
90
+ },
91
+ {
92
+ name: 'WIF + MLDSA keys (advanced)',
93
+ value: 'advanced',
94
+ description: 'Separate Bitcoin WIF and MLDSA private keys',
95
+ },
96
+ ],
97
+ });
98
+ const selectedNetwork = await select({
99
+ message: 'Select network:',
100
+ choices: [
101
+ { name: 'Mainnet', value: 'mainnet' },
102
+ { name: 'Testnet', value: 'testnet' },
103
+ { name: 'Regtest', value: 'regtest' },
104
+ ],
105
+ default: defaultNetwork,
106
+ });
107
+ const selectedLevel = await select({
108
+ message: 'Select MLDSA security level:',
109
+ choices: [
110
+ { name: 'MLDSA-44 (Level 2, fastest)', value: 44, description: '1312 byte public key' },
111
+ { name: 'MLDSA-65 (Level 3, balanced)', value: 65, description: '1952 byte public key' },
112
+ { name: 'MLDSA-87 (Level 5, most secure)', value: 87, description: '2592 byte public key' },
113
+ ],
114
+ default: defaultLevel,
115
+ });
116
+ if (loginMethod === 'mnemonic') {
117
+ const mnemonic = await password({
118
+ message: 'Enter your mnemonic phrase (12 or 24 words):',
119
+ mask: '*',
120
+ validate: (value) => {
121
+ if (!validateMnemonic(value)) {
122
+ return 'Invalid mnemonic phrase. Please enter a valid 12 or 24-word BIP-39 phrase.';
123
+ }
124
+ return true;
125
+ },
126
+ });
127
+ return { mnemonic, mldsaLevel: selectedLevel, network: selectedNetwork };
128
+ }
129
+ const wif = await password({ message: 'Enter Bitcoin WIF private key:', mask: '*' });
130
+ const mldsaKey = await password({ message: 'Enter MLDSA private key (hex):', mask: '*' });
131
+ return {
132
+ wif,
133
+ mldsaPrivateKey: mldsaKey,
134
+ mldsaLevel: selectedLevel,
135
+ network: selectedNetwork,
136
+ };
137
+ }
138
+ }
139
+ export const loginCommand = new LoginCommand().getCommand();
@@ -0,0 +1,7 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class LogoutCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ }
7
+ export declare const logoutCommand: import("commander").Command;
@@ -0,0 +1,57 @@
1
+ import { confirm } from '@inquirer/prompts';
2
+ import { BaseCommand } from './BaseCommand.js';
3
+ import { deleteCredentials, hasCredentials, getCredentialSource } from '../lib/credentials.js';
4
+ export class LogoutCommand extends BaseCommand {
5
+ constructor() {
6
+ super('logout', 'Remove stored wallet credentials');
7
+ }
8
+ configure() {
9
+ this.command
10
+ .option('-y, --yes', 'Skip confirmation prompt')
11
+ .action((options) => this.execute(options));
12
+ }
13
+ async execute(options) {
14
+ try {
15
+ if (!hasCredentials()) {
16
+ this.logger.warn('No credentials found.');
17
+ return;
18
+ }
19
+ const source = getCredentialSource();
20
+ if (source.startsWith('environment')) {
21
+ this.logger.warn(`Credentials are set via ${source}.`);
22
+ this.logger.info('To remove them, unset the environment variables:');
23
+ this.logger.info(' unset OPNET_MNEMONIC');
24
+ this.logger.info(' unset OPNET_PRIVATE_KEY');
25
+ this.logger.info(' unset OPNET_MLDSA_KEY');
26
+ return;
27
+ }
28
+ if (!options.yes) {
29
+ this.logger.warn('This will remove your stored credentials from:');
30
+ this.logger.info(` ${source}`);
31
+ const confirmed = await confirm({
32
+ message: 'Are you sure you want to logout?',
33
+ default: false,
34
+ });
35
+ if (!confirmed) {
36
+ this.logger.warn('Logout cancelled.');
37
+ return;
38
+ }
39
+ }
40
+ const deleted = deleteCredentials();
41
+ if (deleted) {
42
+ this.logger.success('Credentials removed successfully.');
43
+ }
44
+ else {
45
+ this.logger.warn('No credentials file found to remove.');
46
+ }
47
+ }
48
+ catch (error) {
49
+ if (this.isUserCancelled(error)) {
50
+ this.logger.warn('Logout cancelled.');
51
+ process.exit(0);
52
+ }
53
+ this.exitWithError(this.formatError(error));
54
+ }
55
+ }
56
+ }
57
+ export const logoutCommand = new LogoutCommand().getCommand();
@@ -0,0 +1,7 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class PublishCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ }
7
+ export declare const publishCommand: import("commander").Command;
@@ -0,0 +1,163 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import * as crypto from 'crypto';
4
+ import { confirm } from '@inquirer/prompts';
5
+ import { BaseCommand } from './BaseCommand.js';
6
+ import { parseOpnetBinary, formatFileSize, verifyChecksum } from '../lib/binary.js';
7
+ import { CLIWallet } from '../lib/wallet.js';
8
+ import { loadCredentials, canSign } from '../lib/credentials.js';
9
+ import { uploadPlugin } from '../lib/ipfs.js';
10
+ import { getPackage, getScope, parsePackageName, computePermissionsHash, encodeDependencies, pluginTypeToRegistry, mldsaLevelToRegistry, } from '../lib/registry.js';
11
+ export class PublishCommand extends BaseCommand {
12
+ constructor() {
13
+ super('publish', 'Publish a plugin to the OPNet registry');
14
+ }
15
+ configure() {
16
+ this.command
17
+ .argument('[file]', 'Path to .opnet file (default: ./build/<name>.opnet)')
18
+ .option('-n, --network <network>', 'Network to publish to', 'mainnet')
19
+ .option('--dry-run', 'Show what would be published without publishing')
20
+ .option('-y, --yes', 'Skip confirmation prompts')
21
+ .action((file, options) => this.execute(file, options || { network: 'mainnet' }));
22
+ }
23
+ async execute(file, options) {
24
+ try {
25
+ let binaryPath = file;
26
+ if (!binaryPath) {
27
+ const manifestPath = path.join(process.cwd(), 'plugin.json');
28
+ if (fs.existsSync(manifestPath)) {
29
+ const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf-8'));
30
+ const name = manifest.name.replace(/^@/, '').replace(/\//g, '-');
31
+ binaryPath = path.join(process.cwd(), 'build', `${name}.opnet`);
32
+ }
33
+ }
34
+ if (!binaryPath || !fs.existsSync(binaryPath)) {
35
+ this.logger.fail('No .opnet file found.');
36
+ this.logger.info('Run `opnet compile` first or specify the file path.');
37
+ process.exit(1);
38
+ }
39
+ this.logger.info('Parsing plugin binary...');
40
+ const data = fs.readFileSync(binaryPath);
41
+ const parsed = parseOpnetBinary(data);
42
+ if (!verifyChecksum(parsed)) {
43
+ this.logger.fail('Checksum verification failed');
44
+ this.logger.error('The binary appears to be corrupted.');
45
+ process.exit(1);
46
+ }
47
+ const isUnsigned = parsed.publicKey.every((b) => b === 0);
48
+ if (isUnsigned) {
49
+ this.logger.fail('Binary is unsigned');
50
+ this.logger.error('Cannot publish unsigned binaries.');
51
+ this.logger.info('Run `opnet sign` to sign the binary.');
52
+ process.exit(1);
53
+ }
54
+ const meta = parsed.metadata;
55
+ const mldsaLevel = [44, 65, 87][parsed.mldsaLevel];
56
+ this.logger.success(`Parsed: ${meta.name}@${meta.version}`);
57
+ this.logger.info('Loading wallet...');
58
+ const credentials = loadCredentials();
59
+ if (!credentials || !canSign(credentials)) {
60
+ this.logger.fail('No credentials configured');
61
+ this.logger.warn('Run `opnet login` to configure your wallet.');
62
+ process.exit(1);
63
+ }
64
+ const wallet = CLIWallet.fromCredentials(credentials);
65
+ const walletPkHash = wallet.mldsaPublicKeyHash;
66
+ const binaryPkHash = crypto.createHash('sha256').update(parsed.publicKey).digest('hex');
67
+ if (walletPkHash !== binaryPkHash) {
68
+ this.logger.fail('Wallet mismatch');
69
+ this.logger.error('The binary was signed with a different key.');
70
+ this.logger.log(`Binary signer: ${binaryPkHash.substring(0, 32)}...`);
71
+ this.logger.log(`Your key: ${walletPkHash.substring(0, 32)}...`);
72
+ process.exit(1);
73
+ }
74
+ this.logger.success('Wallet verified');
75
+ this.logger.info('Checking registry status...');
76
+ const { scope, name } = parsePackageName(meta.name);
77
+ const network = (options?.network || 'mainnet');
78
+ if (scope) {
79
+ const scopeInfo = await getScope(scope, network);
80
+ if (!scopeInfo) {
81
+ this.logger.fail(`Scope @${scope} is not registered`);
82
+ this.logger.warn(`Register the scope first with: opnet scope register ${scope}`);
83
+ process.exit(1);
84
+ }
85
+ }
86
+ const packageInfo = await getPackage(meta.name, network);
87
+ const isNewPackage = !packageInfo;
88
+ this.logger.success(isNewPackage
89
+ ? 'New package registration'
90
+ : `Existing package (${packageInfo.versionCount} versions)`);
91
+ this.logger.log('');
92
+ this.logger.info('Publishing Summary');
93
+ this.logger.log('─'.repeat(50));
94
+ this.logger.log(`Package: ${meta.name}`);
95
+ this.logger.log(`Version: ${meta.version}`);
96
+ this.logger.log(`Type: ${meta.pluginType}`);
97
+ this.logger.log(`OPNet: ${meta.opnetVersion}`);
98
+ this.logger.log(`Size: ${formatFileSize(data.length)}`);
99
+ this.logger.log(`MLDSA Level: ${mldsaLevel}`);
100
+ this.logger.log(`Network: ${options?.network}`);
101
+ this.logger.log(`Status: ${isNewPackage ? 'New package' : 'New version'}`);
102
+ this.logger.log('');
103
+ if (options?.dryRun) {
104
+ this.logger.warn('Dry run - no changes made.');
105
+ return;
106
+ }
107
+ if (!options?.yes) {
108
+ const confirmed = await confirm({
109
+ message: 'Publish this plugin?',
110
+ default: true,
111
+ });
112
+ if (!confirmed) {
113
+ this.logger.warn('Publishing cancelled.');
114
+ return;
115
+ }
116
+ }
117
+ this.logger.info('Uploading to IPFS...');
118
+ const pinResult = await uploadPlugin(binaryPath);
119
+ this.logger.success(`Uploaded to IPFS: ${pinResult.cid}`);
120
+ const permissionsHash = computePermissionsHash(meta.permissions);
121
+ const dependencies = encodeDependencies(meta.dependencies || {});
122
+ if (isNewPackage) {
123
+ this.logger.info('Registering package...');
124
+ this.logger.warn('Package registration required.');
125
+ this.logger.log(`Transaction would call: registerPackage("${meta.name}")`);
126
+ this.logger.info('Package registration (transaction pending)');
127
+ }
128
+ this.logger.info('Publishing version...');
129
+ this.logger.warn('Version publishing required.');
130
+ this.logger.log('Transaction would call: publishVersion(');
131
+ this.logger.log(` packageName: "${meta.name}",`);
132
+ this.logger.log(` version: "${meta.version}",`);
133
+ this.logger.log(` ipfsCid: "${pinResult.cid}",`);
134
+ this.logger.log(` checksum: <32 bytes>,`);
135
+ this.logger.log(` signature: <${parsed.signature.length} bytes>,`);
136
+ this.logger.log(` mldsaLevel: ${mldsaLevelToRegistry(mldsaLevel)},`);
137
+ this.logger.log(` opnetVersionRange: "${meta.opnetVersion}",`);
138
+ this.logger.log(` pluginType: ${pluginTypeToRegistry(meta.pluginType)},`);
139
+ this.logger.log(` permissionsHash: <32 bytes>,`);
140
+ this.logger.log(` dependencies: <${dependencies.length} bytes>`);
141
+ this.logger.log(')');
142
+ this.logger.info('Version publishing (transaction pending)');
143
+ this.logger.log('');
144
+ this.logger.success('Plugin uploaded successfully!');
145
+ this.logger.log('');
146
+ this.logger.log(`IPFS CID: ${pinResult.cid}`);
147
+ this.logger.log(`Gateway: https://ipfs.opnet.org/ipfs/${pinResult.cid}`);
148
+ this.logger.log('');
149
+ this.logger.warn('Note: Registry transaction support is coming soon.');
150
+ this.logger.warn('The binary has been uploaded to IPFS and is ready for registry submission.');
151
+ this.logger.log('');
152
+ }
153
+ catch (error) {
154
+ this.logger.fail('Publishing failed');
155
+ if (this.isUserCancelled(error)) {
156
+ this.logger.warn('Publishing cancelled.');
157
+ process.exit(0);
158
+ }
159
+ this.exitWithError(this.formatError(error));
160
+ }
161
+ }
162
+ }
163
+ export const publishCommand = new PublishCommand().getCommand();
@@ -0,0 +1,7 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class SearchCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ }
7
+ export declare const searchCommand: import("commander").Command;
@@ -0,0 +1,97 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ import { getPackage, getVersion, registryToMldsaLevel, registryToPluginType } from '../lib/registry.js';
3
+ export class SearchCommand extends BaseCommand {
4
+ constructor() {
5
+ super('search', 'Search for plugins in the registry');
6
+ }
7
+ configure() {
8
+ this.command
9
+ .argument('<query>', 'Package name or search query')
10
+ .option('-n, --network <network>', 'Network', 'mainnet')
11
+ .option('--json', 'Output as JSON')
12
+ .action((query, options) => this.execute(query, options || { network: 'mainnet' }));
13
+ }
14
+ async execute(query, options) {
15
+ try {
16
+ const network = (options?.network || 'mainnet');
17
+ this.logger.info(`Searching for "${query}"...`);
18
+ const packageInfo = await getPackage(query, network);
19
+ if (!packageInfo) {
20
+ this.logger.fail('No results');
21
+ if (options?.json) {
22
+ this.logger.log(JSON.stringify({ results: [], query }));
23
+ }
24
+ else {
25
+ this.logger.log('');
26
+ this.logger.warn(`No package found matching "${query}".`);
27
+ this.logger.log('');
28
+ this.logger.info('Tips:');
29
+ this.logger.info(' - For scoped packages, use @scope/name');
30
+ this.logger.info(' - Package names are case-sensitive');
31
+ this.logger.info(' - Try searching without the version');
32
+ }
33
+ return;
34
+ }
35
+ let latestVersionInfo = null;
36
+ if (packageInfo.latestVersion) {
37
+ latestVersionInfo = await getVersion(query, packageInfo.latestVersion, network);
38
+ }
39
+ this.logger.success('Package found');
40
+ if (options?.json) {
41
+ const output = {
42
+ results: [
43
+ {
44
+ name: query,
45
+ latestVersion: packageInfo.latestVersion,
46
+ versionCount: Number(packageInfo.versionCount),
47
+ createdAt: Number(packageInfo.createdAt),
48
+ owner: packageInfo.owner.toString(),
49
+ details: latestVersionInfo
50
+ ? {
51
+ ipfsCid: latestVersionInfo.ipfsCid,
52
+ mldsaLevel: registryToMldsaLevel(latestVersionInfo.mldsaLevel),
53
+ pluginType: registryToPluginType(latestVersionInfo.pluginType),
54
+ opnetVersion: latestVersionInfo.opnetVersionRange,
55
+ deprecated: latestVersionInfo.deprecated,
56
+ publishedAt: Number(latestVersionInfo.publishedAt),
57
+ }
58
+ : null,
59
+ },
60
+ ],
61
+ query,
62
+ };
63
+ this.logger.log(JSON.stringify(output, null, 2));
64
+ return;
65
+ }
66
+ this.logger.log('');
67
+ this.logger.info('Package Information');
68
+ this.logger.info('─'.repeat(60));
69
+ this.logger.log('');
70
+ this.logger.info(`Name: ${query}`);
71
+ this.logger.info(`Latest: ${packageInfo.latestVersion || 'N/A'}`);
72
+ this.logger.info(`Versions: ${packageInfo.versionCount}`);
73
+ this.logger.info(`Owner: ${packageInfo.owner}`);
74
+ if (latestVersionInfo) {
75
+ this.logger.log('');
76
+ this.logger.info('Latest Version Details:');
77
+ this.logger.info(` Type: ${registryToPluginType(latestVersionInfo.pluginType)}`);
78
+ this.logger.info(` MLDSA Level: ${registryToMldsaLevel(latestVersionInfo.mldsaLevel)}`);
79
+ this.logger.info(` OPNet Range: ${latestVersionInfo.opnetVersionRange}`);
80
+ this.logger.info(` IPFS CID: ${latestVersionInfo.ipfsCid}`);
81
+ this.logger.info(` Deprecated: ${latestVersionInfo.deprecated ? 'Yes' : 'No'}`);
82
+ const publishDate = new Date(Number(latestVersionInfo.publishedAt) * 1000);
83
+ this.logger.info(` Published: ${publishDate.toISOString().split('T')[0]}`);
84
+ }
85
+ this.logger.log('');
86
+ this.logger.info('Install with:');
87
+ this.logger.info(` opnet install ${query}`);
88
+ this.logger.info(` opnet install ${query}@${packageInfo.latestVersion}`);
89
+ this.logger.log('');
90
+ }
91
+ catch (error) {
92
+ this.logger.fail('Search failed');
93
+ this.exitWithError(this.formatError(error));
94
+ }
95
+ }
96
+ }
97
+ export const searchCommand = new SearchCommand().getCommand();
@@ -0,0 +1,7 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class SignCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ }
7
+ export declare const signCommand: import("commander").Command;