@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,80 @@
1
+ import * as fs from 'fs';
2
+ import * as crypto from 'crypto';
3
+ import { BaseCommand } from './BaseCommand.js';
4
+ import { parseOpnetBinary, buildOpnetBinary, computeChecksum, formatFileSize } from '../lib/binary.js';
5
+ import { CLIWallet } from '../lib/wallet.js';
6
+ import { loadCredentials, canSign } from '../lib/credentials.js';
7
+ export class SignCommand extends BaseCommand {
8
+ constructor() {
9
+ super('sign', 'Sign or re-sign a .opnet binary with your MLDSA key');
10
+ }
11
+ configure() {
12
+ this.command
13
+ .argument('<file>', 'Path to .opnet file')
14
+ .option('-o, --output <path>', 'Output file path (default: overwrites input)')
15
+ .option('--force', 'Force re-signing even if already signed by different key')
16
+ .action((file, options) => this.execute(file, options));
17
+ }
18
+ execute(file, options) {
19
+ try {
20
+ if (!fs.existsSync(file)) {
21
+ this.exitWithError(`File not found: ${file}`);
22
+ }
23
+ this.logger.info('Loading wallet...');
24
+ const credentials = loadCredentials();
25
+ if (!credentials || !canSign(credentials)) {
26
+ this.logger.fail('No credentials configured');
27
+ this.logger.warn('To sign plugins, run: opnet login');
28
+ process.exit(1);
29
+ }
30
+ const wallet = CLIWallet.fromCredentials(credentials);
31
+ this.logger.success(`Wallet loaded (MLDSA-${wallet.securityLevel})`);
32
+ this.logger.info('Parsing binary...');
33
+ const data = fs.readFileSync(file);
34
+ const parsed = parseOpnetBinary(data);
35
+ this.logger.success(`Parsed: ${parsed.metadata.name}@${parsed.metadata.version}`);
36
+ const isUnsigned = parsed.publicKey.every((b) => b === 0);
37
+ const currentPkHash = crypto.createHash('sha256').update(parsed.publicKey).digest('hex');
38
+ const newPkHash = wallet.mldsaPublicKeyHash;
39
+ if (!isUnsigned && currentPkHash !== newPkHash && !options.force) {
40
+ this.logger.log('');
41
+ this.logger.warn('Warning: This binary is already signed by a different key.');
42
+ this.logger.log(` Current signer: ${currentPkHash.substring(0, 32)}...`);
43
+ this.logger.log(` Your key: ${newPkHash.substring(0, 32)}...`);
44
+ this.logger.log('');
45
+ this.logger.log('Use --force to re-sign with your key.');
46
+ process.exit(1);
47
+ }
48
+ this.logger.info('Signing...');
49
+ const metadataBytes = Buffer.from(parsed.rawMetadata, 'utf-8');
50
+ const checksum = computeChecksum(metadataBytes, parsed.bytecode, parsed.proto ?? Buffer.alloc(0));
51
+ const signature = wallet.signMLDSA(checksum);
52
+ this.logger.success(`Signed (${formatFileSize(signature.length)} signature)`);
53
+ this.logger.info('Rebuilding binary...');
54
+ const newBinary = buildOpnetBinary({
55
+ mldsaLevel: wallet.securityLevel,
56
+ publicKey: wallet.mldsaPublicKey,
57
+ signature,
58
+ metadata: parsed.metadata,
59
+ bytecode: parsed.bytecode,
60
+ proto: parsed.proto ?? Buffer.alloc(0),
61
+ });
62
+ this.logger.success(`Binary rebuilt (${formatFileSize(newBinary.length)})`);
63
+ const outputPath = options.output || file;
64
+ fs.writeFileSync(outputPath, newBinary);
65
+ this.logger.log('');
66
+ this.logger.success('Plugin signed successfully!');
67
+ this.logger.log('');
68
+ this.logger.log(`Output: ${outputPath}`);
69
+ this.logger.log(`Plugin: ${parsed.metadata.name}@${parsed.metadata.version}`);
70
+ this.logger.log(`MLDSA Level: ${wallet.securityLevel}`);
71
+ this.logger.log(`Publisher: ${newPkHash.substring(0, 32)}...`);
72
+ this.logger.log('');
73
+ }
74
+ catch (error) {
75
+ this.logger.fail('Signing failed');
76
+ this.exitWithError(this.formatError(error));
77
+ }
78
+ }
79
+ }
80
+ export const signCommand = new SignCommand().getCommand();
@@ -0,0 +1,8 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class TransferCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ private handleCancel;
7
+ }
8
+ export declare const transferCommand: import("commander").Command;
@@ -0,0 +1,179 @@
1
+ import { input, confirm } from '@inquirer/prompts';
2
+ import { BaseCommand } from './BaseCommand.js';
3
+ import { getPackage, getScope, getPendingTransfer, getPendingScopeTransfer, } from '../lib/registry.js';
4
+ import { loadCredentials, canSign } from '../lib/credentials.js';
5
+ import { CLIWallet } from '../lib/wallet.js';
6
+ export class TransferCommand extends BaseCommand {
7
+ constructor() {
8
+ super('transfer', 'Initiate ownership transfer of a package or scope');
9
+ }
10
+ configure() {
11
+ this.command
12
+ .argument('<name>', 'Package name or @scope')
13
+ .argument('[newOwner]', 'New owner address')
14
+ .option('-n, --network <network>', 'Network', 'mainnet')
15
+ .option('-y, --yes', 'Skip confirmation')
16
+ .option('--cancel', 'Cancel pending transfer')
17
+ .action((name, newOwner, options) => this.execute(name, newOwner, options || { network: 'mainnet' }));
18
+ }
19
+ async execute(name, newOwner, options) {
20
+ try {
21
+ this.logger.info('Loading wallet...');
22
+ const credentials = loadCredentials();
23
+ if (!credentials || !canSign(credentials)) {
24
+ this.logger.fail('No credentials configured');
25
+ this.logger.warn('Run `opnet login` to configure your wallet.');
26
+ process.exit(1);
27
+ }
28
+ CLIWallet.fromCredentials(credentials);
29
+ this.logger.success('Wallet loaded');
30
+ const network = (options?.network || 'mainnet');
31
+ const isScope = name.startsWith('@') && !name.includes('/');
32
+ const displayName = isScope ? name : `package ${name}`;
33
+ if (options?.cancel) {
34
+ await this.handleCancel(name, isScope, network, options);
35
+ return;
36
+ }
37
+ let targetOwner = newOwner;
38
+ if (!targetOwner) {
39
+ targetOwner = await input({
40
+ message: 'New owner address:',
41
+ validate: (value) => {
42
+ if (!value || value.length < 20) {
43
+ return 'Please enter a valid address';
44
+ }
45
+ return true;
46
+ },
47
+ });
48
+ }
49
+ this.logger.info(`Checking ${displayName}...`);
50
+ if (isScope) {
51
+ const scopeName = name.substring(1);
52
+ const scopeInfo = await getScope(scopeName, network);
53
+ if (!scopeInfo) {
54
+ this.logger.fail('Scope not found');
55
+ this.logger.error(`Scope "${name}" does not exist.`);
56
+ process.exit(1);
57
+ }
58
+ this.logger.success(`Found scope ${name}`);
59
+ }
60
+ else {
61
+ const packageInfo = await getPackage(name, network);
62
+ if (!packageInfo) {
63
+ this.logger.fail('Package not found');
64
+ this.logger.error(`Package "${name}" does not exist.`);
65
+ process.exit(1);
66
+ }
67
+ this.logger.success(`Found package ${name}`);
68
+ }
69
+ this.logger.log('');
70
+ this.logger.info('Transfer Summary');
71
+ this.logger.log('─'.repeat(50));
72
+ this.logger.log(`Type: ${isScope ? 'Scope' : 'Package'}`);
73
+ this.logger.log(`Name: ${name}`);
74
+ this.logger.log(`New Owner: ${targetOwner}`);
75
+ this.logger.log(`Network: ${options?.network}`);
76
+ this.logger.log('');
77
+ this.logger.warn('Note: The new owner must call `opnet accept` to complete the transfer.');
78
+ this.logger.log('');
79
+ if (!options?.yes) {
80
+ const confirmed = await confirm({
81
+ message: `Initiate transfer of ${displayName}?`,
82
+ default: false,
83
+ });
84
+ if (!confirmed) {
85
+ this.logger.warn('Transfer cancelled.');
86
+ return;
87
+ }
88
+ }
89
+ this.logger.info('Initiating transfer...');
90
+ if (isScope) {
91
+ const scopeName = name.substring(1);
92
+ this.logger.warn('Transfer transaction required.');
93
+ this.logger.log('Transaction would call: initiateScopeTransfer(');
94
+ this.logger.log(` scopeName: "${scopeName}",`);
95
+ this.logger.log(` newOwner: "${targetOwner}"`);
96
+ this.logger.log(')');
97
+ }
98
+ else {
99
+ this.logger.warn('Transfer transaction required.');
100
+ this.logger.log('Transaction would call: initiateTransfer(');
101
+ this.logger.log(` packageName: "${name}",`);
102
+ this.logger.log(` newOwner: "${targetOwner}"`);
103
+ this.logger.log(')');
104
+ }
105
+ this.logger.info('Transfer (transaction pending)');
106
+ this.logger.log('');
107
+ this.logger.success('Transfer initiated!');
108
+ this.logger.warn('Note: Registry transaction support is coming soon.');
109
+ this.logger.log('');
110
+ }
111
+ catch (error) {
112
+ this.logger.fail('Transfer failed');
113
+ if (this.isUserCancelled(error)) {
114
+ this.logger.warn('Transfer cancelled.');
115
+ process.exit(0);
116
+ }
117
+ this.exitWithError(this.formatError(error));
118
+ }
119
+ }
120
+ async handleCancel(name, isScope, network, options) {
121
+ this.logger.info('Checking pending transfer...');
122
+ if (isScope) {
123
+ const scopeName = name.substring(1);
124
+ const pending = await getPendingScopeTransfer(scopeName, network);
125
+ if (!pending) {
126
+ this.logger.warn('No pending transfer');
127
+ this.logger.log(`No pending transfer for ${name}.`);
128
+ return;
129
+ }
130
+ this.logger.success(`Found pending transfer to ${pending.pendingOwner}`);
131
+ if (!options?.yes) {
132
+ const confirmed = await confirm({
133
+ message: `Cancel ownership transfer of ${name}?`,
134
+ default: true,
135
+ });
136
+ if (!confirmed) {
137
+ this.logger.warn('Cancelled.');
138
+ return;
139
+ }
140
+ }
141
+ this.logger.info('Cancelling transfer...');
142
+ this.logger.warn('Cancellation transaction required.');
143
+ this.logger.log('Transaction would call: cancelScopeTransfer(');
144
+ this.logger.log(` scopeName: "${scopeName}"`);
145
+ this.logger.log(')');
146
+ this.logger.info('Cancellation (transaction pending)');
147
+ }
148
+ else {
149
+ const pending = await getPendingTransfer(name, network);
150
+ if (!pending) {
151
+ this.logger.warn('No pending transfer');
152
+ this.logger.log(`No pending transfer for ${name}.`);
153
+ return;
154
+ }
155
+ this.logger.success(`Found pending transfer to ${pending.pendingOwner}`);
156
+ if (!options?.yes) {
157
+ const confirmed = await confirm({
158
+ message: `Cancel ownership transfer of ${name}?`,
159
+ default: true,
160
+ });
161
+ if (!confirmed) {
162
+ this.logger.warn('Cancelled.');
163
+ return;
164
+ }
165
+ }
166
+ this.logger.info('Cancelling transfer...');
167
+ this.logger.warn('Cancellation transaction required.');
168
+ this.logger.log('Transaction would call: cancelTransfer(');
169
+ this.logger.log(` packageName: "${name}"`);
170
+ this.logger.log(')');
171
+ this.logger.info('Cancellation (transaction pending)');
172
+ }
173
+ this.logger.log('');
174
+ this.logger.success('Transfer cancellation submitted!');
175
+ this.logger.warn('Note: Registry transaction support is coming soon.');
176
+ this.logger.log('');
177
+ }
178
+ }
179
+ export const transferCommand = new TransferCommand().getCommand();
@@ -0,0 +1,7 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class UndeprecateCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ }
7
+ export declare const undeprecateCommand: import("commander").Command;
@@ -0,0 +1,95 @@
1
+ import { confirm } from '@inquirer/prompts';
2
+ import { BaseCommand } from './BaseCommand.js';
3
+ import { getPackage, getVersion, isVersionImmutable } from '../lib/registry.js';
4
+ import { loadCredentials, canSign } from '../lib/credentials.js';
5
+ import { CLIWallet } from '../lib/wallet.js';
6
+ export class UndeprecateCommand extends BaseCommand {
7
+ constructor() {
8
+ super('undeprecate', 'Remove deprecation from a package version');
9
+ }
10
+ configure() {
11
+ this.command
12
+ .argument('<package>', 'Package name (e.g., @scope/name or name)')
13
+ .argument('<version>', 'Version to undeprecate')
14
+ .option('-n, --network <network>', 'Network', 'mainnet')
15
+ .option('-y, --yes', 'Skip confirmation')
16
+ .action((packageName, version, options) => this.execute(packageName, version, options || { network: 'mainnet' }));
17
+ }
18
+ async execute(packageName, version, options) {
19
+ try {
20
+ this.logger.info('Loading wallet...');
21
+ const credentials = loadCredentials();
22
+ if (!credentials || !canSign(credentials)) {
23
+ this.logger.fail('No credentials configured');
24
+ this.logger.warn('Run `opnet login` to configure your wallet.');
25
+ process.exit(1);
26
+ }
27
+ CLIWallet.fromCredentials(credentials);
28
+ this.logger.success('Wallet loaded');
29
+ this.logger.info('Fetching package info...');
30
+ const network = (options?.network || 'mainnet');
31
+ const packageInfo = await getPackage(packageName, network);
32
+ if (!packageInfo) {
33
+ this.logger.fail('Package not found');
34
+ this.logger.error(`Package "${packageName}" does not exist.`);
35
+ process.exit(1);
36
+ }
37
+ const versionInfo = await getVersion(packageName, version, network);
38
+ if (!versionInfo) {
39
+ this.logger.fail('Version not found');
40
+ this.logger.error(`Version "${version}" does not exist.`);
41
+ process.exit(1);
42
+ }
43
+ if (!versionInfo.deprecated) {
44
+ this.logger.warn('Not deprecated');
45
+ this.logger.log(`Version ${version} is not deprecated.`);
46
+ return;
47
+ }
48
+ const immutable = await isVersionImmutable(packageName, version, network);
49
+ if (immutable) {
50
+ this.logger.fail('Version is immutable');
51
+ this.logger.error('This version is past the 72-hour mutability window.');
52
+ this.logger.error('Immutable versions cannot be undeprecated.');
53
+ process.exit(1);
54
+ }
55
+ this.logger.success(`Found: ${packageName}@${version} (deprecated)`);
56
+ this.logger.log('');
57
+ this.logger.info('Undeprecation Summary');
58
+ this.logger.log('─'.repeat(50));
59
+ this.logger.log(`Package: ${packageName}`);
60
+ this.logger.log(`Version: ${version}`);
61
+ this.logger.log(`Network: ${options?.network}`);
62
+ this.logger.log('');
63
+ if (!options?.yes) {
64
+ const confirmed = await confirm({
65
+ message: `Remove deprecation from ${packageName}@${version}?`,
66
+ default: true,
67
+ });
68
+ if (!confirmed) {
69
+ this.logger.warn('Undeprecation cancelled.');
70
+ return;
71
+ }
72
+ }
73
+ this.logger.info('Removing deprecation...');
74
+ this.logger.warn('Undeprecation transaction required.');
75
+ this.logger.log('Transaction would call: undeprecateVersion(');
76
+ this.logger.log(` packageName: "${packageName}",`);
77
+ this.logger.log(` version: "${version}"`);
78
+ this.logger.log(')');
79
+ this.logger.info('Undeprecation (transaction pending)');
80
+ this.logger.log('');
81
+ this.logger.success('Undeprecation submitted!');
82
+ this.logger.warn('Note: Registry transaction support is coming soon.');
83
+ this.logger.log('');
84
+ }
85
+ catch (error) {
86
+ this.logger.fail('Undeprecation failed');
87
+ if (this.isUserCancelled(error)) {
88
+ this.logger.warn('Undeprecation cancelled.');
89
+ process.exit(0);
90
+ }
91
+ this.exitWithError(this.formatError(error));
92
+ }
93
+ }
94
+ }
95
+ export const undeprecateCommand = new UndeprecateCommand().getCommand();
@@ -0,0 +1,7 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class UpdateCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ }
7
+ export declare const updateCommand: import("commander").Command;
@@ -0,0 +1,130 @@
1
+ import * as fs from 'fs';
2
+ import * as path from 'path';
3
+ import { BaseCommand } from './BaseCommand.js';
4
+ import { parseOpnetBinary, verifyChecksum } from '../lib/binary.js';
5
+ import { getPackage, getVersion } from '../lib/registry.js';
6
+ import { fetchFromIPFS } from '../lib/ipfs.js';
7
+ import { CLIWallet } from '../lib/wallet.js';
8
+ export class UpdateCommand extends BaseCommand {
9
+ constructor() {
10
+ super('update', 'Update installed plugins to latest versions');
11
+ }
12
+ configure() {
13
+ this.command
14
+ .argument('[package]', 'Specific package to update (default: all)')
15
+ .option('-d, --dir <path>', 'Plugins directory (default: ./plugins/)')
16
+ .option('-n, --network <network>', 'Network', 'mainnet')
17
+ .option('--skip-verify', 'Skip signature verification')
18
+ .action((packageName, options) => this.execute(packageName, options || { network: 'mainnet' }));
19
+ }
20
+ async execute(packageName, options) {
21
+ try {
22
+ const pluginsDir = options?.dir || path.join(process.cwd(), 'plugins');
23
+ if (!fs.existsSync(pluginsDir)) {
24
+ this.logger.warn('No plugins directory found.');
25
+ this.logger.log(`Expected: ${pluginsDir}`);
26
+ return;
27
+ }
28
+ const files = fs.readdirSync(pluginsDir).filter((f) => f.endsWith('.opnet'));
29
+ if (files.length === 0) {
30
+ this.logger.warn('No plugins installed.');
31
+ return;
32
+ }
33
+ const network = (options?.network || 'mainnet');
34
+ const updates = [];
35
+ this.logger.info('\nChecking for updates...\n');
36
+ for (const file of files) {
37
+ const filePath = path.join(pluginsDir, file);
38
+ try {
39
+ const data = fs.readFileSync(filePath);
40
+ const parsed = parseOpnetBinary(data);
41
+ const name = parsed.metadata.name;
42
+ if (packageName && name !== packageName) {
43
+ continue;
44
+ }
45
+ this.logger.info(`Checking ${name}...`);
46
+ const packageInfo = await getPackage(name, network);
47
+ if (!packageInfo) {
48
+ this.logger.info(`${name}: not found in registry`);
49
+ continue;
50
+ }
51
+ const currentVersion = parsed.metadata.version;
52
+ const latestVersion = packageInfo.latestVersion;
53
+ if (currentVersion === latestVersion) {
54
+ this.logger.success(`${name}@${currentVersion}: up to date`);
55
+ continue;
56
+ }
57
+ const versionInfo = await getVersion(name, latestVersion, network);
58
+ if (!versionInfo) {
59
+ this.logger.warn(`${name}: latest version info unavailable`);
60
+ continue;
61
+ }
62
+ this.logger.info(`${name}: ${currentVersion} -> ${latestVersion}`);
63
+ updates.push({
64
+ file,
65
+ name,
66
+ currentVersion,
67
+ latestVersion,
68
+ cid: versionInfo.ipfsCid,
69
+ });
70
+ }
71
+ catch {
72
+ this.logger.warn(`${file}: failed to parse`);
73
+ }
74
+ }
75
+ if (updates.length === 0) {
76
+ this.logger.log('');
77
+ this.logger.success('All plugins are up to date!');
78
+ return;
79
+ }
80
+ this.logger.log('');
81
+ this.logger.info('Available Updates:');
82
+ this.logger.log('─'.repeat(60));
83
+ for (const update of updates) {
84
+ this.logger.log(` ${update.name}: ${update.currentVersion} -> ${update.latestVersion}`);
85
+ }
86
+ this.logger.log('');
87
+ for (const update of updates) {
88
+ this.logger.info(`Updating ${update.name}...`);
89
+ try {
90
+ const result = await fetchFromIPFS(update.cid);
91
+ const parsed = parseOpnetBinary(result.data);
92
+ if (!verifyChecksum(parsed)) {
93
+ this.logger.fail(`${update.name}: checksum failed`);
94
+ continue;
95
+ }
96
+ if (!options?.skipVerify) {
97
+ const isUnsigned = parsed.publicKey.every((b) => b === 0);
98
+ if (!isUnsigned) {
99
+ const mldsaLevel = [44, 65, 87][parsed.mldsaLevel];
100
+ const signatureValid = CLIWallet.verifyMLDSA(parsed.checksum, parsed.signature, parsed.publicKey, mldsaLevel);
101
+ if (!signatureValid) {
102
+ this.logger.fail(`${update.name}: signature invalid`);
103
+ continue;
104
+ }
105
+ }
106
+ }
107
+ const newFileName = `${update.name.replace(/^@/, '').replace(/\//g, '-')}-${update.latestVersion}.opnet`;
108
+ const newFilePath = path.join(pluginsDir, newFileName);
109
+ fs.writeFileSync(newFilePath, result.data);
110
+ const oldFilePath = path.join(pluginsDir, update.file);
111
+ if (oldFilePath !== newFilePath && fs.existsSync(oldFilePath)) {
112
+ fs.unlinkSync(oldFilePath);
113
+ }
114
+ this.logger.success(`${update.name}: updated to ${update.latestVersion}`);
115
+ }
116
+ catch (error) {
117
+ this.logger.fail(`${update.name}: ${error instanceof Error ? error.message : String(error)}`);
118
+ }
119
+ }
120
+ this.logger.log('');
121
+ this.logger.success('Update complete!');
122
+ this.logger.log('');
123
+ }
124
+ catch (error) {
125
+ this.logger.fail('Update failed');
126
+ this.exitWithError(this.formatError(error));
127
+ }
128
+ }
129
+ }
130
+ export const updateCommand = new UpdateCommand().getCommand();
@@ -0,0 +1,7 @@
1
+ import { BaseCommand } from './BaseCommand.js';
2
+ export declare class VerifyCommand extends BaseCommand {
3
+ constructor();
4
+ protected configure(): void;
5
+ private execute;
6
+ }
7
+ export declare const verifyCommand: import("commander").Command;