@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,138 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as esbuild from 'esbuild';
|
|
4
|
+
import bytenode from 'bytenode';
|
|
5
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
6
|
+
import { loadManifest, getManifestPath } from '../lib/manifest.js';
|
|
7
|
+
import { buildOpnetBinary, formatFileSize, computeChecksum } from '../lib/binary.js';
|
|
8
|
+
import { CLIWallet } from '../lib/wallet.js';
|
|
9
|
+
import { loadCredentials, canSign } from '../lib/credentials.js';
|
|
10
|
+
export class CompileCommand extends BaseCommand {
|
|
11
|
+
constructor() {
|
|
12
|
+
super('compile', 'Compile plugin to .opnet binary format');
|
|
13
|
+
}
|
|
14
|
+
configure() {
|
|
15
|
+
this.command
|
|
16
|
+
.option('-o, --output <path>', 'Output file path')
|
|
17
|
+
.option('-d, --dir <path>', 'Plugin directory (default: current)')
|
|
18
|
+
.option('--no-sign', 'Skip signing (produce unsigned binary)')
|
|
19
|
+
.option('--minify', 'Minify the bundled code', true)
|
|
20
|
+
.option('--sourcemap', 'Generate source maps', false)
|
|
21
|
+
.action((options) => this.execute(options));
|
|
22
|
+
}
|
|
23
|
+
async execute(options) {
|
|
24
|
+
try {
|
|
25
|
+
const projectDir = options.dir ? path.resolve(options.dir) : process.cwd();
|
|
26
|
+
const manifestPath = getManifestPath(projectDir);
|
|
27
|
+
this.logger.info('Loading plugin manifest...');
|
|
28
|
+
const manifest = loadManifest(manifestPath);
|
|
29
|
+
this.logger.success(`Loaded manifest: ${manifest.name}@${manifest.version}`);
|
|
30
|
+
const srcDir = path.join(projectDir, 'src');
|
|
31
|
+
const entryPoint = path.join(srcDir, 'index.ts');
|
|
32
|
+
if (!fs.existsSync(entryPoint)) {
|
|
33
|
+
this.exitWithError(`Entry point not found: ${entryPoint}`);
|
|
34
|
+
}
|
|
35
|
+
this.logger.info('Bundling TypeScript...');
|
|
36
|
+
const bundleDir = path.join(projectDir, 'build', '.bundle');
|
|
37
|
+
fs.mkdirSync(bundleDir, { recursive: true });
|
|
38
|
+
const bundlePath = path.join(bundleDir, 'bundle.js');
|
|
39
|
+
await esbuild.build({
|
|
40
|
+
entryPoints: [entryPoint],
|
|
41
|
+
bundle: true,
|
|
42
|
+
platform: 'node',
|
|
43
|
+
target: 'es2022',
|
|
44
|
+
format: 'cjs',
|
|
45
|
+
outfile: bundlePath,
|
|
46
|
+
minify: options.minify,
|
|
47
|
+
sourcemap: options.sourcemap,
|
|
48
|
+
treeShaking: true,
|
|
49
|
+
external: [
|
|
50
|
+
'@btc-vision/plugin-sdk',
|
|
51
|
+
'@btc-vision/transaction',
|
|
52
|
+
'@btc-vision/bitcoin',
|
|
53
|
+
'opnet',
|
|
54
|
+
],
|
|
55
|
+
});
|
|
56
|
+
this.logger.success('TypeScript bundled');
|
|
57
|
+
this.logger.info('Compiling to V8 bytecode...');
|
|
58
|
+
const bytecodePath = path.join(bundleDir, 'bundle.jsc');
|
|
59
|
+
await bytenode.compileFile({
|
|
60
|
+
filename: bundlePath,
|
|
61
|
+
output: bytecodePath,
|
|
62
|
+
electron: false,
|
|
63
|
+
});
|
|
64
|
+
const bytecode = fs.readFileSync(bytecodePath);
|
|
65
|
+
this.logger.success(`V8 bytecode generated (${formatFileSize(bytecode.length)})`);
|
|
66
|
+
let proto = Buffer.alloc(0);
|
|
67
|
+
const protoPath = path.join(projectDir, 'plugin.proto');
|
|
68
|
+
if (fs.existsSync(protoPath)) {
|
|
69
|
+
proto = fs.readFileSync(protoPath);
|
|
70
|
+
this.logger.info(`Found proto file (${formatFileSize(proto.length)})`);
|
|
71
|
+
}
|
|
72
|
+
let publicKey;
|
|
73
|
+
let signature;
|
|
74
|
+
let mldsaLevel;
|
|
75
|
+
if (options.sign) {
|
|
76
|
+
this.logger.info('Loading wallet for signing...');
|
|
77
|
+
const credentials = loadCredentials();
|
|
78
|
+
if (!credentials || !canSign(credentials)) {
|
|
79
|
+
this.logger.fail('No credentials configured');
|
|
80
|
+
this.logger.warn('To sign plugins, run: opnet login');
|
|
81
|
+
this.logger.warn('Or use --no-sign to skip signing.');
|
|
82
|
+
process.exit(1);
|
|
83
|
+
}
|
|
84
|
+
const wallet = CLIWallet.fromCredentials(credentials);
|
|
85
|
+
mldsaLevel = wallet.securityLevel;
|
|
86
|
+
publicKey = wallet.mldsaPublicKey;
|
|
87
|
+
this.logger.success(`Wallet loaded (MLDSA-${mldsaLevel})`);
|
|
88
|
+
this.logger.info('Signing plugin...');
|
|
89
|
+
const metadataBytes = Buffer.from(JSON.stringify(manifest), 'utf-8');
|
|
90
|
+
const checksum = computeChecksum(metadataBytes, bytecode, proto);
|
|
91
|
+
signature = wallet.signMLDSA(checksum);
|
|
92
|
+
this.logger.success(`Plugin signed (${formatFileSize(signature.length)} signature)`);
|
|
93
|
+
}
|
|
94
|
+
else {
|
|
95
|
+
this.logger.warn('Skipping signing (--no-sign)');
|
|
96
|
+
mldsaLevel = 44;
|
|
97
|
+
publicKey = Buffer.alloc(1312);
|
|
98
|
+
signature = Buffer.alloc(2420);
|
|
99
|
+
}
|
|
100
|
+
this.logger.info('Assembling .opnet binary...');
|
|
101
|
+
const binary = buildOpnetBinary({
|
|
102
|
+
mldsaLevel,
|
|
103
|
+
publicKey,
|
|
104
|
+
signature,
|
|
105
|
+
metadata: manifest,
|
|
106
|
+
bytecode,
|
|
107
|
+
proto,
|
|
108
|
+
});
|
|
109
|
+
this.logger.success(`Binary assembled (${formatFileSize(binary.length)})`);
|
|
110
|
+
const outputPath = options.output ||
|
|
111
|
+
path.join(projectDir, 'build', `${manifest.name.replace(/^@/, '').replace(/\//g, '-')}.opnet`);
|
|
112
|
+
const outputDir = path.dirname(outputPath);
|
|
113
|
+
fs.mkdirSync(outputDir, { recursive: true });
|
|
114
|
+
fs.writeFileSync(outputPath, binary);
|
|
115
|
+
fs.rmSync(bundleDir, { recursive: true, force: true });
|
|
116
|
+
this.logger.log('');
|
|
117
|
+
this.logger.success('Compilation successful!');
|
|
118
|
+
this.logger.log('');
|
|
119
|
+
this.logger.log(`Output: ${outputPath}`);
|
|
120
|
+
this.logger.log(`Size: ${formatFileSize(binary.length)}`);
|
|
121
|
+
this.logger.log(`Plugin: ${manifest.name}@${manifest.version}`);
|
|
122
|
+
this.logger.log(`Type: ${manifest.pluginType}`);
|
|
123
|
+
this.logger.log(`MLDSA Level: ${mldsaLevel}`);
|
|
124
|
+
this.logger.log(`Signed: ${options.sign ? 'Yes' : 'No'}`);
|
|
125
|
+
this.logger.log('');
|
|
126
|
+
if (!options.sign) {
|
|
127
|
+
this.logger.warn('Note: This binary is unsigned and cannot be published.');
|
|
128
|
+
this.logger.warn('Use `opnet sign` to sign it, or compile with signing enabled.');
|
|
129
|
+
this.logger.log('');
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
this.logger.fail('Compilation failed');
|
|
134
|
+
this.exitWithError(this.formatError(error));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
export const compileCommand = new CompileCommand().getCommand();
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
3
|
+
export declare class ConfigCommand extends BaseCommand {
|
|
4
|
+
constructor();
|
|
5
|
+
protected configure(): void;
|
|
6
|
+
private createGetCommand;
|
|
7
|
+
private createSetCommand;
|
|
8
|
+
private createListCommand;
|
|
9
|
+
private createResetCommand;
|
|
10
|
+
private createPathCommand;
|
|
11
|
+
private handleGet;
|
|
12
|
+
private handleSet;
|
|
13
|
+
private handleList;
|
|
14
|
+
private handleReset;
|
|
15
|
+
private handlePath;
|
|
16
|
+
}
|
|
17
|
+
export declare const configCommand: Command;
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
5
|
+
import { saveConfig, getConfigValue, setConfigValue, displayConfig, } from '../lib/config.js';
|
|
6
|
+
export class ConfigCommand extends BaseCommand {
|
|
7
|
+
constructor() {
|
|
8
|
+
super('config', 'Manage CLI configuration');
|
|
9
|
+
}
|
|
10
|
+
configure() {
|
|
11
|
+
this.command
|
|
12
|
+
.addCommand(this.createGetCommand())
|
|
13
|
+
.addCommand(this.createSetCommand())
|
|
14
|
+
.addCommand(this.createListCommand())
|
|
15
|
+
.addCommand(this.createResetCommand())
|
|
16
|
+
.addCommand(this.createPathCommand());
|
|
17
|
+
}
|
|
18
|
+
createGetCommand() {
|
|
19
|
+
return new Command('get')
|
|
20
|
+
.description('Get a configuration value')
|
|
21
|
+
.argument('[key]', 'Configuration key (dot notation, e.g., rpcUrls.mainnet)')
|
|
22
|
+
.action((key) => {
|
|
23
|
+
this.handleGet(key);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
createSetCommand() {
|
|
27
|
+
return new Command('set')
|
|
28
|
+
.description('Set a configuration value')
|
|
29
|
+
.argument('<key>', 'Configuration key (dot notation)')
|
|
30
|
+
.argument('<value>', 'Value to set')
|
|
31
|
+
.action((key, value) => {
|
|
32
|
+
this.handleSet(key, value);
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
createListCommand() {
|
|
36
|
+
return new Command('list')
|
|
37
|
+
.description('List all configuration values')
|
|
38
|
+
.action(() => {
|
|
39
|
+
this.handleList();
|
|
40
|
+
});
|
|
41
|
+
}
|
|
42
|
+
createResetCommand() {
|
|
43
|
+
return new Command('reset')
|
|
44
|
+
.description('Reset configuration to defaults')
|
|
45
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
46
|
+
.action((options) => {
|
|
47
|
+
this.handleReset(options.yes);
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
createPathCommand() {
|
|
51
|
+
return new Command('path')
|
|
52
|
+
.description('Show configuration file path')
|
|
53
|
+
.action(() => {
|
|
54
|
+
this.handlePath();
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
handleGet(key) {
|
|
58
|
+
if (!key) {
|
|
59
|
+
this.logger.log(displayConfig());
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const value = getConfigValue(key);
|
|
63
|
+
if (value === undefined) {
|
|
64
|
+
this.exitWithError(`Configuration key not found: ${key}`);
|
|
65
|
+
}
|
|
66
|
+
if (typeof value === 'object') {
|
|
67
|
+
this.logger.log(JSON.stringify(value, null, 2));
|
|
68
|
+
}
|
|
69
|
+
else {
|
|
70
|
+
this.logger.log(String(value));
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
handleSet(key, value) {
|
|
74
|
+
let parsedValue = value;
|
|
75
|
+
try {
|
|
76
|
+
parsedValue = JSON.parse(value);
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
}
|
|
80
|
+
setConfigValue(key, parsedValue);
|
|
81
|
+
this.logger.success(`Set ${key} = ${JSON.stringify(parsedValue)}`);
|
|
82
|
+
}
|
|
83
|
+
handleList() {
|
|
84
|
+
this.logger.log(displayConfig());
|
|
85
|
+
}
|
|
86
|
+
handleReset(confirmed) {
|
|
87
|
+
if (!confirmed) {
|
|
88
|
+
this.logger.warn('This will reset all configuration to defaults.');
|
|
89
|
+
this.logger.info('Use --yes to confirm.');
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const defaultConfig = {
|
|
93
|
+
defaultNetwork: 'mainnet',
|
|
94
|
+
rpcUrls: {
|
|
95
|
+
mainnet: 'https://api.opnet.org',
|
|
96
|
+
testnet: 'https://testnet.opnet.org',
|
|
97
|
+
regtest: 'http://localhost:9001',
|
|
98
|
+
},
|
|
99
|
+
ipfsGateway: 'https://ipfs.opnet.org/ipfs/',
|
|
100
|
+
ipfsGateways: [
|
|
101
|
+
'https://ipfs.opnet.org/ipfs/',
|
|
102
|
+
'https://ipfs.io/ipfs/',
|
|
103
|
+
'https://cloudflare-ipfs.com/ipfs/',
|
|
104
|
+
'https://dweb.link/ipfs/',
|
|
105
|
+
],
|
|
106
|
+
ipfsPinningEndpoint: 'https://ipfs.opnet.org/api/v0/add',
|
|
107
|
+
ipfsPinningApiKey: '',
|
|
108
|
+
ipfsPinningAuthHeader: 'Authorization',
|
|
109
|
+
registryAddresses: {
|
|
110
|
+
mainnet: '',
|
|
111
|
+
testnet: '',
|
|
112
|
+
regtest: '',
|
|
113
|
+
},
|
|
114
|
+
defaultMldsaLevel: 44,
|
|
115
|
+
indexerUrl: 'https://indexer.opnet.org',
|
|
116
|
+
};
|
|
117
|
+
saveConfig(defaultConfig);
|
|
118
|
+
this.logger.success('Configuration reset to defaults.');
|
|
119
|
+
}
|
|
120
|
+
handlePath() {
|
|
121
|
+
this.logger.log(path.join(os.homedir(), '.opnet', 'config.json'));
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
export const configCommand = new ConfigCommand().getCommand();
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { input, 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 DeprecateCommand extends BaseCommand {
|
|
7
|
+
constructor() {
|
|
8
|
+
super('deprecate', 'Mark a package version as deprecated');
|
|
9
|
+
}
|
|
10
|
+
configure() {
|
|
11
|
+
this.command
|
|
12
|
+
.argument('<package>', 'Package name (e.g., @scope/name or name)')
|
|
13
|
+
.argument('[version]', 'Version to deprecate (default: latest)')
|
|
14
|
+
.option('-m, --message <message>', 'Deprecation reason/message')
|
|
15
|
+
.option('-n, --network <network>', 'Network', 'mainnet')
|
|
16
|
+
.option('-y, --yes', 'Skip confirmation')
|
|
17
|
+
.action((packageName, version, options) => this.execute(packageName, version, options || { network: 'mainnet' }));
|
|
18
|
+
}
|
|
19
|
+
async execute(packageName, version, 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
|
+
this.logger.info('Fetching package info...');
|
|
31
|
+
const network = (options?.network || 'mainnet');
|
|
32
|
+
const packageInfo = await getPackage(packageName, network);
|
|
33
|
+
if (!packageInfo) {
|
|
34
|
+
this.logger.fail('Package not found');
|
|
35
|
+
this.logger.error(`Package "${packageName}" does not exist.`);
|
|
36
|
+
process.exit(1);
|
|
37
|
+
}
|
|
38
|
+
const targetVersion = version || packageInfo.latestVersion;
|
|
39
|
+
if (!targetVersion) {
|
|
40
|
+
this.logger.fail('No version specified');
|
|
41
|
+
this.logger.error('Package has no versions.');
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
const versionInfo = await getVersion(packageName, targetVersion, network);
|
|
45
|
+
if (!versionInfo) {
|
|
46
|
+
this.logger.fail('Version not found');
|
|
47
|
+
this.logger.error(`Version "${targetVersion}" does not exist.`);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
}
|
|
50
|
+
if (versionInfo.deprecated) {
|
|
51
|
+
this.logger.warn('Already deprecated');
|
|
52
|
+
this.logger.log(`Version ${targetVersion} is already deprecated.`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
const immutable = await isVersionImmutable(packageName, targetVersion, network);
|
|
56
|
+
if (immutable) {
|
|
57
|
+
this.logger.fail('Version is immutable');
|
|
58
|
+
this.logger.error('This version is past the 72-hour mutability window.');
|
|
59
|
+
this.logger.error('Immutable versions cannot be deprecated.');
|
|
60
|
+
process.exit(1);
|
|
61
|
+
}
|
|
62
|
+
this.logger.success(`Found: ${packageName}@${targetVersion}`);
|
|
63
|
+
let message = options?.message;
|
|
64
|
+
if (!message && !options?.yes) {
|
|
65
|
+
message = await input({
|
|
66
|
+
message: 'Deprecation reason (optional):',
|
|
67
|
+
default: '',
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
message = message || 'No reason provided';
|
|
71
|
+
this.logger.log('');
|
|
72
|
+
this.logger.info('Deprecation Summary');
|
|
73
|
+
this.logger.log('─'.repeat(50));
|
|
74
|
+
this.logger.log(`Package: ${packageName}`);
|
|
75
|
+
this.logger.log(`Version: ${targetVersion}`);
|
|
76
|
+
this.logger.log(`Reason: ${message}`);
|
|
77
|
+
this.logger.log(`Network: ${options?.network}`);
|
|
78
|
+
this.logger.log('');
|
|
79
|
+
if (!options?.yes) {
|
|
80
|
+
const confirmed = await confirm({
|
|
81
|
+
message: `Deprecate ${packageName}@${targetVersion}?`,
|
|
82
|
+
default: false,
|
|
83
|
+
});
|
|
84
|
+
if (!confirmed) {
|
|
85
|
+
this.logger.warn('Deprecation cancelled.');
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
this.logger.info('Deprecating version...');
|
|
90
|
+
this.logger.warn('Deprecation transaction required.');
|
|
91
|
+
this.logger.log('Transaction would call: deprecateVersion(');
|
|
92
|
+
this.logger.log(` packageName: "${packageName}",`);
|
|
93
|
+
this.logger.log(` version: "${targetVersion}",`);
|
|
94
|
+
this.logger.log(` reason: "${message}"`);
|
|
95
|
+
this.logger.log(')');
|
|
96
|
+
this.logger.info('Deprecation (transaction pending)');
|
|
97
|
+
this.logger.log('');
|
|
98
|
+
this.logger.success('Deprecation submitted!');
|
|
99
|
+
this.logger.warn('Note: Registry transaction support is coming soon.');
|
|
100
|
+
this.logger.log('');
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
this.logger.fail('Deprecation failed');
|
|
104
|
+
if (this.isUserCancelled(error)) {
|
|
105
|
+
this.logger.warn('Deprecation cancelled.');
|
|
106
|
+
process.exit(0);
|
|
107
|
+
}
|
|
108
|
+
this.exitWithError(this.formatError(error));
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
export const deprecateCommand = new DeprecateCommand().getCommand();
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
export declare class InfoCommand extends BaseCommand {
|
|
3
|
+
constructor();
|
|
4
|
+
protected configure(): void;
|
|
5
|
+
private execute;
|
|
6
|
+
private displayBinaryInfo;
|
|
7
|
+
private displayProjectInfo;
|
|
8
|
+
private displayPermissions;
|
|
9
|
+
}
|
|
10
|
+
export declare const infoCommand: import("commander").Command;
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as crypto from 'crypto';
|
|
4
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
5
|
+
import { parseOpnetBinary, formatFileSize, getParsedMldsaLevel } from '../lib/binary.js';
|
|
6
|
+
import { loadManifest, getManifestPath } from '../lib/manifest.js';
|
|
7
|
+
export class InfoCommand extends BaseCommand {
|
|
8
|
+
constructor() {
|
|
9
|
+
super('info', 'Display information about a plugin or .opnet file');
|
|
10
|
+
}
|
|
11
|
+
configure() {
|
|
12
|
+
this.command
|
|
13
|
+
.argument('[path]', 'Path to plugin directory or .opnet file (default: current directory)')
|
|
14
|
+
.option('--json', 'Output as JSON')
|
|
15
|
+
.action((inputPath, options) => this.execute(inputPath, options));
|
|
16
|
+
}
|
|
17
|
+
execute(inputPath, options) {
|
|
18
|
+
try {
|
|
19
|
+
const targetPath = inputPath ? path.resolve(inputPath) : process.cwd();
|
|
20
|
+
if (!fs.existsSync(targetPath)) {
|
|
21
|
+
this.exitWithError(`Path not found: ${targetPath}`);
|
|
22
|
+
}
|
|
23
|
+
const stat = fs.statSync(targetPath);
|
|
24
|
+
if (stat.isFile() && targetPath.endsWith('.opnet')) {
|
|
25
|
+
this.displayBinaryInfo(targetPath, options?.json);
|
|
26
|
+
}
|
|
27
|
+
else if (stat.isDirectory()) {
|
|
28
|
+
this.displayProjectInfo(targetPath, options?.json);
|
|
29
|
+
}
|
|
30
|
+
else if (stat.isFile() && targetPath.endsWith('plugin.json')) {
|
|
31
|
+
this.displayProjectInfo(path.dirname(targetPath), options?.json);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
this.exitWithError('Invalid path. Provide a plugin directory or .opnet file.');
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
this.exitWithError(this.formatError(error));
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
displayBinaryInfo(filePath, json) {
|
|
42
|
+
const data = fs.readFileSync(filePath);
|
|
43
|
+
const parsed = parseOpnetBinary(data);
|
|
44
|
+
const mldsaLevel = getParsedMldsaLevel(parsed);
|
|
45
|
+
const isUnsigned = parsed.publicKey.every((b) => b === 0);
|
|
46
|
+
const publicKeyHash = crypto.createHash('sha256').update(parsed.publicKey).digest('hex');
|
|
47
|
+
if (json) {
|
|
48
|
+
const output = {
|
|
49
|
+
type: 'binary',
|
|
50
|
+
file: filePath,
|
|
51
|
+
fileSize: data.length,
|
|
52
|
+
formatVersion: parsed.formatVersion,
|
|
53
|
+
mldsaLevel,
|
|
54
|
+
signed: !isUnsigned,
|
|
55
|
+
publicKeyHash: isUnsigned ? null : publicKeyHash,
|
|
56
|
+
metadata: parsed.metadata,
|
|
57
|
+
sizes: {
|
|
58
|
+
metadata: parsed.rawMetadata.length,
|
|
59
|
+
bytecode: parsed.bytecode.length,
|
|
60
|
+
proto: parsed.proto?.length ?? 0,
|
|
61
|
+
publicKey: parsed.publicKey.length,
|
|
62
|
+
signature: parsed.signature.length,
|
|
63
|
+
},
|
|
64
|
+
};
|
|
65
|
+
this.logger.log(JSON.stringify(output, null, 2));
|
|
66
|
+
return;
|
|
67
|
+
}
|
|
68
|
+
const meta = parsed.metadata;
|
|
69
|
+
this.logger.info('\nOPNet Binary Information\n');
|
|
70
|
+
this.logger.log('─'.repeat(60));
|
|
71
|
+
this.logger.log(`File: ${filePath}`);
|
|
72
|
+
this.logger.log(`Size: ${formatFileSize(data.length)}`);
|
|
73
|
+
this.logger.log(`Format: v${parsed.formatVersion}`);
|
|
74
|
+
this.logger.log('');
|
|
75
|
+
this.logger.log('Plugin:');
|
|
76
|
+
this.logger.log(` Name: ${meta.name}`);
|
|
77
|
+
this.logger.log(` Version: ${meta.version}`);
|
|
78
|
+
this.logger.log(` Type: ${meta.pluginType}`);
|
|
79
|
+
this.logger.log(` OPNet: ${meta.opnetVersion}`);
|
|
80
|
+
if (meta.description) {
|
|
81
|
+
this.logger.log(` Description: ${meta.description}`);
|
|
82
|
+
}
|
|
83
|
+
this.logger.log('');
|
|
84
|
+
this.logger.log('Author:');
|
|
85
|
+
this.logger.log(` Name: ${meta.author.name}`);
|
|
86
|
+
if (meta.author.email) {
|
|
87
|
+
this.logger.log(` Email: ${meta.author.email}`);
|
|
88
|
+
}
|
|
89
|
+
this.logger.log('');
|
|
90
|
+
this.logger.log('Cryptography:');
|
|
91
|
+
this.logger.log(` MLDSA Level: MLDSA-${mldsaLevel}`);
|
|
92
|
+
this.logger.log(` Signed: ${isUnsigned ? 'No' : 'Yes'}`);
|
|
93
|
+
if (!isUnsigned) {
|
|
94
|
+
this.logger.log(` Publisher: ${publicKeyHash.substring(0, 32)}...`);
|
|
95
|
+
}
|
|
96
|
+
this.logger.log('');
|
|
97
|
+
this.logger.log('Sizes:');
|
|
98
|
+
this.logger.log(` Bytecode: ${formatFileSize(parsed.bytecode.length)}`);
|
|
99
|
+
this.logger.log(` Metadata: ${formatFileSize(parsed.rawMetadata.length)}`);
|
|
100
|
+
this.logger.log(` Proto: ${formatFileSize(parsed.proto?.length ?? 0)}`);
|
|
101
|
+
this.logger.log('');
|
|
102
|
+
this.logger.log('Permissions:');
|
|
103
|
+
this.displayPermissions(meta.permissions);
|
|
104
|
+
this.logger.log('');
|
|
105
|
+
if (Object.keys(meta.dependencies || {}).length > 0) {
|
|
106
|
+
this.logger.log('Dependencies:');
|
|
107
|
+
for (const [name, version] of Object.entries(meta.dependencies || {})) {
|
|
108
|
+
this.logger.log(` ${name}: ${version}`);
|
|
109
|
+
}
|
|
110
|
+
this.logger.log('');
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
displayProjectInfo(projectDir, json) {
|
|
114
|
+
const manifestPath = getManifestPath(projectDir);
|
|
115
|
+
if (!fs.existsSync(manifestPath)) {
|
|
116
|
+
this.logger.fail('No plugin.json found in this directory.');
|
|
117
|
+
this.logger.info('Run `opnet init` to create a new plugin project.');
|
|
118
|
+
process.exit(1);
|
|
119
|
+
}
|
|
120
|
+
const manifest = loadManifest(manifestPath);
|
|
121
|
+
const binaryPath = path.join(projectDir, 'build', `${manifest.name.replace(/^@/, '').replace(/\//g, '-')}.opnet`);
|
|
122
|
+
const hasBinary = fs.existsSync(binaryPath);
|
|
123
|
+
let binarySize = null;
|
|
124
|
+
if (hasBinary) {
|
|
125
|
+
binarySize = fs.statSync(binaryPath).size;
|
|
126
|
+
}
|
|
127
|
+
const srcDir = path.join(projectDir, 'src');
|
|
128
|
+
const hasSrc = fs.existsSync(srcDir);
|
|
129
|
+
const hasNodeModules = fs.existsSync(path.join(projectDir, 'node_modules'));
|
|
130
|
+
if (json) {
|
|
131
|
+
const output = {
|
|
132
|
+
type: 'project',
|
|
133
|
+
directory: projectDir,
|
|
134
|
+
manifest,
|
|
135
|
+
compiled: hasBinary,
|
|
136
|
+
binaryPath: hasBinary ? binaryPath : null,
|
|
137
|
+
binarySize,
|
|
138
|
+
hasSource: hasSrc,
|
|
139
|
+
hasNodeModules,
|
|
140
|
+
};
|
|
141
|
+
this.logger.log(JSON.stringify(output, null, 2));
|
|
142
|
+
return;
|
|
143
|
+
}
|
|
144
|
+
this.logger.info('\nOPNet Plugin Project\n');
|
|
145
|
+
this.logger.log('─'.repeat(60));
|
|
146
|
+
this.logger.log(`Directory: ${projectDir}`);
|
|
147
|
+
this.logger.log('');
|
|
148
|
+
this.logger.log('Plugin:');
|
|
149
|
+
this.logger.log(` Name: ${manifest.name}`);
|
|
150
|
+
this.logger.log(` Version: ${manifest.version}`);
|
|
151
|
+
this.logger.log(` Type: ${manifest.pluginType}`);
|
|
152
|
+
this.logger.log(` OPNet: ${manifest.opnetVersion}`);
|
|
153
|
+
if (manifest.description) {
|
|
154
|
+
this.logger.log(` Description: ${manifest.description}`);
|
|
155
|
+
}
|
|
156
|
+
this.logger.log('');
|
|
157
|
+
this.logger.log('Author:');
|
|
158
|
+
this.logger.log(` Name: ${manifest.author.name}`);
|
|
159
|
+
if (manifest.author.email) {
|
|
160
|
+
this.logger.log(` Email: ${manifest.author.email}`);
|
|
161
|
+
}
|
|
162
|
+
this.logger.log('');
|
|
163
|
+
this.logger.log('Status:');
|
|
164
|
+
this.logger.log(` Source: ${hasSrc ? 'Found' : 'Missing'}`);
|
|
165
|
+
this.logger.log(` Dependencies: ${hasNodeModules ? 'Installed' : 'Not installed'}`);
|
|
166
|
+
this.logger.log(` Compiled: ${hasBinary && binarySize !== null ? `Yes (${formatFileSize(binarySize)})` : 'No'}`);
|
|
167
|
+
this.logger.log('');
|
|
168
|
+
this.logger.log('Permissions:');
|
|
169
|
+
this.displayPermissions(manifest.permissions);
|
|
170
|
+
this.logger.log('');
|
|
171
|
+
if (manifest.resources) {
|
|
172
|
+
this.logger.log('Resources:');
|
|
173
|
+
if (manifest.resources.memory) {
|
|
174
|
+
this.logger.log(` Max Heap: ${manifest.resources.memory.maxHeapMB ?? 'N/A'} MB`);
|
|
175
|
+
}
|
|
176
|
+
if (manifest.resources.cpu) {
|
|
177
|
+
this.logger.log(` Max Threads: ${manifest.resources.cpu.maxThreads ?? 'N/A'}`);
|
|
178
|
+
this.logger.log(` Priority: ${manifest.resources.cpu.priority ?? 'normal'}`);
|
|
179
|
+
}
|
|
180
|
+
if (manifest.resources.timeout) {
|
|
181
|
+
this.logger.log(` Init Timeout: ${manifest.resources.timeout.initMs ?? 'N/A'} ms`);
|
|
182
|
+
this.logger.log(` Hook Timeout: ${manifest.resources.timeout.hookMs ?? 'N/A'} ms`);
|
|
183
|
+
}
|
|
184
|
+
this.logger.log('');
|
|
185
|
+
}
|
|
186
|
+
if (Object.keys(manifest.dependencies || {}).length > 0) {
|
|
187
|
+
this.logger.log('Plugin Dependencies:');
|
|
188
|
+
for (const [name, version] of Object.entries(manifest.dependencies || {})) {
|
|
189
|
+
this.logger.log(` ${name}: ${version}`);
|
|
190
|
+
}
|
|
191
|
+
this.logger.log('');
|
|
192
|
+
}
|
|
193
|
+
if (manifest.lifecycle) {
|
|
194
|
+
this.logger.log('Lifecycle:');
|
|
195
|
+
this.logger.log(` Load Priority: ${manifest.lifecycle.loadPriority ?? 100}`);
|
|
196
|
+
this.logger.log(` Enabled: ${manifest.lifecycle.enabledByDefault !== false ? 'Yes' : 'No'}`);
|
|
197
|
+
this.logger.log(` Requires Restart: ${manifest.lifecycle.requiresRestart ? 'Yes' : 'No'}`);
|
|
198
|
+
this.logger.log('');
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
displayPermissions(permissions) {
|
|
202
|
+
if (!permissions) {
|
|
203
|
+
this.logger.log(' (none configured)');
|
|
204
|
+
return;
|
|
205
|
+
}
|
|
206
|
+
const db = permissions.database?.enabled ?? false;
|
|
207
|
+
const blocks = permissions.blocks?.preProcess ||
|
|
208
|
+
permissions.blocks?.postProcess ||
|
|
209
|
+
permissions.blocks?.onChange ||
|
|
210
|
+
false;
|
|
211
|
+
const epochs = permissions.epochs?.onChange || permissions.epochs?.onFinalized || false;
|
|
212
|
+
const mempool = permissions.mempool?.txFeed ?? false;
|
|
213
|
+
const api = permissions.api?.addEndpoints || permissions.api?.addWebsocket || false;
|
|
214
|
+
const fsPerms = permissions.filesystem?.configDir || permissions.filesystem?.tempDir || false;
|
|
215
|
+
this.logger.log(` Database: ${db ? 'Yes' : 'No'}`);
|
|
216
|
+
this.logger.log(` Block Hooks: ${blocks ? 'Yes' : 'No'}`);
|
|
217
|
+
this.logger.log(` Epoch Hooks: ${epochs ? 'Yes' : 'No'}`);
|
|
218
|
+
this.logger.log(` Mempool Feed: ${mempool ? 'Yes' : 'No'}`);
|
|
219
|
+
this.logger.log(` API Endpoints: ${api ? 'Yes' : 'No'}`);
|
|
220
|
+
this.logger.log(` Filesystem: ${fsPerms ? 'Yes' : 'No'}`);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
export const infoCommand = new InfoCommand().getCommand();
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { BaseCommand } from './BaseCommand.js';
|
|
2
|
+
export declare class InitCommand extends BaseCommand {
|
|
3
|
+
constructor();
|
|
4
|
+
protected configure(): void;
|
|
5
|
+
private execute;
|
|
6
|
+
private gatherConfig;
|
|
7
|
+
private createProject;
|
|
8
|
+
private createPluginJson;
|
|
9
|
+
private createPackageJson;
|
|
10
|
+
private createTsConfig;
|
|
11
|
+
private createEntryPoint;
|
|
12
|
+
private createGitignore;
|
|
13
|
+
private createReadme;
|
|
14
|
+
private toPascalCase;
|
|
15
|
+
}
|
|
16
|
+
export declare const initCommand: import("commander").Command;
|