@super-protocol/sp-cli 0.0.9 → 0.0.10-beta

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 (90) hide show
  1. package/README.md +191 -163
  2. package/dist/commands/account/base.d.ts +3 -4
  3. package/dist/commands/account/base.js +8 -6
  4. package/dist/commands/account/forget.js +3 -3
  5. package/dist/commands/account/get-sppi.js +7 -11
  6. package/dist/commands/account/info.d.ts +1 -13
  7. package/dist/commands/account/info.js +20 -40
  8. package/dist/commands/account/list.js +6 -6
  9. package/dist/commands/account/login.d.ts +6 -5
  10. package/dist/commands/account/login.js +96 -145
  11. package/dist/commands/account/switch.js +2 -2
  12. package/dist/commands/assets/base.d.ts +39 -0
  13. package/dist/commands/assets/base.js +217 -0
  14. package/dist/commands/assets/create.d.ts +41 -0
  15. package/dist/commands/assets/create.js +277 -0
  16. package/dist/commands/assets/delete.d.ts +14 -0
  17. package/dist/commands/assets/delete.js +69 -0
  18. package/dist/commands/assets/get.d.ts +14 -0
  19. package/dist/commands/assets/get.js +79 -0
  20. package/dist/commands/assets/list.d.ts +7 -0
  21. package/dist/commands/assets/list.js +33 -0
  22. package/dist/commands/assets/update.d.ts +44 -0
  23. package/dist/commands/assets/update.js +321 -0
  24. package/dist/commands/base.d.ts +1 -0
  25. package/dist/commands/base.js +5 -0
  26. package/dist/config/config.schema.d.ts +2 -11
  27. package/dist/config/config.schema.js +2 -7
  28. package/dist/constants.d.ts +3 -3
  29. package/dist/constants.js +3 -3
  30. package/dist/errors.d.ts +0 -2
  31. package/dist/errors.js +0 -2
  32. package/dist/hooks/prerun/auth.js +3 -8
  33. package/dist/interfaces/config-manager.interface.d.ts +3 -1
  34. package/dist/lib/container.d.ts +4 -12
  35. package/dist/lib/container.js +28 -113
  36. package/dist/lib/swarm-client/fetch-api.d.ts +7 -0
  37. package/dist/lib/swarm-client/fetch-api.js +41 -0
  38. package/dist/lib/swarm-client/fetch-timeout.client.d.ts +1 -0
  39. package/dist/lib/swarm-client/fetch-timeout.client.js +32 -0
  40. package/dist/lib/swarm-client/index.d.ts +6 -0
  41. package/dist/lib/swarm-client/index.js +31 -0
  42. package/dist/lib/swarm-client/middlewares/authorization.middleware.d.ts +2 -0
  43. package/dist/lib/swarm-client/middlewares/authorization.middleware.js +12 -0
  44. package/dist/lib/swarm-client/middlewares/index.d.ts +6 -0
  45. package/dist/lib/swarm-client/middlewares/index.js +5 -0
  46. package/dist/lib/swarm-client/middlewares/logger.middleware.d.ts +2 -0
  47. package/dist/lib/swarm-client/middlewares/logger.middleware.js +30 -0
  48. package/dist/lib/swarm-client/middlewares/request-id.middleware.d.ts +2 -0
  49. package/dist/lib/swarm-client/middlewares/request-id.middleware.js +13 -0
  50. package/dist/lib/swarm-client/types.d.ts +23 -0
  51. package/dist/lib/swarm-client/types.js +1 -0
  52. package/dist/managers/account-manager.d.ts +1 -0
  53. package/dist/managers/account-manager.js +13 -18
  54. package/dist/managers/config-file-manager.d.ts +22 -9
  55. package/dist/managers/config-file-manager.js +247 -122
  56. package/dist/managers/config-manager.d.ts +6 -6
  57. package/dist/managers/config-manager.js +8 -8
  58. package/dist/services/account.service.d.ts +42 -0
  59. package/dist/services/account.service.js +140 -0
  60. package/dist/services/asset.service.d.ts +35 -0
  61. package/dist/services/asset.service.js +120 -0
  62. package/dist/services/auth.service.d.ts +4 -6
  63. package/dist/services/auth.service.js +108 -118
  64. package/dist/utils/helper.js +2 -2
  65. package/oclif.manifest.json +462 -212
  66. package/package.json +7 -8
  67. package/dist/commands/files/download.d.ts +0 -15
  68. package/dist/commands/files/download.js +0 -63
  69. package/dist/commands/files/upload.d.ts +0 -18
  70. package/dist/commands/files/upload.js +0 -83
  71. package/dist/commands/storage/base.d.ts +0 -13
  72. package/dist/commands/storage/base.js +0 -125
  73. package/dist/commands/storage/create.d.ts +0 -11
  74. package/dist/commands/storage/create.js +0 -53
  75. package/dist/commands/storage/select.d.ts +0 -9
  76. package/dist/commands/storage/select.js +0 -38
  77. package/dist/commands/storage/show.d.ts +0 -17
  78. package/dist/commands/storage/show.js +0 -34
  79. package/dist/commands/storage/update.d.ts +0 -14
  80. package/dist/commands/storage/update.js +0 -204
  81. package/dist/commands/workflows/extend-lease.d.ts +0 -17
  82. package/dist/commands/workflows/extend-lease.js +0 -102
  83. package/dist/hooks/finally/shutdown-blockchain.d.ts +0 -3
  84. package/dist/hooks/finally/shutdown-blockchain.js +0 -8
  85. package/dist/middlewares/auth-middleware.d.ts +0 -9
  86. package/dist/middlewares/auth-middleware.js +0 -91
  87. package/dist/middlewares/cookies-middleware.d.ts +0 -8
  88. package/dist/middlewares/cookies-middleware.js +0 -80
  89. package/dist/services/storage.service.d.ts +0 -73
  90. package/dist/services/storage.service.js +0 -378
@@ -1,18 +1,14 @@
1
- import { formatEther } from 'viem';
1
+ //import { formatEther } from 'viem';
2
2
  import { BaseAccountCommand } from './base.js';
3
3
  export default class AccountGetSppi extends BaseAccountCommand {
4
4
  static description = 'Get SPPI (Super Protocol incentive tokens) ';
5
5
  static examples = ['<%= config.bin %> <%= command.id %>'];
6
6
  async run() {
7
- await this.initAccountContext();
8
- const { providerClient } = this.container;
9
- const { data, error } = await providerClient.POST('/api/faucet/request-tokens');
10
- if (error) {
11
- this.error(`Getting SPPI failed. Reason: ${error.message}`);
12
- }
13
- const tokens = formatEther(BigInt(data.amount || '0'));
14
- this.log('Tokens replenished');
15
- this.log(`Current balance: ${tokens} SPPI`);
16
- return { tokens };
7
+ this.error('Not implemented yet');
8
+ // await this.initAccountContext();
9
+ // const tokens = formatEther(BigInt('0'));
10
+ // this.log('Tokens replenished');
11
+ // this.log(`Current balance: ${tokens} SPPI`);
12
+ // return { tokens };
17
13
  }
18
14
  }
@@ -6,20 +6,8 @@ export declare class InfoCommand extends BaseAccountCommand<typeof InfoCommand>
6
6
  };
7
7
  init(): Promise<void>;
8
8
  run(): Promise<{
9
- address: string;
9
+ address: string | undefined;
10
10
  balance: string;
11
- storage: {
12
- bucket: string;
13
- createdAt: string;
14
- id: string;
15
- isCentralized: boolean;
16
- prefix: string;
17
- s3Credentials?: import("@super-protocol/provider-client").components["schemas"]["S3CredentialsResponseDto"];
18
- storageType: import("@super-protocol/provider-client").components["schemas"]["StorageType"];
19
- storjCredentials?: import("@super-protocol/provider-client").components["schemas"]["StorJCredentialsResponseDto"];
20
- updatedAt: string;
21
- userId: string;
22
- };
23
11
  wallet: string;
24
12
  }>;
25
13
  }
@@ -1,7 +1,6 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { formatEther } from 'viem';
3
- import { PROVIDER_URL } from '../../constants.js';
4
- import { StorageService } from '../../services/storage.service.js';
3
+ import { SWARM_URL } from '../../constants.js';
5
4
  import { BaseAccountCommand } from './base.js';
6
5
  export class InfoCommand extends BaseAccountCommand {
7
6
  static description = 'Information about current authorized user';
@@ -17,54 +16,36 @@ export class InfoCommand extends BaseAccountCommand {
17
16
  }
18
17
  async run() {
19
18
  const { flags } = await this.parse(InfoCommand);
20
- const { providerClient } = this.container;
21
- const storageService = new StorageService(providerClient, this.logger);
22
- const { data, error } = await providerClient.GET('/api/auth/me');
23
- if (error) {
24
- this.error(error.message);
19
+ const { accountManager, configFileManager } = this.container;
20
+ const current = configFileManager.getCurrentConfigFile();
21
+ let address;
22
+ try {
23
+ address = accountManager.getAddress();
25
24
  }
26
- if (!data) {
27
- this.error('Failed to retrieve account information');
28
- }
29
- const { data: wallet } = await providerClient.GET('/api/users/me/wallet');
30
- if (!wallet) {
31
- this.error('Failed to retrieve wallet information');
32
- }
33
- const storage = await storageService.getCurrentStorage();
34
- const balance = formatEther(BigInt(wallet?.teeBalance || '0'));
35
- this.log(`Login: ${data.address}`);
36
- this.log(`Super Wallet: ${data.internalAddress}`);
25
+ catch { }
26
+ const walletAddress = 'N/A';
27
+ const balance = formatEther(0n);
28
+ this.log(`Login: ${address || 'N/A'}`);
29
+ this.log(`Super Wallet: ${walletAddress}`);
37
30
  this.log(`Balance: ${balance} SPPI`);
38
- if (storage.isCentralized) {
39
- this.log('Storage: Super cloud');
40
- }
41
- else {
42
- this.log('Active storage:');
43
- this.log(` bucket: ${storage.bucket}`);
44
- if (storage.prefix) {
45
- this.log(` prefix: ${storage.prefix}`);
46
- }
47
- }
48
31
  if (flags.detail) {
49
- const { configFileManager } = this.container;
50
- const current = configFileManager.getCurrentConfigFile();
51
32
  const configDir = configFileManager.getConfigDir();
52
33
  this.log('\n-----------------(DETAILS)-----------------------');
53
34
  if (current) {
54
- const configs = configFileManager.getConfigsWithNames();
35
+ const configs = await configFileManager.getConfigsWithNames();
55
36
  const currentConfig = configs.find((c) => c.file === current);
56
37
  const displayName = currentConfig?.name || current;
57
- const configData = configFileManager.getConfigData(current);
38
+ const configData = await configFileManager.getConfigData(current);
58
39
  this.log(`Current account: ${displayName}`);
59
40
  this.log(`Configuration file: ${current}`);
60
41
  this.log(`Configuration directory: ${configDir}`);
61
42
  if (configData) {
62
43
  this.log('\nAccount details:');
63
- if (configData.providerUrl) {
64
- this.log(` Provider URL: ${configData.providerUrl}`);
44
+ if (configData.swarmUrl) {
45
+ this.log(` Swarm URL: ${configData.swarmUrl}`);
65
46
  }
66
47
  else {
67
- this.log(` Provider URL: ${PROVIDER_URL} (using default)`);
48
+ this.log(` Swarm URL: ${SWARM_URL} (using default)`);
68
49
  }
69
50
  if (configData.auth?.accessKey) {
70
51
  this.log(' Authorization: Configured');
@@ -72,8 +53,8 @@ export class InfoCommand extends BaseAccountCommand {
72
53
  else {
73
54
  this.log(' Authorization: Unauthorized');
74
55
  }
75
- if (configData.account?.address) {
76
- this.log(` Account: ${configData.account.address}`);
56
+ if (address) {
57
+ this.log(` Account: ${address}`);
77
58
  }
78
59
  else {
79
60
  this.log(' Account: Not configured');
@@ -87,10 +68,9 @@ export class InfoCommand extends BaseAccountCommand {
87
68
  }
88
69
  }
89
70
  return {
90
- address: data.address,
71
+ address,
91
72
  balance,
92
- storage,
93
- wallet: data.internalAddress,
73
+ wallet: walletAddress,
94
74
  };
95
75
  }
96
76
  }
@@ -1,5 +1,5 @@
1
1
  import { printTable } from 'console-table-printer';
2
- import { PROVIDER_URL } from '../../constants.js';
2
+ import { SWARM_URL } from '../../constants.js';
3
3
  import { BaseCommand } from '../base.js';
4
4
  export default class AccountListCommand extends BaseCommand {
5
5
  static authenticate = false;
@@ -13,23 +13,23 @@ export default class AccountListCommand extends BaseCommand {
13
13
  await this.container.initConfigManager().build();
14
14
  }
15
15
  async run() {
16
- const configs = this.configFileManager.getConfigsWithNames();
16
+ const configs = await this.configFileManager.getConfigsWithNames();
17
17
  const current = this.configFileManager.getCurrentConfigFile();
18
18
  if (configs.length === 0) {
19
19
  this.log('No Accounts found');
20
- this.log('Create a new account with: sp account login --privateKey "<PRIVATE_KEY>" --url "<PROVIDER_URL>" --name "Account Name"');
20
+ this.log('Create a new account with: sp account login --privateKey "<PRIVATE_KEY>" --url "<SWARM_URL>" --name "Account Name"');
21
21
  return;
22
22
  }
23
23
  const rows = [];
24
24
  for (const config of configs) {
25
25
  const marker = config.file === current ? '* ' : ' ';
26
- const configData = this.configFileManager.getConfigData(config.file);
26
+ const configData = await this.configFileManager.getConfigData(config.file);
27
27
  const row = {
28
28
  account: configData?.account?.address ?? '[not found in config file]',
29
29
  current: marker,
30
30
  file: config.file,
31
31
  name: config.name,
32
- url: configData?.providerUrl ?? PROVIDER_URL,
32
+ url: configData?.swarmUrl ?? SWARM_URL,
33
33
  };
34
34
  rows.push(row);
35
35
  }
@@ -38,7 +38,7 @@ export default class AccountListCommand extends BaseCommand {
38
38
  { name: 'current', title: 'Current' },
39
39
  { name: 'name', title: 'Config name' },
40
40
  { name: 'account', title: 'Account' },
41
- { name: 'url', title: 'Provider Url' },
41
+ { name: 'url', title: 'Swarm Url' },
42
42
  { name: 'file', title: 'Configuration File' },
43
43
  ],
44
44
  title: 'Available Accounts',
@@ -2,6 +2,7 @@ import { BaseAccountCommand } from './base.js';
2
2
  export default class AccountLoginCommand extends BaseAccountCommand<typeof AccountLoginCommand> {
3
3
  static aliases: string[];
4
4
  static authenticate: boolean;
5
+ static summary: string;
5
6
  static description: string;
6
7
  static examples: string[];
7
8
  static flags: {
@@ -9,9 +10,10 @@ export default class AccountLoginCommand extends BaseAccountCommand<typeof Accou
9
10
  privateKey: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  path: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
13
+ authUrl: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
12
14
  yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
13
15
  };
14
- private configFileManager;
16
+ private accountService;
15
17
  private shouldSkipLogin;
16
18
  private inputPrompt;
17
19
  private checkStorage;
@@ -26,14 +28,13 @@ export default class AccountLoginCommand extends BaseAccountCommand<typeof Accou
26
28
  private askName;
27
29
  private confirmPrompt;
28
30
  private createAccountFromKey;
29
- private createConfigIfMissing;
30
31
  private updateConfigDisplayName;
31
- private findConfigsByPrivateKey;
32
+ private setCurrentAndAuth;
33
+ private applySelectionWithDisplayName;
34
+ private selectMatchedConfig;
32
35
  private getPrivateKeyOrGenerate;
33
36
  private resolveByName;
34
- private assertConfigHasAccount;
35
37
  private loadConfigFromPath;
36
- private resolveConfigFileName;
37
38
  private resolveByPath;
38
39
  private resolveByPrivateKey;
39
40
  private resolveConfiguration;
@@ -1,18 +1,15 @@
1
- import { existsSync } from 'node:fs';
2
- import path from 'node:path';
3
1
  import { Flags } from '@oclif/core';
4
- import { Value } from 'typebox/value';
5
- import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
6
- import { cliConfigSchema } from '../../config/config.schema.js';
7
- import { PROVIDER_URL } from '../../constants.js';
2
+ import { generatePrivateKey } from 'viem/accounts';
3
+ import { SWARM_URL } from '../../constants.js';
4
+ import { AccountService } from '../../services/account.service.js';
8
5
  import { AuthService } from '../../services/auth.service.js';
9
- import { StorageService } from '../../services/storage.service.js';
10
- import { getConfigName, getConfigNameFromCredentials, preparePath, readJsonFile, } from '../../utils/helper.js';
6
+ import { getConfigName, preparePath } from '../../utils/helper.js';
11
7
  import { promptService } from '../../utils/prompt.service.js';
12
8
  import { BaseAccountCommand } from './base.js';
13
9
  export default class AccountLoginCommand extends BaseAccountCommand {
14
10
  static aliases = ['login'];
15
11
  static authenticate = false;
12
+ static summary = 'Login and account creation for SuperProtocol';
16
13
  static description = 'Login to SuperProtocol (sign up or sign in).';
17
14
  static examples = [
18
15
  `<%= config.bin %> <%= command.id %>
@@ -29,7 +26,11 @@ export default class AccountLoginCommand extends BaseAccountCommand {
29
26
  description: 'Path to account(configuration) file to import',
30
27
  }),
31
28
  url: Flags.string({
32
- description: 'Provider base URL',
29
+ description: 'Swarm Api URL',
30
+ hidden: true,
31
+ }),
32
+ authUrl: Flags.string({
33
+ description: 'Auth URL',
33
34
  hidden: true,
34
35
  }),
35
36
  yes: Flags.boolean({
@@ -38,49 +39,40 @@ export default class AccountLoginCommand extends BaseAccountCommand {
38
39
  description: 'Skip questions (generate keys when needed).',
39
40
  }),
40
41
  };
41
- configFileManager;
42
+ accountService;
42
43
  shouldSkipLogin = false;
43
44
  async inputPrompt(options) {
44
45
  return promptService.text(options);
45
46
  }
46
47
  async checkStorage() {
47
- await this.container
48
- .initProviderClient({ enableAuth: true, enableCookies: true, rebuild: true })
49
- .build();
50
- const { providerClient } = this.container;
51
- const storageService = new StorageService(providerClient, this.logger);
52
- if (!(await storageService.hasStorage())) {
53
- this.logger.info('Requesting default storage');
54
- const { id, isCentralized, storageType } = await storageService.getCentralizedStorage();
55
- this.logger.info({ id, isCentralized, storageType }, 'Requested new storage');
56
- await storageService.saveStorage(id);
57
- }
48
+ this.logger.info('Storage initialization skipped (mock)');
58
49
  }
59
50
  async init() {
60
51
  await super.init();
61
52
  await this.container.initConfigFileManager().build();
62
- this.configFileManager = this.container.configFileManager;
53
+ this.accountService = new AccountService(this.container.configFileManager);
63
54
  await this.resolveConfiguration();
64
55
  if (this.shouldSkipLogin) {
65
56
  return;
66
57
  }
67
58
  await this.initAccountContext({
68
59
  assumeYes: this.flags.yes,
69
- enableAuth: false,
70
- enableCookies: true,
71
60
  });
72
61
  }
73
62
  async run() {
74
63
  if (this.shouldSkipLogin) {
75
64
  return { account: undefined, success: false };
76
65
  }
77
- const { accountManager, configManager, providerClient } = this.container;
78
- const authService = new AuthService(accountManager, configManager, providerClient, this.logger);
66
+ const { accountManager, configManager } = this.container;
67
+ const authService = new AuthService(accountManager, configManager, this.logger);
79
68
  try {
80
69
  await authService.auth({ force: true });
81
70
  }
82
71
  catch (error) {
83
- this.error(error, { exit: 1 });
72
+ if (error instanceof Error) {
73
+ this.error(error, { exit: 1 });
74
+ }
75
+ this.error(String(error), { exit: 1 });
84
76
  }
85
77
  try {
86
78
  await this.checkStorage();
@@ -106,52 +98,48 @@ export default class AccountLoginCommand extends BaseAccountCommand {
106
98
  }
107
99
  createAccountFromKey(privateKey) {
108
100
  try {
109
- const account = privateKeyToAccount(privateKey);
110
- return {
111
- address: account.address,
112
- privateKey: privateKey,
113
- };
101
+ return this.accountService.createAccountFromKey(privateKey);
114
102
  }
115
103
  catch (error) {
116
- this.logger.error({ err: error }, 'Invalid private key provided.');
117
- this.error('Invalid private key provided.');
104
+ const message = error instanceof Error && error.message ? error.message : 'Invalid private key provided.';
105
+ this.logger.error({ err: error }, message);
106
+ this.error(message);
118
107
  }
119
108
  }
120
- async createConfigIfMissing(configFile, displayName, providerUrl, account) {
121
- const configPath = path.join(this.configFileManager.getConfigDir(), configFile);
122
- if (existsSync(configPath)) {
123
- await this.configFileManager.updateConfigName(configFile, displayName);
124
- return;
125
- }
126
- await this.configFileManager.createConfig(configFile, displayName, providerUrl, account);
127
- this.log(`Created account: ${displayName}`);
128
- }
129
109
  async updateConfigDisplayName(configFile, displayName) {
130
- if (!displayName) {
131
- return;
110
+ const result = await this.accountService.updateConfigDisplayName(configFile, displayName);
111
+ if (result.status === 'skipped') {
112
+ return configFile;
132
113
  }
133
- const updated = await this.configFileManager.updateConfigName(configFile, displayName);
134
- if (updated) {
114
+ if (result.status === 'updated') {
135
115
  this.log(`Updated account name to "${displayName}"`);
136
- return;
137
116
  }
138
- this.log(`Account name: "${displayName}"`);
117
+ else {
118
+ this.log(`Account name: "${displayName}"`);
119
+ }
120
+ return result.configFile;
139
121
  }
140
- findConfigsByPrivateKey(privateKey, providerUrl, matchProviderUrl) {
141
- const normalizedKey = privateKey.trim().toLowerCase();
142
- const configs = this.configFileManager.getConfigsWithNames();
143
- const matches = [];
144
- for (const config of configs) {
145
- const configData = this.configFileManager.getConfigData(config.file);
146
- const storedKey = configData?.account?.privateKey?.toLowerCase();
147
- const storedUrl = (configData?.providerUrl ?? PROVIDER_URL).trim();
148
- const keyMatches = storedKey === normalizedKey;
149
- const urlMatches = !matchProviderUrl || storedUrl === providerUrl;
150
- if (keyMatches && urlMatches) {
151
- matches.push(config);
152
- }
122
+ async setCurrentAndAuth(configFile, authUrl) {
123
+ await this.accountService.setCurrentConfig(configFile);
124
+ await this.accountService.updateConfigAuthUrl(configFile, authUrl);
125
+ }
126
+ async applySelectionWithDisplayName(configFile, displayName, authUrl) {
127
+ const updatedFile = await this.updateConfigDisplayName(configFile, displayName);
128
+ await this.setCurrentAndAuth(updatedFile, authUrl);
129
+ return updatedFile;
130
+ }
131
+ async selectMatchedConfig(matches) {
132
+ if (matches.length === 1) {
133
+ return matches[0].file;
134
+ }
135
+ if (this.flags.yes) {
136
+ this.log(`Too many matched accounts: ${matches.map((conf) => conf.name).join(',')} Selected first: ${matches[0].name}`);
137
+ return matches[0].file;
153
138
  }
154
- return matches;
139
+ return this.selectPrompt({
140
+ message: 'Multiple accounts found for this private key. Select one:',
141
+ options: matches.map((match) => ({ label: match.name, value: match.file })),
142
+ });
155
143
  }
156
144
  async getPrivateKeyOrGenerate() {
157
145
  const generatedKey = generatePrivateKey();
@@ -166,12 +154,12 @@ export default class AccountLoginCommand extends BaseAccountCommand {
166
154
  const trimmed = value.trim();
167
155
  return trimmed || generatedKey;
168
156
  }
169
- async resolveByName(name, privateKey, providerUrl) {
170
- const configs = this.configFileManager.getConfigsWithNames();
157
+ async resolveByName(name, privateKey, swarmUrl, authUrl) {
158
+ const configs = await this.accountService.getConfigsWithNames();
171
159
  const normalized = name.toLowerCase();
172
160
  const existingByName = configs.find((config) => config.name.toLowerCase() === normalized || config.file.toLowerCase() === normalized);
173
161
  if (existingByName) {
174
- await this.configFileManager.setCurrentConfig(existingByName.file);
162
+ await this.setCurrentAndAuth(existingByName.file, authUrl);
175
163
  return;
176
164
  }
177
165
  let shouldCreate = true;
@@ -184,8 +172,11 @@ export default class AccountLoginCommand extends BaseAccountCommand {
184
172
  }
185
173
  const resolvedKey = privateKey ?? (await this.getPrivateKeyOrGenerate());
186
174
  const account = this.createAccountFromKey(resolvedKey);
187
- const configFile = getConfigNameFromCredentials(resolvedKey, providerUrl);
188
- await this.createConfigIfMissing(configFile, name, providerUrl, account);
175
+ const configFile = getConfigName(name);
176
+ const { created, configFile: createdFile } = await this.accountService.createConfigIfMissing(configFile, name, swarmUrl, account, authUrl);
177
+ if (created) {
178
+ this.log(`Created account: ${name}`);
179
+ }
189
180
  let shouldSwitch = true;
190
181
  if (!this.flags.yes) {
191
182
  shouldSwitch = await this.confirmPrompt(`Switch to account "${name}" now?`, true);
@@ -194,104 +185,57 @@ export default class AccountLoginCommand extends BaseAccountCommand {
194
185
  this.shouldSkipLogin = true;
195
186
  return;
196
187
  }
197
- await this.configFileManager.setCurrentConfig(configFile);
198
- }
199
- assertConfigHasAccount(config) {
200
- if (!config.account?.privateKey) {
201
- throw new Error('Account private key is required for import.');
202
- }
188
+ await this.setCurrentAndAuth(createdFile, authUrl);
203
189
  }
204
190
  async loadConfigFromPath(configPath) {
205
191
  try {
206
- const config = await readJsonFile({ path: configPath });
207
- if (!Value.Check(cliConfigSchema, config)) {
208
- throw new Error("Account doesn't match required schema");
209
- }
210
- this.assertConfigHasAccount(config);
211
- return config;
192
+ return await this.accountService.loadConfigFromPath(configPath);
212
193
  }
213
194
  catch (error) {
214
- this.error(`Invalid account file: ${error instanceof Error ? error.message : String(error)}`, { exit: 1 });
215
- }
216
- }
217
- resolveConfigFileName(configPath, config, name) {
218
- if (name) {
219
- return getConfigName(name);
220
- }
221
- if (config.name) {
222
- return getConfigName(config.name);
195
+ const message = error instanceof Error ? error.message : String(error);
196
+ this.error(`Invalid account file: ${message}`, { exit: 1 });
223
197
  }
224
- const baseName = path.basename(configPath, path.extname(configPath));
225
- return getConfigName(baseName);
226
198
  }
227
- async resolveByPath(rawPath, name) {
199
+ async resolveByPath(rawPath, name, authUrl) {
228
200
  const sourcePath = preparePath(rawPath);
229
201
  const config = await this.loadConfigFromPath(sourcePath);
230
- const targetFile = this.resolveConfigFileName(sourcePath, config, name);
231
- const targetPath = path.join(this.configFileManager.getConfigDir(), targetFile);
202
+ const targetFile = this.accountService.resolveConfigFileName(sourcePath, config, name);
232
203
  const desiredName = name?.trim();
233
204
  const privateKey = config.account.privateKey;
234
- const providerUrl = (config.providerUrl ?? PROVIDER_URL).trim();
235
- const matches = this.findConfigsByPrivateKey(privateKey, providerUrl, true);
205
+ const swarmUrl = (config.swarmUrl ?? SWARM_URL).trim();
206
+ const matches = await this.accountService.findConfigsByPrivateKey(privateKey, swarmUrl, true);
236
207
  if (matches.length > 0) {
237
- let selectedFile = matches[0].file;
238
- if (matches.length > 1) {
239
- if (this.flags.yes) {
240
- this.log(`Too many matched accounts: ${matches.map((conf) => conf.name).join(',')} Selected first: ${matches[0].name}`);
241
- }
242
- else {
243
- selectedFile = await this.selectPrompt({
244
- message: 'Multiple accounts found for this private key. Select one:',
245
- options: matches.map((match) => ({ label: match.name, value: match.file })),
246
- });
247
- }
248
- }
208
+ const selectedFile = await this.selectMatchedConfig(matches);
249
209
  this.log('Account already exists. Nothing to import.');
250
- await this.updateConfigDisplayName(selectedFile, desiredName);
251
- await this.configFileManager.setCurrentConfig(selectedFile);
210
+ await this.applySelectionWithDisplayName(selectedFile, desiredName, authUrl);
252
211
  return;
253
212
  }
254
- if (existsSync(targetPath)) {
255
- const existingConfig = this.configFileManager.getConfigData(targetFile);
213
+ if (await this.accountService.configExists(targetFile)) {
214
+ const existingConfig = await this.accountService.getConfigData(targetFile);
256
215
  const existingKey = existingConfig?.account?.privateKey?.trim().toLowerCase();
257
- const existingUrl = (existingConfig?.providerUrl ?? PROVIDER_URL).trim();
216
+ const existingUrl = (existingConfig?.swarmUrl ?? SWARM_URL).trim();
258
217
  const importedKey = privateKey.trim().toLowerCase();
259
- if (!existingKey || existingKey !== importedKey || existingUrl !== providerUrl) {
218
+ if (!existingKey || existingKey !== importedKey || existingUrl !== swarmUrl) {
260
219
  this.error(`Account file "${targetFile}" already exists with different credentials. Use a different name or remove the existing file before importing.`, { exit: 1 });
261
220
  }
262
221
  this.log(`Account already exists. Switching to ${name || targetFile}`);
263
- await this.configFileManager.setCurrentConfig(targetFile);
222
+ await this.setCurrentAndAuth(targetFile, authUrl);
264
223
  return;
265
224
  }
266
225
  try {
267
- const importedFile = await this.configFileManager.importConfig(sourcePath, name);
268
- await this.configFileManager.setCurrentConfig(importedFile);
226
+ const importedFile = await this.accountService.importConfig(sourcePath, name);
227
+ await this.setCurrentAndAuth(importedFile, authUrl);
269
228
  }
270
229
  catch (error) {
271
230
  this.error(error instanceof Error ? error.message : String(error), { exit: 1 });
272
231
  }
273
232
  }
274
- async resolveByPrivateKey(privateKey, providerUrl, matchProviderUrl, name) {
275
- const matches = this.findConfigsByPrivateKey(privateKey, providerUrl, matchProviderUrl);
233
+ async resolveByPrivateKey(privateKey, swarmUrl, matchSwarmUrl, name, authUrl) {
234
+ const matches = await this.accountService.findConfigsByPrivateKey(privateKey, swarmUrl, matchSwarmUrl);
276
235
  const desiredName = name?.trim();
277
- if (matches.length === 1) {
278
- await this.updateConfigDisplayName(matches[0].file, desiredName);
279
- await this.configFileManager.setCurrentConfig(matches[0].file);
280
- return;
281
- }
282
- if (matches.length > 1) {
283
- if (this.flags.yes) {
284
- this.log(`Too many matched accounts: ${matches.map((conf) => conf.name).join(',')} Selected first: ${matches[0].name}`);
285
- await this.updateConfigDisplayName(matches[0].file, desiredName);
286
- await this.configFileManager.setCurrentConfig(matches[0].file);
287
- return;
288
- }
289
- const selected = await this.selectPrompt({
290
- message: 'Multiple accounts found for this private key. Select one:',
291
- options: matches.map((match) => ({ label: match.name, value: match.file })),
292
- });
293
- await this.updateConfigDisplayName(selected, desiredName);
294
- await this.configFileManager.setCurrentConfig(selected);
236
+ if (matches.length > 0) {
237
+ const selectedFile = await this.selectMatchedConfig(matches);
238
+ await this.applySelectionWithDisplayName(selectedFile, desiredName, authUrl);
295
239
  return;
296
240
  }
297
241
  let shouldCreate = true;
@@ -305,8 +249,11 @@ export default class AccountLoginCommand extends BaseAccountCommand {
305
249
  const account = this.createAccountFromKey(privateKey);
306
250
  const suggestedName = desiredName || account.address;
307
251
  const displayName = this.flags.yes ? suggestedName : await this.askName(suggestedName);
308
- const configFile = getConfigNameFromCredentials(privateKey, providerUrl);
309
- await this.createConfigIfMissing(configFile, displayName, providerUrl, account);
252
+ const configFile = getConfigName(displayName);
253
+ const { created, configFile: createdFile } = await this.accountService.createConfigIfMissing(configFile, displayName, swarmUrl, account, authUrl);
254
+ if (created) {
255
+ this.log(`Created account: ${displayName}`);
256
+ }
310
257
  let shouldSwitch = true;
311
258
  if (!this.flags.yes) {
312
259
  shouldSwitch = await this.confirmPrompt(`Switch to account "${displayName}" now?`, true);
@@ -315,7 +262,7 @@ export default class AccountLoginCommand extends BaseAccountCommand {
315
262
  this.shouldSkipLogin = true;
316
263
  return;
317
264
  }
318
- await this.configFileManager.setCurrentConfig(configFile);
265
+ await this.setCurrentAndAuth(createdFile, authUrl);
319
266
  }
320
267
  async resolveConfiguration() {
321
268
  const name = this.flags.name?.trim();
@@ -326,19 +273,23 @@ export default class AccountLoginCommand extends BaseAccountCommand {
326
273
  const privateKey = this.flags.privateKey?.trim();
327
274
  const url = this.flags.url?.trim();
328
275
  if (url !== undefined && !url) {
329
- this.error('Provider URL cannot be empty.', { exit: 1 });
276
+ this.error('Swarm URL cannot be empty.', { exit: 1 });
277
+ }
278
+ const authUrl = this.flags.authUrl?.trim();
279
+ if (authUrl !== undefined && !authUrl) {
280
+ this.error('Auth URL cannot be empty.', { exit: 1 });
330
281
  }
331
- const providerUrl = (url || PROVIDER_URL).trim();
282
+ const swarmUrl = (url || SWARM_URL).trim();
332
283
  if (configPath) {
333
- await this.resolveByPath(configPath, name);
284
+ await this.resolveByPath(configPath, name, authUrl);
334
285
  return;
335
286
  }
336
287
  if (privateKey) {
337
- await this.resolveByPrivateKey(privateKey, providerUrl, Boolean(url), name);
288
+ await this.resolveByPrivateKey(privateKey, swarmUrl, Boolean(url), name, authUrl);
338
289
  return;
339
290
  }
340
291
  if (name) {
341
- await this.resolveByName(name, privateKey, providerUrl);
292
+ await this.resolveByName(name, privateKey, swarmUrl, authUrl);
342
293
  return;
343
294
  }
344
295
  }
@@ -20,7 +20,7 @@ export default class AccountSwitchCommand extends BaseAccountCommand {
20
20
  }
21
21
  async run() {
22
22
  const { name } = this.flags;
23
- const configs = this.configFileManager.getConfigsWithNames();
23
+ const configs = await this.configFileManager.getConfigsWithNames();
24
24
  const currentConfig = this.configFileManager.getCurrentConfigFile();
25
25
  if (configs.length === 0) {
26
26
  this.log('No accounts found');
@@ -30,7 +30,7 @@ export default class AccountSwitchCommand extends BaseAccountCommand {
30
30
  let selected;
31
31
  if (name) {
32
32
  try {
33
- const config = this.configFileManager.getConfigWithName(name);
33
+ const config = await this.configFileManager.getConfigWithName(name);
34
34
  selected = config.file;
35
35
  }
36
36
  catch {