@btc-vision/cli 1.0.0 → 1.0.1

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 (37) hide show
  1. package/build/commands/AcceptCommand.js +2 -2
  2. package/build/commands/CompileCommand.js +3 -3
  3. package/build/commands/ConfigCommand.js +3 -7
  4. package/build/commands/DeprecateCommand.js +2 -2
  5. package/build/commands/InfoCommand.js +2 -2
  6. package/build/commands/InitCommand.d.ts +2 -0
  7. package/build/commands/InitCommand.js +113 -19
  8. package/build/commands/InstallCommand.js +1 -1
  9. package/build/commands/KeygenCommand.js +1 -1
  10. package/build/commands/ListCommand.js +6 -2
  11. package/build/commands/LoginCommand.js +22 -10
  12. package/build/commands/LogoutCommand.js +1 -1
  13. package/build/commands/PublishCommand.js +3 -3
  14. package/build/commands/SearchCommand.js +1 -1
  15. package/build/commands/SignCommand.js +6 -3
  16. package/build/commands/TransferCommand.js +3 -3
  17. package/build/commands/UndeprecateCommand.js +1 -1
  18. package/build/commands/VerifyCommand.js +5 -2
  19. package/build/commands/WhoamiCommand.js +1 -1
  20. package/build/lib/PackageRegistry.abi.js +1 -1
  21. package/build/lib/binary.js +8 -5
  22. package/build/lib/config.d.ts +1 -1
  23. package/build/lib/credentials.d.ts +1 -1
  24. package/build/lib/ipfs.js +3 -2
  25. package/build/lib/manifest.d.ts +1 -1
  26. package/build/lib/manifest.js +22 -11
  27. package/build/lib/provider.js +1 -1
  28. package/build/lib/registry.d.ts +1 -1
  29. package/build/lib/wallet.d.ts +5 -5
  30. package/build/lib/wallet.js +31 -31
  31. package/build/types/PackageRegistry.d.ts +1 -1
  32. package/build/types/index.js +1 -1
  33. package/package.json +3 -3
  34. package/src/commands/InitCommand.ts +86 -4
  35. package/src/lib/config.ts +3 -4
  36. package/src/lib/ipfs.ts +0 -1
  37. package/src/lib/manifest.ts +30 -7
@@ -1,7 +1,7 @@
1
1
  import { confirm } from '@inquirer/prompts';
2
2
  import { BaseCommand } from './BaseCommand.js';
3
- import { getPendingTransfer, getPendingScopeTransfer } from '../lib/registry.js';
4
- import { loadCredentials, canSign } from '../lib/credentials.js';
3
+ import { getPendingScopeTransfer, getPendingTransfer } from '../lib/registry.js';
4
+ import { canSign, loadCredentials } from '../lib/credentials.js';
5
5
  import { CLIWallet } from '../lib/wallet.js';
6
6
  export class AcceptCommand extends BaseCommand {
7
7
  constructor() {
@@ -3,10 +3,10 @@ import * as path from 'path';
3
3
  import * as esbuild from 'esbuild';
4
4
  import bytenode from 'bytenode';
5
5
  import { BaseCommand } from './BaseCommand.js';
6
- import { loadManifest, getManifestPath } from '../lib/manifest.js';
7
- import { buildOpnetBinary, formatFileSize, computeChecksum } from '../lib/binary.js';
6
+ import { getManifestPath, loadManifest } from '../lib/manifest.js';
7
+ import { buildOpnetBinary, computeChecksum, formatFileSize } from '../lib/binary.js';
8
8
  import { CLIWallet } from '../lib/wallet.js';
9
- import { loadCredentials, canSign } from '../lib/credentials.js';
9
+ import { canSign, loadCredentials } from '../lib/credentials.js';
10
10
  export class CompileCommand extends BaseCommand {
11
11
  constructor() {
12
12
  super('compile', 'Compile plugin to .opnet binary format');
@@ -2,7 +2,7 @@ import { Command } from 'commander';
2
2
  import * as os from 'os';
3
3
  import * as path from 'path';
4
4
  import { BaseCommand } from './BaseCommand.js';
5
- import { saveConfig, getConfigValue, setConfigValue, displayConfig, } from '../lib/config.js';
5
+ import { displayConfig, getConfigValue, saveConfig, setConfigValue } from '../lib/config.js';
6
6
  export class ConfigCommand extends BaseCommand {
7
7
  constructor() {
8
8
  super('config', 'Manage CLI configuration');
@@ -33,9 +33,7 @@ export class ConfigCommand extends BaseCommand {
33
33
  });
34
34
  }
35
35
  createListCommand() {
36
- return new Command('list')
37
- .description('List all configuration values')
38
- .action(() => {
36
+ return new Command('list').description('List all configuration values').action(() => {
39
37
  this.handleList();
40
38
  });
41
39
  }
@@ -48,9 +46,7 @@ export class ConfigCommand extends BaseCommand {
48
46
  });
49
47
  }
50
48
  createPathCommand() {
51
- return new Command('path')
52
- .description('Show configuration file path')
53
- .action(() => {
49
+ return new Command('path').description('Show configuration file path').action(() => {
54
50
  this.handlePath();
55
51
  });
56
52
  }
@@ -1,7 +1,7 @@
1
- import { input, confirm } from '@inquirer/prompts';
1
+ import { confirm, input } from '@inquirer/prompts';
2
2
  import { BaseCommand } from './BaseCommand.js';
3
3
  import { getPackage, getVersion, isVersionImmutable } from '../lib/registry.js';
4
- import { loadCredentials, canSign } from '../lib/credentials.js';
4
+ import { canSign, loadCredentials } from '../lib/credentials.js';
5
5
  import { CLIWallet } from '../lib/wallet.js';
6
6
  export class DeprecateCommand extends BaseCommand {
7
7
  constructor() {
@@ -2,8 +2,8 @@ import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import * as crypto from 'crypto';
4
4
  import { BaseCommand } from './BaseCommand.js';
5
- import { parseOpnetBinary, formatFileSize, getParsedMldsaLevel } from '../lib/binary.js';
6
- import { loadManifest, getManifestPath } from '../lib/manifest.js';
5
+ import { formatFileSize, getParsedMldsaLevel, parseOpnetBinary } from '../lib/binary.js';
6
+ import { getManifestPath, loadManifest } from '../lib/manifest.js';
7
7
  export class InfoCommand extends BaseCommand {
8
8
  constructor() {
9
9
  super('info', 'Display information about a plugin or .opnet file');
@@ -11,6 +11,8 @@ export declare class InitCommand extends BaseCommand {
11
11
  private createEntryPoint;
12
12
  private createGitignore;
13
13
  private createReadme;
14
+ private createEslintConfig;
15
+ private createPrettierConfig;
14
16
  private toPascalCase;
15
17
  }
16
18
  export declare const initCommand: import("commander").Command;
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
- import { input, select, confirm } from '@inquirer/prompts';
3
+ import { confirm, input, select } from '@inquirer/prompts';
4
4
  import { BaseCommand } from './BaseCommand.js';
5
5
  import { validatePluginName } from '../lib/manifest.js';
6
6
  export class InitCommand extends BaseCommand {
@@ -45,21 +45,29 @@ export class InitCommand extends BaseCommand {
45
45
  };
46
46
  }
47
47
  this.logger.info('\nOPNet Plugin Initialization\n');
48
- const pluginName = name || await input({
49
- message: 'Plugin name:',
50
- default: path.basename(process.cwd()),
51
- validate: (value) => {
52
- const errors = validatePluginName(value);
53
- return errors.length > 0 ? errors[0] : true;
54
- },
48
+ const pluginName = name ||
49
+ (await input({
50
+ message: 'Plugin name:',
51
+ default: path.basename(process.cwd()),
52
+ validate: (value) => {
53
+ const errors = validatePluginName(value);
54
+ return errors.length > 0 ? errors[0] : true;
55
+ },
56
+ }));
57
+ const description = (await input({ message: 'Description:', default: '' })) || undefined;
58
+ const authorName = await input({
59
+ message: 'Author name:',
60
+ default: process.env.USER || 'Author',
55
61
  });
56
- const description = await input({ message: 'Description:', default: '' }) || undefined;
57
- const authorName = await input({ message: 'Author name:', default: process.env.USER || 'Author' });
58
- const authorEmail = await input({ message: 'Author email (optional):', default: '' }) || undefined;
62
+ const authorEmail = (await input({ message: 'Author email (optional):', default: '' })) || undefined;
59
63
  const pluginType = await select({
60
64
  message: 'Plugin type:',
61
65
  choices: [
62
- { name: 'Standalone', value: 'standalone', description: 'Independent plugin' },
66
+ {
67
+ name: 'Standalone',
68
+ value: 'standalone',
69
+ description: 'Independent plugin',
70
+ },
63
71
  { name: 'Library', value: 'library', description: 'Shared library' },
64
72
  ],
65
73
  default: options?.template || 'standalone',
@@ -74,7 +82,10 @@ export class InitCommand extends BaseCommand {
74
82
  const projectDir = process.cwd();
75
83
  const pluginJsonPath = path.join(projectDir, 'plugin.json');
76
84
  if (fs.existsSync(pluginJsonPath) && !force) {
77
- const overwrite = await confirm({ message: 'plugin.json exists. Overwrite?', default: false });
85
+ const overwrite = await confirm({
86
+ message: 'plugin.json exists. Overwrite?',
87
+ default: false,
88
+ });
78
89
  if (!overwrite) {
79
90
  this.logger.warn('Initialization cancelled.');
80
91
  return;
@@ -94,6 +105,8 @@ export class InitCommand extends BaseCommand {
94
105
  this.createEntryPoint(projectDir, config, force);
95
106
  this.createGitignore(projectDir, force);
96
107
  this.createReadme(projectDir, config, force);
108
+ this.createEslintConfig(projectDir, force);
109
+ this.createPrettierConfig(projectDir, force);
97
110
  }
98
111
  createPluginJson(projectDir, config) {
99
112
  const manifest = {
@@ -103,7 +116,6 @@ export class InitCommand extends BaseCommand {
103
116
  main: 'dist/index.jsc',
104
117
  target: 'bytenode',
105
118
  type: 'plugin',
106
- checksum: '',
107
119
  author: config.authorEmail
108
120
  ? { name: config.authorName, email: config.authorEmail }
109
121
  : { name: config.authorName },
@@ -185,14 +197,25 @@ export class InitCommand extends BaseCommand {
185
197
  main: 'dist/index.js',
186
198
  scripts: {
187
199
  build: 'tsc',
188
- compile: 'opnet compile',
189
- verify: 'opnet verify',
200
+ compile: 'npx opnet compile',
201
+ verify: 'npx opnet verify',
190
202
  lint: 'eslint src/',
203
+ format: 'prettier --write src/',
191
204
  },
192
- author: config.authorEmail ? `${config.authorName} <${config.authorEmail}>` : config.authorName,
205
+ author: config.authorEmail
206
+ ? `${config.authorName} <${config.authorEmail}>`
207
+ : config.authorName,
193
208
  license: 'Apache-2.0',
194
209
  dependencies: { '@btc-vision/plugin-sdk': '^1.0.0' },
195
- devDependencies: { '@types/node': '^22.0.0', typescript: '^5.8.0', '@btc-vision/cli': '^1.0.0' },
210
+ devDependencies: {
211
+ '@eslint/js': '^9.39.0',
212
+ '@types/node': '^25.0.0',
213
+ eslint: '^9.39.0',
214
+ prettier: '^3.6.0',
215
+ typescript: '^5.8.0',
216
+ 'typescript-eslint': '^8.39.0',
217
+ '@btc-vision/cli': '^1.0.0',
218
+ },
196
219
  };
197
220
  fs.writeFileSync(packageJsonPath, JSON.stringify(packageJson, null, 4));
198
221
  this.logger.success(' Created package.json');
@@ -329,8 +352,79 @@ Apache-2.0
329
352
  `);
330
353
  this.logger.success(' Created README.md');
331
354
  }
355
+ createEslintConfig(projectDir, force) {
356
+ const eslintPath = path.join(projectDir, 'eslint.config.js');
357
+ if (fs.existsSync(eslintPath) && !force)
358
+ return;
359
+ const content = `// @ts-check
360
+
361
+ import eslint from '@eslint/js';
362
+ import tseslint from 'typescript-eslint';
363
+
364
+ export default tseslint.config(
365
+ eslint.configs.recommended,
366
+ ...tseslint.configs.strictTypeChecked,
367
+ {
368
+ languageOptions: {
369
+ parserOptions: {
370
+ projectService: true,
371
+ tsconfigDirName: import.meta.dirname,
372
+ },
373
+ },
374
+ rules: {
375
+ 'no-undef': 'off',
376
+ '@typescript-eslint/no-unused-vars': 'off',
377
+ 'no-empty': 'off',
378
+ '@typescript-eslint/restrict-template-expressions': 'off',
379
+ '@typescript-eslint/only-throw-error': 'off',
380
+ '@typescript-eslint/no-unnecessary-condition': 'off',
381
+ '@typescript-eslint/unbound-method': 'warn',
382
+ '@typescript-eslint/no-confusing-void-expression': 'off',
383
+ '@typescript-eslint/no-extraneous-class': 'off',
384
+ 'no-async-promise-executor': 'off',
385
+ '@typescript-eslint/no-misused-promises': 'off',
386
+ '@typescript-eslint/no-unnecessary-type-parameters': 'off',
387
+ '@typescript-eslint/no-duplicate-enum-values': 'off',
388
+ 'prefer-spread': 'off',
389
+ '@typescript-eslint/no-empty-object-type': 'off',
390
+ '@typescript-eslint/no-base-to-string': 'off',
391
+ '@typescript-eslint/no-dynamic-delete': 'off',
392
+ '@typescript-eslint/no-redundant-type-constituents': 'off',
393
+ },
394
+ },
395
+ {
396
+ files: ['**/*.js'],
397
+ ...tseslint.configs.disableTypeChecked,
398
+ },
399
+ );
400
+ `;
401
+ fs.writeFileSync(eslintPath, content);
402
+ this.logger.success(' Created eslint.config.js');
403
+ }
404
+ createPrettierConfig(projectDir, force) {
405
+ const prettierPath = path.join(projectDir, '.prettierrc.json');
406
+ if (fs.existsSync(prettierPath) && !force)
407
+ return;
408
+ const config = {
409
+ printWidth: 100,
410
+ trailingComma: 'all',
411
+ tabWidth: 4,
412
+ semi: true,
413
+ singleQuote: true,
414
+ quoteProps: 'as-needed',
415
+ bracketSpacing: true,
416
+ bracketSameLine: true,
417
+ arrowParens: 'always',
418
+ singleAttributePerLine: true,
419
+ };
420
+ fs.writeFileSync(prettierPath, JSON.stringify(config, null, 4));
421
+ this.logger.success(' Created .prettierrc.json');
422
+ }
332
423
  toPascalCase(str) {
333
- return str.split(/[-_]/).map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase()).join('');
424
+ return str
425
+ .split(/[-_]/)
426
+ .map((w) => w.charAt(0).toUpperCase() + w.slice(1).toLowerCase())
427
+ .join('');
334
428
  }
335
429
  }
336
430
  export const initCommand = new InitCommand().getCommand();
@@ -3,7 +3,7 @@ import * as path from 'path';
3
3
  import { BaseCommand } from './BaseCommand.js';
4
4
  import { getPackage, getVersion, registryToMldsaLevel } from '../lib/registry.js';
5
5
  import { fetchFromIPFS, isValidCid } from '../lib/ipfs.js';
6
- import { parseOpnetBinary, verifyChecksum, formatFileSize } from '../lib/binary.js';
6
+ import { formatFileSize, parseOpnetBinary, verifyChecksum } from '../lib/binary.js';
7
7
  import { CLIWallet } from '../lib/wallet.js';
8
8
  export class InstallCommand extends BaseCommand {
9
9
  constructor() {
@@ -1,7 +1,7 @@
1
1
  import { Command } from 'commander';
2
2
  import * as fs from 'fs';
3
3
  import { BaseCommand } from './BaseCommand.js';
4
- import { generateMLDSAKeypair, generateMnemonic, computePublicKeyHash } from '../lib/wallet.js';
4
+ import { computePublicKeyHash, generateMLDSAKeypair, generateMnemonic } from '../lib/wallet.js';
5
5
  import { isValidMldsaLevel } from '../lib/credentials.js';
6
6
  export class KeygenCommand extends BaseCommand {
7
7
  constructor() {
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
3
  import { BaseCommand } from './BaseCommand.js';
4
- import { parseOpnetBinary, formatFileSize } from '../lib/binary.js';
4
+ import { formatFileSize, parseOpnetBinary } from '../lib/binary.js';
5
5
  export class ListCommand extends BaseCommand {
6
6
  constructor() {
7
7
  super('list', 'List installed plugins');
@@ -98,7 +98,11 @@ export class ListCommand extends BaseCommand {
98
98
  const versionWidth = 12;
99
99
  const typeWidth = 12;
100
100
  const sizeWidth = 10;
101
- this.logger.info('Name'.padEnd(nameWidth) + 'Version'.padEnd(versionWidth) + 'Type'.padEnd(typeWidth) + 'Size'.padEnd(sizeWidth) + 'Signed');
101
+ this.logger.info('Name'.padEnd(nameWidth) +
102
+ 'Version'.padEnd(versionWidth) +
103
+ 'Type'.padEnd(typeWidth) +
104
+ 'Size'.padEnd(sizeWidth) +
105
+ 'Signed');
102
106
  this.logger.info('─'.repeat(nameWidth + versionWidth + typeWidth + sizeWidth + 8));
103
107
  for (const plugin of plugins) {
104
108
  const signedText = plugin.signed ? 'Yes' : 'No';
@@ -1,7 +1,7 @@
1
- import { select, confirm, password } from '@inquirer/prompts';
1
+ import { confirm, password, select } from '@inquirer/prompts';
2
2
  import { BaseCommand } from './BaseCommand.js';
3
- import { saveCredentials, isValidMldsaLevel, isValidNetwork } from '../lib/credentials.js';
4
- import { validateMnemonic, CLIWallet } from '../lib/wallet.js';
3
+ import { isValidMldsaLevel, isValidNetwork, saveCredentials } from '../lib/credentials.js';
4
+ import { CLIWallet, validateMnemonic } from '../lib/wallet.js';
5
5
  export class LoginCommand extends BaseCommand {
6
6
  constructor() {
7
7
  super('login', 'Configure wallet credentials for signing and publishing');
@@ -95,7 +95,7 @@ export class LoginCommand extends BaseCommand {
95
95
  },
96
96
  ],
97
97
  });
98
- const selectedNetwork = await select({
98
+ const selectedNetwork = (await select({
99
99
  message: 'Select network:',
100
100
  choices: [
101
101
  { name: 'Mainnet', value: 'mainnet' },
@@ -103,16 +103,28 @@ export class LoginCommand extends BaseCommand {
103
103
  { name: 'Regtest', value: 'regtest' },
104
104
  ],
105
105
  default: defaultNetwork,
106
- });
107
- const selectedLevel = await select({
106
+ }));
107
+ const selectedLevel = (await select({
108
108
  message: 'Select MLDSA security level:',
109
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' },
110
+ {
111
+ name: 'MLDSA-44 (Level 2, fastest)',
112
+ value: 44,
113
+ description: '1312 byte public key',
114
+ },
115
+ {
116
+ name: 'MLDSA-65 (Level 3, balanced)',
117
+ value: 65,
118
+ description: '1952 byte public key',
119
+ },
120
+ {
121
+ name: 'MLDSA-87 (Level 5, most secure)',
122
+ value: 87,
123
+ description: '2592 byte public key',
124
+ },
113
125
  ],
114
126
  default: defaultLevel,
115
- });
127
+ }));
116
128
  if (loginMethod === 'mnemonic') {
117
129
  const mnemonic = await password({
118
130
  message: 'Enter your mnemonic phrase (12 or 24 words):',
@@ -1,6 +1,6 @@
1
1
  import { confirm } from '@inquirer/prompts';
2
2
  import { BaseCommand } from './BaseCommand.js';
3
- import { deleteCredentials, hasCredentials, getCredentialSource } from '../lib/credentials.js';
3
+ import { deleteCredentials, getCredentialSource, hasCredentials } from '../lib/credentials.js';
4
4
  export class LogoutCommand extends BaseCommand {
5
5
  constructor() {
6
6
  super('logout', 'Remove stored wallet credentials');
@@ -3,11 +3,11 @@ import * as path from 'path';
3
3
  import * as crypto from 'crypto';
4
4
  import { confirm } from '@inquirer/prompts';
5
5
  import { BaseCommand } from './BaseCommand.js';
6
- import { parseOpnetBinary, formatFileSize, verifyChecksum } from '../lib/binary.js';
6
+ import { formatFileSize, parseOpnetBinary, verifyChecksum } from '../lib/binary.js';
7
7
  import { CLIWallet } from '../lib/wallet.js';
8
- import { loadCredentials, canSign } from '../lib/credentials.js';
8
+ import { canSign, loadCredentials } from '../lib/credentials.js';
9
9
  import { uploadPlugin } from '../lib/ipfs.js';
10
- import { getPackage, getScope, parsePackageName, computePermissionsHash, encodeDependencies, pluginTypeToRegistry, mldsaLevelToRegistry, } from '../lib/registry.js';
10
+ import { computePermissionsHash, encodeDependencies, getPackage, getScope, mldsaLevelToRegistry, parsePackageName, pluginTypeToRegistry, } from '../lib/registry.js';
11
11
  export class PublishCommand extends BaseCommand {
12
12
  constructor() {
13
13
  super('publish', 'Publish a plugin to the OPNet registry');
@@ -1,5 +1,5 @@
1
1
  import { BaseCommand } from './BaseCommand.js';
2
- import { getPackage, getVersion, registryToMldsaLevel, registryToPluginType } from '../lib/registry.js';
2
+ import { getPackage, getVersion, registryToMldsaLevel, registryToPluginType, } from '../lib/registry.js';
3
3
  export class SearchCommand extends BaseCommand {
4
4
  constructor() {
5
5
  super('search', 'Search for plugins in the registry');
@@ -1,9 +1,9 @@
1
1
  import * as fs from 'fs';
2
2
  import * as crypto from 'crypto';
3
3
  import { BaseCommand } from './BaseCommand.js';
4
- import { parseOpnetBinary, buildOpnetBinary, computeChecksum, formatFileSize } from '../lib/binary.js';
4
+ import { buildOpnetBinary, computeChecksum, formatFileSize, parseOpnetBinary, } from '../lib/binary.js';
5
5
  import { CLIWallet } from '../lib/wallet.js';
6
- import { loadCredentials, canSign } from '../lib/credentials.js';
6
+ import { canSign, loadCredentials } from '../lib/credentials.js';
7
7
  export class SignCommand extends BaseCommand {
8
8
  constructor() {
9
9
  super('sign', 'Sign or re-sign a .opnet binary with your MLDSA key');
@@ -34,7 +34,10 @@ export class SignCommand extends BaseCommand {
34
34
  const parsed = parseOpnetBinary(data);
35
35
  this.logger.success(`Parsed: ${parsed.metadata.name}@${parsed.metadata.version}`);
36
36
  const isUnsigned = parsed.publicKey.every((b) => b === 0);
37
- const currentPkHash = crypto.createHash('sha256').update(parsed.publicKey).digest('hex');
37
+ const currentPkHash = crypto
38
+ .createHash('sha256')
39
+ .update(parsed.publicKey)
40
+ .digest('hex');
38
41
  const newPkHash = wallet.mldsaPublicKeyHash;
39
42
  if (!isUnsigned && currentPkHash !== newPkHash && !options.force) {
40
43
  this.logger.log('');
@@ -1,7 +1,7 @@
1
- import { input, confirm } from '@inquirer/prompts';
1
+ import { confirm, input } from '@inquirer/prompts';
2
2
  import { BaseCommand } from './BaseCommand.js';
3
- import { getPackage, getScope, getPendingTransfer, getPendingScopeTransfer, } from '../lib/registry.js';
4
- import { loadCredentials, canSign } from '../lib/credentials.js';
3
+ import { getPackage, getPendingScopeTransfer, getPendingTransfer, getScope, } from '../lib/registry.js';
4
+ import { canSign, loadCredentials } from '../lib/credentials.js';
5
5
  import { CLIWallet } from '../lib/wallet.js';
6
6
  export class TransferCommand extends BaseCommand {
7
7
  constructor() {
@@ -1,7 +1,7 @@
1
1
  import { confirm } from '@inquirer/prompts';
2
2
  import { BaseCommand } from './BaseCommand.js';
3
3
  import { getPackage, getVersion, isVersionImmutable } from '../lib/registry.js';
4
- import { loadCredentials, canSign } from '../lib/credentials.js';
4
+ import { canSign, loadCredentials } from '../lib/credentials.js';
5
5
  import { CLIWallet } from '../lib/wallet.js';
6
6
  export class UndeprecateCommand extends BaseCommand {
7
7
  constructor() {
@@ -1,7 +1,7 @@
1
1
  import * as fs from 'fs';
2
2
  import * as crypto from 'crypto';
3
3
  import { BaseCommand } from './BaseCommand.js';
4
- import { parseOpnetBinary, verifyChecksum, formatFileSize } from '../lib/binary.js';
4
+ import { formatFileSize, parseOpnetBinary, verifyChecksum } from '../lib/binary.js';
5
5
  import { CLIWallet } from '../lib/wallet.js';
6
6
  export class VerifyCommand extends BaseCommand {
7
7
  constructor() {
@@ -68,7 +68,10 @@ export class VerifyCommand extends BaseCommand {
68
68
  signatureError,
69
69
  isUnsigned,
70
70
  metadata: parsed.metadata,
71
- publicKeyHash: crypto.createHash('sha256').update(parsed.publicKey).digest('hex'),
71
+ publicKeyHash: crypto
72
+ .createHash('sha256')
73
+ .update(parsed.publicKey)
74
+ .digest('hex'),
72
75
  bytecodeSize: parsed.bytecode.length,
73
76
  protoSize: parsed.proto?.length ?? 0,
74
77
  };
@@ -1,5 +1,5 @@
1
1
  import { BaseCommand } from './BaseCommand.js';
2
- import { loadCredentials, hasCredentials, getCredentialSource, maskSensitive, } from '../lib/credentials.js';
2
+ import { getCredentialSource, hasCredentials, loadCredentials, maskSensitive, } from '../lib/credentials.js';
3
3
  import { CLIWallet } from '../lib/wallet.js';
4
4
  export class WhoamiCommand extends BaseCommand {
5
5
  constructor() {
@@ -1,5 +1,5 @@
1
- import { ABIDataTypes } from '@btc-vision/transaction';
2
1
  import { BitcoinAbiTypes } from 'opnet';
2
+ import { ABIDataTypes } from '@btc-vision/transaction';
3
3
  export const PACKAGE_REGISTRY_ABI = [
4
4
  {
5
5
  name: 'setTreasuryAddress',
@@ -1,5 +1,5 @@
1
1
  import * as crypto from 'crypto';
2
- import { PLUGIN_MAGIC_BYTES, PLUGIN_FORMAT_VERSION, MLDSA_PUBLIC_KEY_SIZES, MLDSA_SIGNATURE_SIZES, } from '@btc-vision/plugin-sdk';
2
+ import { MLDSA_PUBLIC_KEY_SIZES, MLDSA_SIGNATURE_SIZES, PLUGIN_FORMAT_VERSION, PLUGIN_MAGIC_BYTES, } from '@btc-vision/plugin-sdk';
3
3
  import { cliLevelToMLDSALevel, mldsaLevelToCLI } from '../types/index.js';
4
4
  export function parseOpnetBinary(data) {
5
5
  let offset = 0;
@@ -91,7 +91,7 @@ export function verifyChecksum(parsed) {
91
91
  return computed.equals(parsed.checksum);
92
92
  }
93
93
  export function buildOpnetBinary(options) {
94
- const { mldsaLevel, publicKey, signature, metadata, bytecode, proto = Buffer.alloc(0) } = options;
94
+ const { mldsaLevel, publicKey, signature, metadata, bytecode, proto = Buffer.alloc(0), } = options;
95
95
  const sdkLevel = cliLevelToMLDSALevel(mldsaLevel);
96
96
  const expectedPkSize = MLDSA_PUBLIC_KEY_SIZES[sdkLevel];
97
97
  const expectedSigSize = MLDSA_SIGNATURE_SIZES[sdkLevel];
@@ -109,9 +109,12 @@ export function buildOpnetBinary(options) {
109
109
  1 +
110
110
  publicKey.length +
111
111
  signature.length +
112
- 4 + metadataBytes.length +
113
- 4 + bytecode.length +
114
- 4 + proto.length +
112
+ 4 +
113
+ metadataBytes.length +
114
+ 4 +
115
+ bytecode.length +
116
+ 4 +
117
+ proto.length +
115
118
  32;
116
119
  const buffer = Buffer.alloc(totalSize);
117
120
  let offset = 0;
@@ -1,4 +1,4 @@
1
- import { CLIConfig, NetworkName, CLIMldsaLevel } from '../types/index.js';
1
+ import { CLIConfig, CLIMldsaLevel, NetworkName } from '../types/index.js';
2
2
  export declare function ensureConfigDir(): void;
3
3
  export declare function loadConfig(): CLIConfig;
4
4
  export declare function saveConfig(config: CLIConfig): void;
@@ -1,4 +1,4 @@
1
- import { CLICredentials, NetworkName, CLIMldsaLevel } from '../types/index.js';
1
+ import { CLICredentials, CLIMldsaLevel, NetworkName } from '../types/index.js';
2
2
  export declare function loadCredentials(): CLICredentials | null;
3
3
  export declare function saveCredentials(credentials: CLICredentials): void;
4
4
  export declare function deleteCredentials(): boolean;
package/build/lib/ipfs.js CHANGED
@@ -62,7 +62,9 @@ export async function pinToIPFS(data, name) {
62
62
  'Content-Length': body.length.toString(),
63
63
  };
64
64
  if (config.ipfsPinningAuthHeader) {
65
- const [headerName, headerValue] = config.ipfsPinningAuthHeader.split(':').map((s) => s.trim());
65
+ const [headerName, headerValue] = config.ipfsPinningAuthHeader
66
+ .split(':')
67
+ .map((s) => s.trim());
66
68
  if (headerName && headerValue) {
67
69
  headers[headerName] = headerValue;
68
70
  }
@@ -139,7 +141,6 @@ export async function fetchFromIPFS(cid) {
139
141
  }
140
142
  catch (error) {
141
143
  lastError = error instanceof Error ? error : new Error(String(error));
142
- continue;
143
144
  }
144
145
  }
145
146
  throw new Error(`Failed to fetch from all gateways: ${lastError?.message}`);
@@ -10,5 +10,5 @@ export declare function createManifest(options: {
10
10
  email?: string;
11
11
  description?: string;
12
12
  pluginType: 'standalone' | 'library';
13
- }): IPluginMetadata;
13
+ }): Omit<IPluginMetadata, 'checksum'>;
14
14
  export declare function getManifestPath(dir?: string): string;
@@ -1,6 +1,6 @@
1
1
  import * as fs from 'fs';
2
2
  import * as path from 'path';
3
- import { validateManifest as sdkValidateManifest, PLUGIN_NAME_REGEX, PLUGIN_MANIFEST_FILENAME, DEFAULT_PERMISSIONS, DEFAULT_RESOURCES, DEFAULT_LIFECYCLE, } from '@btc-vision/plugin-sdk';
3
+ import { DEFAULT_LIFECYCLE, DEFAULT_PERMISSIONS, DEFAULT_RESOURCES, PLUGIN_MANIFEST_FILENAME, PLUGIN_NAME_REGEX, validateManifest as sdkValidateManifest, } from '@btc-vision/plugin-sdk';
4
4
  const SCOPED_NAME_PATTERN = /^@[a-z][a-z0-9-]*\/[a-z][a-z0-9-]*$/;
5
5
  export function validatePluginName(name) {
6
6
  const errors = [];
@@ -29,8 +29,7 @@ export function validateManifest(manifest) {
29
29
  export function validatePermissions(permissions) {
30
30
  const errors = [];
31
31
  if (permissions.database?.enabled) {
32
- if (!permissions.database.collections ||
33
- !Array.isArray(permissions.database.collections)) {
32
+ if (!permissions.database.collections || !Array.isArray(permissions.database.collections)) {
34
33
  errors.push('database.collections must be an array when database is enabled');
35
34
  }
36
35
  }
@@ -49,14 +48,27 @@ export function loadManifest(manifestPath) {
49
48
  catch (e) {
50
49
  throw new Error(`Failed to parse manifest: ${e instanceof Error ? e.message : String(e)}`);
51
50
  }
52
- const result = validateManifest(manifest);
53
- if (!result.valid) {
54
- const errorList = result.errors
55
- .map((e) => ` - ${e.path}: ${e.message}`)
56
- .join('\n');
57
- throw new Error(`Invalid manifest:\n${errorList}`);
51
+ const errors = [];
52
+ if (!manifest.name || typeof manifest.name !== 'string') {
53
+ errors.push('name is required');
54
+ }
55
+ if (!manifest.version || typeof manifest.version !== 'string') {
56
+ errors.push('version is required');
57
+ }
58
+ if (!manifest.author || typeof manifest.author !== 'object') {
59
+ errors.push('author is required');
60
+ }
61
+ if (!manifest.pluginType || !['standalone', 'library'].includes(manifest.pluginType)) {
62
+ errors.push('pluginType must be "standalone" or "library"');
63
+ }
64
+ if (errors.length > 0) {
65
+ throw new Error(`Invalid manifest:\n${errors.map((e) => ` - ${e}`).join('\n')}`);
66
+ }
67
+ const result = manifest;
68
+ if (!result.checksum) {
69
+ result.checksum = '';
58
70
  }
59
- return manifest;
71
+ return result;
60
72
  }
61
73
  export function saveManifest(manifestPath, manifest) {
62
74
  const content = JSON.stringify(manifest, null, 4);
@@ -70,7 +82,6 @@ export function createManifest(options) {
70
82
  main: 'dist/index.jsc',
71
83
  target: 'bytenode',
72
84
  type: 'plugin',
73
- checksum: '',
74
85
  author: {
75
86
  name: options.author,
76
87
  email: options.email,
@@ -1,5 +1,5 @@
1
1
  import { JSONRpcProvider } from 'opnet';
2
- import { getRpcUrl, getRegistryAddress, loadConfig } from './config.js';
2
+ import { getRegistryAddress, getRpcUrl, loadConfig } from './config.js';
3
3
  import { getNetwork } from './wallet.js';
4
4
  const DEFAULT_TIMEOUT = 30000;
5
5
  const providerCache = new Map();
@@ -1,5 +1,5 @@
1
1
  import { Address } from '@btc-vision/transaction';
2
- import { NetworkName, CLIMldsaLevel, RegistryPluginType } from '../types/index.js';
2
+ import { CLIMldsaLevel, NetworkName, RegistryPluginType } from '../types/index.js';
3
3
  import { IPluginPermissions } from '@btc-vision/plugin-sdk';
4
4
  import { IPackageRegistry } from '../types/PackageRegistry.js';
5
5
  export interface ScopeInfo {
@@ -1,7 +1,7 @@
1
1
  import { Network } from '@btc-vision/bitcoin';
2
2
  import { MLDSASecurityLevel } from '@btc-vision/bip32';
3
- import { Wallet, EcKeyPair } from '@btc-vision/transaction';
4
- import { CLICredentials, NetworkName, CLIMldsaLevel } from '../types/index.js';
3
+ import { EcKeyPair, Wallet } from '@btc-vision/transaction';
4
+ import { CLICredentials, CLIMldsaLevel, NetworkName } from '../types/index.js';
5
5
  export declare function getNetwork(networkName: NetworkName): Network;
6
6
  export declare function getMLDSASecurityLevel(level: CLIMldsaLevel): MLDSASecurityLevel;
7
7
  export declare class CLIWallet {
@@ -9,8 +9,6 @@ export declare class CLIWallet {
9
9
  private readonly network;
10
10
  private readonly mldsaLevel;
11
11
  private constructor();
12
- static fromCredentials(credentials: CLICredentials): CLIWallet;
13
- static load(): CLIWallet;
14
12
  get p2trAddress(): string;
15
13
  get underlyingWallet(): Wallet;
16
14
  get keypair(): EcKeyPair;
@@ -19,9 +17,11 @@ export declare class CLIWallet {
19
17
  get mldsaPublicKeyHash(): string;
20
18
  get securityLevel(): CLIMldsaLevel;
21
19
  get walletNetwork(): Network;
20
+ static fromCredentials(credentials: CLICredentials): CLIWallet;
21
+ static load(): CLIWallet;
22
+ static verifyMLDSA(data: Buffer, signature: Buffer, publicKey: Buffer, level: CLIMldsaLevel): boolean;
22
23
  signMLDSA(data: Buffer): Buffer;
23
24
  verifyMLDSA(data: Buffer, signature: Buffer): boolean;
24
- static verifyMLDSA(data: Buffer, signature: Buffer, publicKey: Buffer, level: CLIMldsaLevel): boolean;
25
25
  }
26
26
  export declare function generateMLDSAKeypair(level: CLIMldsaLevel): {
27
27
  privateKey: Buffer;
@@ -1,8 +1,8 @@
1
1
  import { networks } from '@btc-vision/bitcoin';
2
2
  import { MLDSASecurityLevel, QuantumBIP32Factory } from '@btc-vision/bip32';
3
- import { Mnemonic, Wallet, EcKeyPair, MessageSigner } from '@btc-vision/transaction';
3
+ import { EcKeyPair, MessageSigner, Mnemonic, Wallet } from '@btc-vision/transaction';
4
4
  import * as crypto from 'crypto';
5
- import { loadCredentials, canSign } from './credentials.js';
5
+ import { canSign, loadCredentials } from './credentials.js';
6
6
  export function getNetwork(networkName) {
7
7
  switch (networkName) {
8
8
  case 'mainnet':
@@ -32,30 +32,6 @@ export class CLIWallet {
32
32
  this.network = network;
33
33
  this.mldsaLevel = mldsaLevel;
34
34
  }
35
- static fromCredentials(credentials) {
36
- const network = getNetwork(credentials.network);
37
- const securityLevel = getMLDSASecurityLevel(credentials.mldsaLevel);
38
- if (credentials.mnemonic) {
39
- const mnemonic = new Mnemonic(credentials.mnemonic, '', network, securityLevel);
40
- const wallet = mnemonic.deriveUnisat();
41
- return new CLIWallet(wallet, network, credentials.mldsaLevel);
42
- }
43
- if (credentials.wif && credentials.mldsaPrivateKey) {
44
- const wallet = Wallet.fromWif(credentials.wif, credentials.mldsaPrivateKey, network, securityLevel);
45
- return new CLIWallet(wallet, network, credentials.mldsaLevel);
46
- }
47
- throw new Error('Invalid credentials: requires either mnemonic or both WIF and MLDSA key');
48
- }
49
- static load() {
50
- const credentials = loadCredentials();
51
- if (!credentials) {
52
- throw new Error('No credentials found. Run `opnet login` to configure your wallet.');
53
- }
54
- if (!canSign(credentials)) {
55
- throw new Error('Credentials incomplete for signing. Run `opnet login` to reconfigure.');
56
- }
57
- return CLIWallet.fromCredentials(credentials);
58
- }
59
35
  get p2trAddress() {
60
36
  return this.wallet.p2tr;
61
37
  }
@@ -81,12 +57,29 @@ export class CLIWallet {
81
57
  get walletNetwork() {
82
58
  return this.network;
83
59
  }
84
- signMLDSA(data) {
85
- const result = MessageSigner.signMLDSAMessage(this.wallet.mldsaKeypair, data);
86
- return Buffer.from(result.signature);
60
+ static fromCredentials(credentials) {
61
+ const network = getNetwork(credentials.network);
62
+ const securityLevel = getMLDSASecurityLevel(credentials.mldsaLevel);
63
+ if (credentials.mnemonic) {
64
+ const mnemonic = new Mnemonic(credentials.mnemonic, '', network, securityLevel);
65
+ const wallet = mnemonic.deriveUnisat();
66
+ return new CLIWallet(wallet, network, credentials.mldsaLevel);
67
+ }
68
+ if (credentials.wif && credentials.mldsaPrivateKey) {
69
+ const wallet = Wallet.fromWif(credentials.wif, credentials.mldsaPrivateKey, network, securityLevel);
70
+ return new CLIWallet(wallet, network, credentials.mldsaLevel);
71
+ }
72
+ throw new Error('Invalid credentials: requires either mnemonic or both WIF and MLDSA key');
87
73
  }
88
- verifyMLDSA(data, signature) {
89
- return MessageSigner.verifyMLDSASignature(this.wallet.mldsaKeypair, data, signature);
74
+ static load() {
75
+ const credentials = loadCredentials();
76
+ if (!credentials) {
77
+ throw new Error('No credentials found. Run `opnet login` to configure your wallet.');
78
+ }
79
+ if (!canSign(credentials)) {
80
+ throw new Error('Credentials incomplete for signing. Run `opnet login` to reconfigure.');
81
+ }
82
+ return CLIWallet.fromCredentials(credentials);
90
83
  }
91
84
  static verifyMLDSA(data, signature, publicKey, level) {
92
85
  const securityLevel = getMLDSASecurityLevel(level);
@@ -94,6 +87,13 @@ export class CLIWallet {
94
87
  const keypair = QuantumBIP32Factory.fromPublicKey(publicKey, dummyChainCode, networks.bitcoin, securityLevel);
95
88
  return keypair.verify(data, signature);
96
89
  }
90
+ signMLDSA(data) {
91
+ const result = MessageSigner.signMLDSAMessage(this.wallet.mldsaKeypair, data);
92
+ return Buffer.from(result.signature);
93
+ }
94
+ verifyMLDSA(data, signature) {
95
+ return MessageSigner.verifyMLDSASignature(this.wallet.mldsaKeypair, data, signature);
96
+ }
97
97
  }
98
98
  export function generateMLDSAKeypair(level) {
99
99
  const securityLevel = getMLDSASecurityLevel(level);
@@ -1,5 +1,5 @@
1
1
  import { Address } from '@btc-vision/transaction';
2
- import { CallResult, OPNetEvent, IOP_NETContract } from 'opnet';
2
+ import { CallResult, IOP_NETContract, OPNetEvent } from 'opnet';
3
3
  export type TreasuryAddressChangedEvent = {
4
4
  readonly previousAddressHash: bigint;
5
5
  readonly newAddressHash: bigint;
@@ -1,4 +1,4 @@
1
- import { MLDSALevel, MLDSA_PUBLIC_KEY_SIZES, MLDSA_SIGNATURE_SIZES } from '@btc-vision/plugin-sdk';
1
+ import { MLDSA_PUBLIC_KEY_SIZES, MLDSA_SIGNATURE_SIZES, MLDSALevel } from '@btc-vision/plugin-sdk';
2
2
  export function cliLevelToMLDSALevel(level) {
3
3
  switch (level) {
4
4
  case 44:
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@btc-vision/cli",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "type": "module",
5
5
  "description": "CLI for the OPNet plugin ecosystem - scaffolding, compilation, signing, and registry interaction",
6
6
  "author": "OP_NET",
@@ -53,12 +53,12 @@
53
53
  "bytenode": "^1.5.7",
54
54
  "commander": "^14.0.0",
55
55
  "esbuild": "^0.25.5",
56
- "opnet": "^1.7.17",
56
+ "opnet": "^1.7.18",
57
57
  "ora": "^8.2.0"
58
58
  },
59
59
  "devDependencies": {
60
60
  "@eslint/js": "^9.39.1",
61
- "@types/node": "^24.3.0",
61
+ "@types/node": "^25.0.2",
62
62
  "eslint": "^9.39.1",
63
63
  "gulp": "^5.0.1",
64
64
  "gulp-cached": "^1.1.1",
@@ -164,6 +164,12 @@ export class InitCommand extends BaseCommand {
164
164
 
165
165
  // Create README.md
166
166
  this.createReadme(projectDir, config, force);
167
+
168
+ // Create ESLint config
169
+ this.createEslintConfig(projectDir, force);
170
+
171
+ // Create Prettier config
172
+ this.createPrettierConfig(projectDir, force);
167
173
  }
168
174
 
169
175
  private createPluginJson(
@@ -183,7 +189,6 @@ export class InitCommand extends BaseCommand {
183
189
  main: 'dist/index.jsc',
184
190
  target: 'bytenode',
185
191
  type: 'plugin',
186
- checksum: '',
187
192
  author: config.authorEmail
188
193
  ? { name: config.authorName, email: config.authorEmail }
189
194
  : { name: config.authorName },
@@ -277,9 +282,10 @@ export class InitCommand extends BaseCommand {
277
282
  main: 'dist/index.js',
278
283
  scripts: {
279
284
  build: 'tsc',
280
- compile: 'opnet compile',
281
- verify: 'opnet verify',
285
+ compile: 'npx opnet compile',
286
+ verify: 'npx opnet verify',
282
287
  lint: 'eslint src/',
288
+ format: 'prettier --write src/',
283
289
  },
284
290
  author: config.authorEmail
285
291
  ? `${config.authorName} <${config.authorEmail}>`
@@ -287,8 +293,12 @@ export class InitCommand extends BaseCommand {
287
293
  license: 'Apache-2.0',
288
294
  dependencies: { '@btc-vision/plugin-sdk': '^1.0.0' },
289
295
  devDependencies: {
290
- '@types/node': '^22.0.0',
296
+ '@eslint/js': '^9.39.0',
297
+ '@types/node': '^25.0.0',
298
+ eslint: '^9.39.0',
299
+ prettier: '^3.6.0',
291
300
  typescript: '^5.8.0',
301
+ 'typescript-eslint': '^8.39.0',
292
302
  '@btc-vision/cli': '^1.0.0',
293
303
  },
294
304
  };
@@ -454,6 +464,78 @@ Apache-2.0
454
464
  this.logger.success(' Created README.md');
455
465
  }
456
466
 
467
+ private createEslintConfig(projectDir: string, force?: boolean): void {
468
+ const eslintPath = path.join(projectDir, 'eslint.config.js');
469
+ if (fs.existsSync(eslintPath) && !force) return;
470
+
471
+ const content = `// @ts-check
472
+
473
+ import eslint from '@eslint/js';
474
+ import tseslint from 'typescript-eslint';
475
+
476
+ export default tseslint.config(
477
+ eslint.configs.recommended,
478
+ ...tseslint.configs.strictTypeChecked,
479
+ {
480
+ languageOptions: {
481
+ parserOptions: {
482
+ projectService: true,
483
+ tsconfigDirName: import.meta.dirname,
484
+ },
485
+ },
486
+ rules: {
487
+ 'no-undef': 'off',
488
+ '@typescript-eslint/no-unused-vars': 'off',
489
+ 'no-empty': 'off',
490
+ '@typescript-eslint/restrict-template-expressions': 'off',
491
+ '@typescript-eslint/only-throw-error': 'off',
492
+ '@typescript-eslint/no-unnecessary-condition': 'off',
493
+ '@typescript-eslint/unbound-method': 'warn',
494
+ '@typescript-eslint/no-confusing-void-expression': 'off',
495
+ '@typescript-eslint/no-extraneous-class': 'off',
496
+ 'no-async-promise-executor': 'off',
497
+ '@typescript-eslint/no-misused-promises': 'off',
498
+ '@typescript-eslint/no-unnecessary-type-parameters': 'off',
499
+ '@typescript-eslint/no-duplicate-enum-values': 'off',
500
+ 'prefer-spread': 'off',
501
+ '@typescript-eslint/no-empty-object-type': 'off',
502
+ '@typescript-eslint/no-base-to-string': 'off',
503
+ '@typescript-eslint/no-dynamic-delete': 'off',
504
+ '@typescript-eslint/no-redundant-type-constituents': 'off',
505
+ },
506
+ },
507
+ {
508
+ files: ['**/*.js'],
509
+ ...tseslint.configs.disableTypeChecked,
510
+ },
511
+ );
512
+ `;
513
+
514
+ fs.writeFileSync(eslintPath, content);
515
+ this.logger.success(' Created eslint.config.js');
516
+ }
517
+
518
+ private createPrettierConfig(projectDir: string, force?: boolean): void {
519
+ const prettierPath = path.join(projectDir, '.prettierrc.json');
520
+ if (fs.existsSync(prettierPath) && !force) return;
521
+
522
+ const config = {
523
+ printWidth: 100,
524
+ trailingComma: 'all',
525
+ tabWidth: 4,
526
+ semi: true,
527
+ singleQuote: true,
528
+ quoteProps: 'as-needed',
529
+ bracketSpacing: true,
530
+ bracketSameLine: true,
531
+ arrowParens: 'always',
532
+ singleAttributePerLine: true,
533
+ };
534
+
535
+ fs.writeFileSync(prettierPath, JSON.stringify(config, null, 4));
536
+ this.logger.success(' Created .prettierrc.json');
537
+ }
538
+
457
539
  private toPascalCase(str: string): string {
458
540
  return str
459
541
  .split(/[-_]/)
package/src/lib/config.ts CHANGED
@@ -10,7 +10,6 @@ import * as fs from 'fs';
10
10
  import * as path from 'path';
11
11
  import * as os from 'os';
12
12
  import { CLIConfig, CLIMldsaLevel, NetworkName } from '../types/index.js';
13
- import { Address } from '@btc-vision/transaction';
14
13
 
15
14
  /** Default configuration directory */
16
15
  const CONFIG_DIR = path.join(os.homedir(), '.opnet');
@@ -38,9 +37,9 @@ const DEFAULT_CONFIG: CLIConfig = {
38
37
  ipfsPinningApiKey: '',
39
38
  ipfsPinningAuthHeader: 'Authorization',
40
39
  registryAddresses: {
41
- mainnet: Address.dead().toHex(), // TODO: Set once deployed
42
- testnet: Address.dead().toHex(), // TODO: Set once deployed
43
- regtest: Address.dead().toHex(), // TODO: Set once deployed
40
+ mainnet: '', // TODO: Set once deployed
41
+ testnet: '', // TODO: Set once deployed
42
+ regtest: '', // TODO: Set once deployed
44
43
  },
45
44
  defaultMldsaLevel: 44,
46
45
  indexerUrl: 'https://indexer.opnet.org',
package/src/lib/ipfs.ts CHANGED
@@ -243,7 +243,6 @@ export async function fetchFromIPFS(cid: string): Promise<FetchResult> {
243
243
  } catch (error) {
244
244
  lastError = error instanceof Error ? error : new Error(String(error));
245
245
  // Try next gateway
246
- continue;
247
246
  }
248
247
  }
249
248
 
@@ -89,6 +89,10 @@ export function validatePermissions(permissions: IPluginPermissions): string[] {
89
89
  /**
90
90
  * Load and validate a plugin manifest from file
91
91
  *
92
+ * Note: This performs basic validation suitable for source manifests.
93
+ * The SDK's strict validation requires fields like 'checksum' which
94
+ * are only computed during compilation, not in source plugin.json.
95
+ *
92
96
  * @param manifestPath - Path to plugin.json
93
97
  * @returns The loaded manifest or throws with validation errors
94
98
  */
@@ -107,13 +111,33 @@ export function loadManifest(manifestPath: string): IPluginMetadata {
107
111
  throw new Error(`Failed to parse manifest: ${e instanceof Error ? e.message : String(e)}`);
108
112
  }
109
113
 
110
- const result = validateManifest(manifest);
111
- if (!result.valid) {
112
- const errorList = result.errors.map((e) => ` - ${e.path}: ${e.message}`).join('\n');
113
- throw new Error(`Invalid manifest:\n${errorList}`);
114
+ // Basic validation for source manifests (checksum is computed during compilation)
115
+ const errors: string[] = [];
116
+ if (!manifest.name || typeof manifest.name !== 'string') {
117
+ errors.push('name is required');
118
+ }
119
+ if (!manifest.version || typeof manifest.version !== 'string') {
120
+ errors.push('version is required');
121
+ }
122
+ if (!manifest.author || typeof manifest.author !== 'object') {
123
+ errors.push('author is required');
124
+ }
125
+ if (!manifest.pluginType || !['standalone', 'library'].includes(manifest.pluginType)) {
126
+ errors.push('pluginType must be "standalone" or "library"');
127
+ }
128
+
129
+ if (errors.length > 0) {
130
+ throw new Error(`Invalid manifest:\n${errors.map((e) => ` - ${e}`).join('\n')}`);
131
+ }
132
+
133
+ // Set default checksum for compilation (will be computed)
134
+ // Cast to mutable to allow setting checksum
135
+ const result = manifest as { -readonly [K in keyof IPluginMetadata]: IPluginMetadata[K] };
136
+ if (!result.checksum) {
137
+ result.checksum = '';
114
138
  }
115
139
 
116
- return manifest as IPluginMetadata;
140
+ return result as IPluginMetadata;
117
141
  }
118
142
 
119
143
  /**
@@ -139,7 +163,7 @@ export function createManifest(options: {
139
163
  email?: string;
140
164
  description?: string;
141
165
  pluginType: 'standalone' | 'library';
142
- }): IPluginMetadata {
166
+ }): Omit<IPluginMetadata, 'checksum'> {
143
167
  return {
144
168
  name: options.name,
145
169
  version: '1.0.0',
@@ -147,7 +171,6 @@ export function createManifest(options: {
147
171
  main: 'dist/index.jsc',
148
172
  target: 'bytenode',
149
173
  type: 'plugin',
150
- checksum: '',
151
174
  author: {
152
175
  name: options.author,
153
176
  email: options.email,