@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.
- package/.gitattributes +2 -0
- package/.github/dependabot.yml +9 -0
- package/.github/workflows/ci.yml +48 -0
- package/.prettierrc.json +12 -0
- package/CONTRIBUTING.md +56 -0
- package/LICENSE +190 -0
- package/NOTICE +17 -0
- package/README.md +509 -0
- package/SECURITY.md +35 -0
- package/build/commands/AcceptCommand.d.ts +7 -0
- package/build/commands/AcceptCommand.js +110 -0
- package/build/commands/BaseCommand.d.ts +12 -0
- package/build/commands/BaseCommand.js +27 -0
- package/build/commands/CompileCommand.d.ts +7 -0
- package/build/commands/CompileCommand.js +138 -0
- package/build/commands/ConfigCommand.d.ts +17 -0
- package/build/commands/ConfigCommand.js +124 -0
- package/build/commands/DeprecateCommand.d.ts +7 -0
- package/build/commands/DeprecateCommand.js +112 -0
- package/build/commands/InfoCommand.d.ts +10 -0
- package/build/commands/InfoCommand.js +223 -0
- package/build/commands/InitCommand.d.ts +16 -0
- package/build/commands/InitCommand.js +336 -0
- package/build/commands/InstallCommand.d.ts +7 -0
- package/build/commands/InstallCommand.js +130 -0
- package/build/commands/KeygenCommand.d.ts +13 -0
- package/build/commands/KeygenCommand.js +133 -0
- package/build/commands/ListCommand.d.ts +7 -0
- package/build/commands/ListCommand.js +117 -0
- package/build/commands/LoginCommand.d.ts +9 -0
- package/build/commands/LoginCommand.js +139 -0
- package/build/commands/LogoutCommand.d.ts +7 -0
- package/build/commands/LogoutCommand.js +57 -0
- package/build/commands/PublishCommand.d.ts +7 -0
- package/build/commands/PublishCommand.js +163 -0
- package/build/commands/SearchCommand.d.ts +7 -0
- package/build/commands/SearchCommand.js +97 -0
- package/build/commands/SignCommand.d.ts +7 -0
- package/build/commands/SignCommand.js +80 -0
- package/build/commands/TransferCommand.d.ts +8 -0
- package/build/commands/TransferCommand.js +179 -0
- package/build/commands/UndeprecateCommand.d.ts +7 -0
- package/build/commands/UndeprecateCommand.js +95 -0
- package/build/commands/UpdateCommand.d.ts +7 -0
- package/build/commands/UpdateCommand.js +130 -0
- package/build/commands/VerifyCommand.d.ts +7 -0
- package/build/commands/VerifyCommand.js +167 -0
- package/build/commands/WhoamiCommand.d.ts +7 -0
- package/build/commands/WhoamiCommand.js +84 -0
- package/build/index.d.ts +2 -0
- package/build/index.js +64 -0
- package/build/lib/PackageRegistry.abi.d.ts +2 -0
- package/build/lib/PackageRegistry.abi.js +356 -0
- package/build/lib/binary.d.ts +16 -0
- package/build/lib/binary.js +165 -0
- package/build/lib/config.d.ts +11 -0
- package/build/lib/config.js +160 -0
- package/build/lib/credentials.d.ts +10 -0
- package/build/lib/credentials.js +89 -0
- package/build/lib/ipfs.d.ts +16 -0
- package/build/lib/ipfs.js +209 -0
- package/build/lib/manifest.d.ts +14 -0
- package/build/lib/manifest.js +88 -0
- package/build/lib/provider.d.ts +9 -0
- package/build/lib/provider.js +48 -0
- package/build/lib/registry.d.ts +58 -0
- package/build/lib/registry.js +197 -0
- package/build/lib/wallet.d.ts +32 -0
- package/build/lib/wallet.js +114 -0
- package/build/types/PackageRegistry.d.ts +177 -0
- package/build/types/PackageRegistry.js +1 -0
- package/build/types/index.d.ts +30 -0
- package/build/types/index.js +52 -0
- package/eslint.config.js +41 -0
- package/gulpfile.js +41 -0
- package/package.json +83 -0
- package/src/commands/AcceptCommand.ts +151 -0
- package/src/commands/BaseCommand.ts +59 -0
- package/src/commands/CompileCommand.ts +196 -0
- package/src/commands/ConfigCommand.ts +144 -0
- package/src/commands/DeprecateCommand.ts +156 -0
- package/src/commands/InfoCommand.ts +293 -0
- package/src/commands/InitCommand.ts +465 -0
- package/src/commands/InstallCommand.ts +179 -0
- package/src/commands/KeygenCommand.ts +157 -0
- package/src/commands/ListCommand.ts +169 -0
- package/src/commands/LoginCommand.ts +197 -0
- package/src/commands/LogoutCommand.ts +76 -0
- package/src/commands/PublishCommand.ts +230 -0
- package/src/commands/SearchCommand.ts +141 -0
- package/src/commands/SignCommand.ts +122 -0
- package/src/commands/TransferCommand.ts +235 -0
- package/src/commands/UndeprecateCommand.ts +134 -0
- package/src/commands/UpdateCommand.ts +200 -0
- package/src/commands/VerifyCommand.ts +228 -0
- package/src/commands/WhoamiCommand.ts +113 -0
- package/src/index.ts +86 -0
- package/src/lib/PackageRegistry.abi.json +765 -0
- package/src/lib/PackageRegistry.abi.ts +365 -0
- package/src/lib/binary.ts +336 -0
- package/src/lib/config.ts +265 -0
- package/src/lib/credentials.ts +176 -0
- package/src/lib/ipfs.ts +369 -0
- package/src/lib/manifest.ts +172 -0
- package/src/lib/provider.ts +121 -0
- package/src/lib/registry.ts +464 -0
- package/src/lib/wallet.ts +271 -0
- package/src/types/PackageRegistry.ts +344 -0
- package/src/types/index.ts +145 -0
- package/tsconfig.json +25 -0
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Transfer command - Initiate ownership transfer
|
|
3
|
+
*
|
|
4
|
+
* @module commands/TransferCommand
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { confirm, input } from '@inquirer/prompts';
|
|
8
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
9
|
+
import {
|
|
10
|
+
getPackage,
|
|
11
|
+
getPendingScopeTransfer,
|
|
12
|
+
getPendingTransfer,
|
|
13
|
+
getScope,
|
|
14
|
+
} from '../lib/registry.js';
|
|
15
|
+
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
16
|
+
import { CLIWallet } from '../lib/wallet.js';
|
|
17
|
+
import { NetworkName } from '../types/index.js';
|
|
18
|
+
|
|
19
|
+
interface TransferOptions {
|
|
20
|
+
network: string;
|
|
21
|
+
yes?: boolean;
|
|
22
|
+
cancel?: boolean;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class TransferCommand extends BaseCommand {
|
|
26
|
+
constructor() {
|
|
27
|
+
super('transfer', 'Initiate ownership transfer of a package or scope');
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
protected configure(): void {
|
|
31
|
+
this.command
|
|
32
|
+
.argument('<name>', 'Package name or @scope')
|
|
33
|
+
.argument('[newOwner]', 'New owner address')
|
|
34
|
+
.option('-n, --network <network>', 'Network', 'mainnet')
|
|
35
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
36
|
+
.option('--cancel', 'Cancel pending transfer')
|
|
37
|
+
.action((name: string, newOwner?: string, options?: TransferOptions) =>
|
|
38
|
+
this.execute(name, newOwner, options || { network: 'mainnet' }),
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
private async execute(
|
|
43
|
+
name: string,
|
|
44
|
+
newOwner?: string,
|
|
45
|
+
options?: TransferOptions,
|
|
46
|
+
): Promise<void> {
|
|
47
|
+
try {
|
|
48
|
+
// Load credentials
|
|
49
|
+
this.logger.info('Loading wallet...');
|
|
50
|
+
const credentials = loadCredentials();
|
|
51
|
+
if (!credentials || !canSign(credentials)) {
|
|
52
|
+
this.logger.fail('No credentials configured');
|
|
53
|
+
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
54
|
+
process.exit(1);
|
|
55
|
+
}
|
|
56
|
+
CLIWallet.fromCredentials(credentials);
|
|
57
|
+
this.logger.success('Wallet loaded');
|
|
58
|
+
|
|
59
|
+
const network = (options?.network || 'mainnet') as NetworkName;
|
|
60
|
+
const isScope = name.startsWith('@') && !name.includes('/');
|
|
61
|
+
const displayName = isScope ? name : `package ${name}`;
|
|
62
|
+
|
|
63
|
+
if (options?.cancel) {
|
|
64
|
+
await this.handleCancel(name, isScope, network, options);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Initiate transfer
|
|
69
|
+
let targetOwner = newOwner;
|
|
70
|
+
if (!targetOwner) {
|
|
71
|
+
targetOwner = await input({
|
|
72
|
+
message: 'New owner address:',
|
|
73
|
+
validate: (value) => {
|
|
74
|
+
if (!value || value.length < 20) {
|
|
75
|
+
return 'Please enter a valid address';
|
|
76
|
+
}
|
|
77
|
+
return true;
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Verify target exists
|
|
83
|
+
this.logger.info(`Checking ${displayName}...`);
|
|
84
|
+
|
|
85
|
+
if (isScope) {
|
|
86
|
+
const scopeName = name.substring(1);
|
|
87
|
+
const scopeInfo = await getScope(scopeName, network);
|
|
88
|
+
if (!scopeInfo) {
|
|
89
|
+
this.logger.fail('Scope not found');
|
|
90
|
+
this.logger.error(`Scope "${name}" does not exist.`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
this.logger.success(`Found scope ${name}`);
|
|
94
|
+
} else {
|
|
95
|
+
const packageInfo = await getPackage(name, network);
|
|
96
|
+
if (!packageInfo) {
|
|
97
|
+
this.logger.fail('Package not found');
|
|
98
|
+
this.logger.error(`Package "${name}" does not exist.`);
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
this.logger.success(`Found package ${name}`);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// Display summary
|
|
105
|
+
this.logger.log('');
|
|
106
|
+
this.logger.info('Transfer Summary');
|
|
107
|
+
this.logger.log('─'.repeat(50));
|
|
108
|
+
this.logger.log(`Type: ${isScope ? 'Scope' : 'Package'}`);
|
|
109
|
+
this.logger.log(`Name: ${name}`);
|
|
110
|
+
this.logger.log(`New Owner: ${targetOwner}`);
|
|
111
|
+
this.logger.log(`Network: ${options?.network}`);
|
|
112
|
+
this.logger.log('');
|
|
113
|
+
|
|
114
|
+
this.logger.warn(
|
|
115
|
+
'Note: The new owner must call `opnet accept` to complete the transfer.',
|
|
116
|
+
);
|
|
117
|
+
this.logger.log('');
|
|
118
|
+
|
|
119
|
+
// Confirmation
|
|
120
|
+
if (!options?.yes) {
|
|
121
|
+
const confirmed = await confirm({
|
|
122
|
+
message: `Initiate transfer of ${displayName}?`,
|
|
123
|
+
default: false,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
if (!confirmed) {
|
|
127
|
+
this.logger.warn('Transfer cancelled.');
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// Execute transfer
|
|
133
|
+
this.logger.info('Initiating transfer...');
|
|
134
|
+
|
|
135
|
+
if (isScope) {
|
|
136
|
+
const scopeName = name.substring(1);
|
|
137
|
+
this.logger.warn('Transfer transaction required.');
|
|
138
|
+
this.logger.log('Transaction would call: initiateScopeTransfer(');
|
|
139
|
+
this.logger.log(` scopeName: "${scopeName}",`);
|
|
140
|
+
this.logger.log(` newOwner: "${targetOwner}"`);
|
|
141
|
+
this.logger.log(')');
|
|
142
|
+
} else {
|
|
143
|
+
this.logger.warn('Transfer transaction required.');
|
|
144
|
+
this.logger.log('Transaction would call: initiateTransfer(');
|
|
145
|
+
this.logger.log(` packageName: "${name}",`);
|
|
146
|
+
this.logger.log(` newOwner: "${targetOwner}"`);
|
|
147
|
+
this.logger.log(')');
|
|
148
|
+
}
|
|
149
|
+
this.logger.info('Transfer (transaction pending)');
|
|
150
|
+
|
|
151
|
+
this.logger.log('');
|
|
152
|
+
this.logger.success('Transfer initiated!');
|
|
153
|
+
this.logger.warn('Note: Registry transaction support is coming soon.');
|
|
154
|
+
this.logger.log('');
|
|
155
|
+
} catch (error) {
|
|
156
|
+
this.logger.fail('Transfer failed');
|
|
157
|
+
if (this.isUserCancelled(error)) {
|
|
158
|
+
this.logger.warn('Transfer cancelled.');
|
|
159
|
+
process.exit(0);
|
|
160
|
+
}
|
|
161
|
+
this.exitWithError(this.formatError(error));
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private async handleCancel(
|
|
166
|
+
name: string,
|
|
167
|
+
isScope: boolean,
|
|
168
|
+
network: NetworkName,
|
|
169
|
+
options: TransferOptions,
|
|
170
|
+
): Promise<void> {
|
|
171
|
+
this.logger.info('Checking pending transfer...');
|
|
172
|
+
|
|
173
|
+
if (isScope) {
|
|
174
|
+
const scopeName = name.substring(1);
|
|
175
|
+
const pending = await getPendingScopeTransfer(scopeName, network);
|
|
176
|
+
if (!pending) {
|
|
177
|
+
this.logger.warn('No pending transfer');
|
|
178
|
+
this.logger.log(`No pending transfer for ${name}.`);
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
this.logger.success(`Found pending transfer to ${pending.pendingOwner}`);
|
|
182
|
+
|
|
183
|
+
if (!options?.yes) {
|
|
184
|
+
const confirmed = await confirm({
|
|
185
|
+
message: `Cancel ownership transfer of ${name}?`,
|
|
186
|
+
default: true,
|
|
187
|
+
});
|
|
188
|
+
if (!confirmed) {
|
|
189
|
+
this.logger.warn('Cancelled.');
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
this.logger.info('Cancelling transfer...');
|
|
195
|
+
this.logger.warn('Cancellation transaction required.');
|
|
196
|
+
this.logger.log('Transaction would call: cancelScopeTransfer(');
|
|
197
|
+
this.logger.log(` scopeName: "${scopeName}"`);
|
|
198
|
+
this.logger.log(')');
|
|
199
|
+
this.logger.info('Cancellation (transaction pending)');
|
|
200
|
+
} else {
|
|
201
|
+
const pending = await getPendingTransfer(name, network);
|
|
202
|
+
if (!pending) {
|
|
203
|
+
this.logger.warn('No pending transfer');
|
|
204
|
+
this.logger.log(`No pending transfer for ${name}.`);
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
this.logger.success(`Found pending transfer to ${pending.pendingOwner}`);
|
|
208
|
+
|
|
209
|
+
if (!options?.yes) {
|
|
210
|
+
const confirmed = await confirm({
|
|
211
|
+
message: `Cancel ownership transfer of ${name}?`,
|
|
212
|
+
default: true,
|
|
213
|
+
});
|
|
214
|
+
if (!confirmed) {
|
|
215
|
+
this.logger.warn('Cancelled.');
|
|
216
|
+
return;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this.logger.info('Cancelling transfer...');
|
|
221
|
+
this.logger.warn('Cancellation transaction required.');
|
|
222
|
+
this.logger.log('Transaction would call: cancelTransfer(');
|
|
223
|
+
this.logger.log(` packageName: "${name}"`);
|
|
224
|
+
this.logger.log(')');
|
|
225
|
+
this.logger.info('Cancellation (transaction pending)');
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
this.logger.log('');
|
|
229
|
+
this.logger.success('Transfer cancellation submitted!');
|
|
230
|
+
this.logger.warn('Note: Registry transaction support is coming soon.');
|
|
231
|
+
this.logger.log('');
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
export const transferCommand = new TransferCommand().getCommand();
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Undeprecate command - Remove deprecation from a package version
|
|
3
|
+
*
|
|
4
|
+
* @module commands/UndeprecateCommand
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { confirm } from '@inquirer/prompts';
|
|
8
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
9
|
+
import { getPackage, getVersion, isVersionImmutable } from '../lib/registry.js';
|
|
10
|
+
import { canSign, loadCredentials } from '../lib/credentials.js';
|
|
11
|
+
import { CLIWallet } from '../lib/wallet.js';
|
|
12
|
+
import { NetworkName } from '../types/index.js';
|
|
13
|
+
|
|
14
|
+
interface UndeprecateOptions {
|
|
15
|
+
network: string;
|
|
16
|
+
yes?: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class UndeprecateCommand extends BaseCommand {
|
|
20
|
+
constructor() {
|
|
21
|
+
super('undeprecate', 'Remove deprecation from a package version');
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
protected configure(): void {
|
|
25
|
+
this.command
|
|
26
|
+
.argument('<package>', 'Package name (e.g., @scope/name or name)')
|
|
27
|
+
.argument('<version>', 'Version to undeprecate')
|
|
28
|
+
.option('-n, --network <network>', 'Network', 'mainnet')
|
|
29
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
30
|
+
.action((packageName: string, version: string, options?: UndeprecateOptions) =>
|
|
31
|
+
this.execute(packageName, version, options || { network: 'mainnet' }),
|
|
32
|
+
);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
private async execute(
|
|
36
|
+
packageName: string,
|
|
37
|
+
version: string,
|
|
38
|
+
options?: UndeprecateOptions,
|
|
39
|
+
): Promise<void> {
|
|
40
|
+
try {
|
|
41
|
+
// Load credentials
|
|
42
|
+
this.logger.info('Loading wallet...');
|
|
43
|
+
const credentials = loadCredentials();
|
|
44
|
+
if (!credentials || !canSign(credentials)) {
|
|
45
|
+
this.logger.fail('No credentials configured');
|
|
46
|
+
this.logger.warn('Run `opnet login` to configure your wallet.');
|
|
47
|
+
process.exit(1);
|
|
48
|
+
}
|
|
49
|
+
CLIWallet.fromCredentials(credentials);
|
|
50
|
+
this.logger.success('Wallet loaded');
|
|
51
|
+
|
|
52
|
+
// Get package info
|
|
53
|
+
this.logger.info('Fetching package info...');
|
|
54
|
+
const network = (options?.network || 'mainnet') as NetworkName;
|
|
55
|
+
const packageInfo = await getPackage(packageName, network);
|
|
56
|
+
|
|
57
|
+
if (!packageInfo) {
|
|
58
|
+
this.logger.fail('Package not found');
|
|
59
|
+
this.logger.error(`Package "${packageName}" does not exist.`);
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Get version info
|
|
64
|
+
const versionInfo = await getVersion(packageName, version, network);
|
|
65
|
+
if (!versionInfo) {
|
|
66
|
+
this.logger.fail('Version not found');
|
|
67
|
+
this.logger.error(`Version "${version}" does not exist.`);
|
|
68
|
+
process.exit(1);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (!versionInfo.deprecated) {
|
|
72
|
+
this.logger.warn('Not deprecated');
|
|
73
|
+
this.logger.log(`Version ${version} is not deprecated.`);
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Check if immutable
|
|
78
|
+
const immutable = await isVersionImmutable(packageName, version, network);
|
|
79
|
+
if (immutable) {
|
|
80
|
+
this.logger.fail('Version is immutable');
|
|
81
|
+
this.logger.error('This version is past the 72-hour mutability window.');
|
|
82
|
+
this.logger.error('Immutable versions cannot be undeprecated.');
|
|
83
|
+
process.exit(1);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
this.logger.success(`Found: ${packageName}@${version} (deprecated)`);
|
|
87
|
+
|
|
88
|
+
// Display summary
|
|
89
|
+
this.logger.log('');
|
|
90
|
+
this.logger.info('Undeprecation Summary');
|
|
91
|
+
this.logger.log('─'.repeat(50));
|
|
92
|
+
this.logger.log(`Package: ${packageName}`);
|
|
93
|
+
this.logger.log(`Version: ${version}`);
|
|
94
|
+
this.logger.log(`Network: ${options?.network}`);
|
|
95
|
+
this.logger.log('');
|
|
96
|
+
|
|
97
|
+
// Confirmation
|
|
98
|
+
if (!options?.yes) {
|
|
99
|
+
const confirmed = await confirm({
|
|
100
|
+
message: `Remove deprecation from ${packageName}@${version}?`,
|
|
101
|
+
default: true,
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
if (!confirmed) {
|
|
105
|
+
this.logger.warn('Undeprecation cancelled.');
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Execute undeprecation
|
|
111
|
+
this.logger.info('Removing deprecation...');
|
|
112
|
+
this.logger.warn('Undeprecation transaction required.');
|
|
113
|
+
this.logger.log('Transaction would call: undeprecateVersion(');
|
|
114
|
+
this.logger.log(` packageName: "${packageName}",`);
|
|
115
|
+
this.logger.log(` version: "${version}"`);
|
|
116
|
+
this.logger.log(')');
|
|
117
|
+
this.logger.info('Undeprecation (transaction pending)');
|
|
118
|
+
|
|
119
|
+
this.logger.log('');
|
|
120
|
+
this.logger.success('Undeprecation submitted!');
|
|
121
|
+
this.logger.warn('Note: Registry transaction support is coming soon.');
|
|
122
|
+
this.logger.log('');
|
|
123
|
+
} catch (error) {
|
|
124
|
+
this.logger.fail('Undeprecation failed');
|
|
125
|
+
if (this.isUserCancelled(error)) {
|
|
126
|
+
this.logger.warn('Undeprecation cancelled.');
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
this.exitWithError(this.formatError(error));
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
export const undeprecateCommand = new UndeprecateCommand().getCommand();
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Update command - Update installed plugins
|
|
3
|
+
*
|
|
4
|
+
* @module commands/UpdateCommand
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import * as fs from 'fs';
|
|
8
|
+
import * as path from 'path';
|
|
9
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
10
|
+
import { parseOpnetBinary, verifyChecksum } from '../lib/binary.js';
|
|
11
|
+
import { getPackage, getVersion } from '../lib/registry.js';
|
|
12
|
+
import { fetchFromIPFS } from '../lib/ipfs.js';
|
|
13
|
+
import { CLIWallet } from '../lib/wallet.js';
|
|
14
|
+
import { NetworkName } from '../types/index.js';
|
|
15
|
+
|
|
16
|
+
interface UpdateOptions {
|
|
17
|
+
dir?: string;
|
|
18
|
+
network: string;
|
|
19
|
+
skipVerify?: boolean;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface UpdateInfo {
|
|
23
|
+
file: string;
|
|
24
|
+
name: string;
|
|
25
|
+
currentVersion: string;
|
|
26
|
+
latestVersion: string;
|
|
27
|
+
cid: string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class UpdateCommand extends BaseCommand {
|
|
31
|
+
constructor() {
|
|
32
|
+
super('update', 'Update installed plugins to latest versions');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
protected configure(): void {
|
|
36
|
+
this.command
|
|
37
|
+
.argument('[package]', 'Specific package to update (default: all)')
|
|
38
|
+
.option('-d, --dir <path>', 'Plugins directory (default: ./plugins/)')
|
|
39
|
+
.option('-n, --network <network>', 'Network', 'mainnet')
|
|
40
|
+
.option('--skip-verify', 'Skip signature verification')
|
|
41
|
+
.action((packageName?: string, options?: UpdateOptions) =>
|
|
42
|
+
this.execute(packageName, options || { network: 'mainnet' }),
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
private async execute(packageName?: string, options?: UpdateOptions): Promise<void> {
|
|
47
|
+
try {
|
|
48
|
+
const pluginsDir = options?.dir || path.join(process.cwd(), 'plugins');
|
|
49
|
+
|
|
50
|
+
if (!fs.existsSync(pluginsDir)) {
|
|
51
|
+
this.logger.warn('No plugins directory found.');
|
|
52
|
+
this.logger.log(`Expected: ${pluginsDir}`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Find all .opnet files
|
|
57
|
+
const files = fs.readdirSync(pluginsDir).filter((f) => f.endsWith('.opnet'));
|
|
58
|
+
|
|
59
|
+
if (files.length === 0) {
|
|
60
|
+
this.logger.warn('No plugins installed.');
|
|
61
|
+
return;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const network = (options?.network || 'mainnet') as NetworkName;
|
|
65
|
+
const updates: UpdateInfo[] = [];
|
|
66
|
+
|
|
67
|
+
// Check for updates
|
|
68
|
+
this.logger.info('\nChecking for updates...\n');
|
|
69
|
+
|
|
70
|
+
for (const file of files) {
|
|
71
|
+
const filePath = path.join(pluginsDir, file);
|
|
72
|
+
|
|
73
|
+
try {
|
|
74
|
+
const data = fs.readFileSync(filePath);
|
|
75
|
+
const parsed = parseOpnetBinary(data);
|
|
76
|
+
const name = parsed.metadata.name;
|
|
77
|
+
|
|
78
|
+
// Filter by package name if specified
|
|
79
|
+
if (packageName && name !== packageName) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.logger.info(`Checking ${name}...`);
|
|
84
|
+
|
|
85
|
+
const packageInfo = await getPackage(name, network);
|
|
86
|
+
if (!packageInfo) {
|
|
87
|
+
this.logger.info(`${name}: not found in registry`);
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const currentVersion = parsed.metadata.version;
|
|
92
|
+
const latestVersion = packageInfo.latestVersion;
|
|
93
|
+
|
|
94
|
+
if (currentVersion === latestVersion) {
|
|
95
|
+
this.logger.success(`${name}@${currentVersion}: up to date`);
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// Get latest version info
|
|
100
|
+
const versionInfo = await getVersion(name, latestVersion, network);
|
|
101
|
+
if (!versionInfo) {
|
|
102
|
+
this.logger.warn(`${name}: latest version info unavailable`);
|
|
103
|
+
continue;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
this.logger.info(`${name}: ${currentVersion} -> ${latestVersion}`);
|
|
107
|
+
updates.push({
|
|
108
|
+
file,
|
|
109
|
+
name,
|
|
110
|
+
currentVersion,
|
|
111
|
+
latestVersion,
|
|
112
|
+
cid: versionInfo.ipfsCid,
|
|
113
|
+
});
|
|
114
|
+
} catch {
|
|
115
|
+
this.logger.warn(`${file}: failed to parse`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (updates.length === 0) {
|
|
120
|
+
this.logger.log('');
|
|
121
|
+
this.logger.success('All plugins are up to date!');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// Display updates
|
|
126
|
+
this.logger.log('');
|
|
127
|
+
this.logger.info('Available Updates:');
|
|
128
|
+
this.logger.log('─'.repeat(60));
|
|
129
|
+
for (const update of updates) {
|
|
130
|
+
this.logger.log(
|
|
131
|
+
` ${update.name}: ${update.currentVersion} -> ${update.latestVersion}`,
|
|
132
|
+
);
|
|
133
|
+
}
|
|
134
|
+
this.logger.log('');
|
|
135
|
+
|
|
136
|
+
// Perform updates
|
|
137
|
+
for (const update of updates) {
|
|
138
|
+
this.logger.info(`Updating ${update.name}...`);
|
|
139
|
+
|
|
140
|
+
try {
|
|
141
|
+
// Download from IPFS
|
|
142
|
+
const result = await fetchFromIPFS(update.cid);
|
|
143
|
+
|
|
144
|
+
// Verify
|
|
145
|
+
const parsed = parseOpnetBinary(result.data);
|
|
146
|
+
|
|
147
|
+
if (!verifyChecksum(parsed)) {
|
|
148
|
+
this.logger.fail(`${update.name}: checksum failed`);
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
if (!options?.skipVerify) {
|
|
153
|
+
const isUnsigned = parsed.publicKey.every((b) => b === 0);
|
|
154
|
+
if (!isUnsigned) {
|
|
155
|
+
const mldsaLevel = ([44, 65, 87] as const)[parsed.mldsaLevel];
|
|
156
|
+
const signatureValid = CLIWallet.verifyMLDSA(
|
|
157
|
+
parsed.checksum,
|
|
158
|
+
parsed.signature,
|
|
159
|
+
parsed.publicKey,
|
|
160
|
+
mldsaLevel,
|
|
161
|
+
);
|
|
162
|
+
|
|
163
|
+
if (!signatureValid) {
|
|
164
|
+
this.logger.fail(`${update.name}: signature invalid`);
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// Save updated plugin
|
|
171
|
+
const newFileName = `${update.name.replace(/^@/, '').replace(/\//g, '-')}-${update.latestVersion}.opnet`;
|
|
172
|
+
const newFilePath = path.join(pluginsDir, newFileName);
|
|
173
|
+
|
|
174
|
+
fs.writeFileSync(newFilePath, result.data);
|
|
175
|
+
|
|
176
|
+
// Remove old file if different
|
|
177
|
+
const oldFilePath = path.join(pluginsDir, update.file);
|
|
178
|
+
if (oldFilePath !== newFilePath && fs.existsSync(oldFilePath)) {
|
|
179
|
+
fs.unlinkSync(oldFilePath);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this.logger.success(`${update.name}: updated to ${update.latestVersion}`);
|
|
183
|
+
} catch (error) {
|
|
184
|
+
this.logger.fail(
|
|
185
|
+
`${update.name}: ${error instanceof Error ? error.message : String(error)}`,
|
|
186
|
+
);
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.logger.log('');
|
|
191
|
+
this.logger.success('Update complete!');
|
|
192
|
+
this.logger.log('');
|
|
193
|
+
} catch (error) {
|
|
194
|
+
this.logger.fail('Update failed');
|
|
195
|
+
this.exitWithError(this.formatError(error));
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
export const updateCommand = new UpdateCommand().getCommand();
|