@super-protocol/sp-cli 0.0.8 → 0.0.9

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/README.md CHANGED
@@ -21,7 +21,7 @@ $ npm install -g @super-protocol/sp-cli
21
21
  $ sp COMMAND
22
22
  running command...
23
23
  $ sp (--version)
24
- @super-protocol/sp-cli/0.0.8 linux-x64 node-v22.21.1
24
+ @super-protocol/sp-cli/0.0.9 linux-x64 node-v22.22.0
25
25
  $ sp --help [COMMAND]
26
26
  USAGE
27
27
  $ sp COMMAND
@@ -53,11 +53,11 @@ Purge all account details
53
53
 
54
54
  ```
55
55
  USAGE
56
- $ sp account forget -n <value> [--json] [--tty] [-f]
56
+ $ sp account forget [--json] [--tty] [-f] [-n <value>]
57
57
 
58
58
  FLAGS
59
- -f, --force Force deletion without confirmation
60
- -n, --name=<value> (required) Configuration name to delete
59
+ -f, --force Force forget without confirmation
60
+ -n, --name=<value> Account name to forget
61
61
 
62
62
  GLOBAL FLAGS
63
63
  --json Format output as json.
@@ -74,7 +74,7 @@ EXAMPLES
74
74
  $ sp account forget --name "My Account" --force
75
75
  ```
76
76
 
77
- _See code: [src/commands/account/forget.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/account/forget.ts)_
77
+ _See code: [src/commands/account/forget.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/account/forget.ts)_
78
78
 
79
79
  ## `sp account get-sppi`
80
80
 
@@ -95,7 +95,7 @@ EXAMPLES
95
95
  $ sp account get-sppi
96
96
  ```
97
97
 
98
- _See code: [src/commands/account/get-sppi.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/account/get-sppi.ts)_
98
+ _See code: [src/commands/account/get-sppi.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/account/get-sppi.ts)_
99
99
 
100
100
  ## `sp account info`
101
101
 
@@ -116,7 +116,7 @@ DESCRIPTION
116
116
  Information about current authorized user
117
117
  ```
118
118
 
119
- _See code: [src/commands/account/info.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/account/info.ts)_
119
+ _See code: [src/commands/account/info.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/account/info.ts)_
120
120
 
121
121
  ## `sp account list`
122
122
 
@@ -137,7 +137,7 @@ EXAMPLES
137
137
  $ sp account list
138
138
  ```
139
139
 
140
- _See code: [src/commands/account/list.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/account/list.ts)_
140
+ _See code: [src/commands/account/list.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/account/list.ts)_
141
141
 
142
142
  ## `sp account login`
143
143
 
@@ -145,11 +145,12 @@ Login to SuperProtocol (sign up or sign in).
145
145
 
146
146
  ```
147
147
  USAGE
148
- $ sp account login [--json] [--tty] [--name <value>] [--privateKey <value>] [-y]
148
+ $ sp account login [--json] [--tty] [--name <value>] [--privateKey <value>] [--path <value>] [-y]
149
149
 
150
150
  FLAGS
151
151
  -y, --yes Skip questions (generate keys when needed).
152
152
  --name=<value> Account readable name
153
+ --path=<value> Path to account(configuration) file to import
153
154
  --privateKey=<value> Account private key used for authentication
154
155
 
155
156
  GLOBAL FLAGS
@@ -166,7 +167,7 @@ EXAMPLES
166
167
  $ sp account login
167
168
  ```
168
169
 
169
- _See code: [src/commands/account/login.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/account/login.ts)_
170
+ _See code: [src/commands/account/login.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/account/login.ts)_
170
171
 
171
172
  ## `sp account switch`
172
173
 
@@ -174,7 +175,10 @@ Switch to a different account
174
175
 
175
176
  ```
176
177
  USAGE
177
- $ sp account switch [--json] [--tty]
178
+ $ sp account switch [--json] [--tty] [-n <value>]
179
+
180
+ FLAGS
181
+ -n, --name=<value> Account name to switch
178
182
 
179
183
  GLOBAL FLAGS
180
184
  --json Format output as json.
@@ -187,7 +191,7 @@ EXAMPLES
187
191
  $ sp account switch
188
192
  ```
189
193
 
190
- _See code: [src/commands/account/switch.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/account/switch.ts)_
194
+ _See code: [src/commands/account/switch.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/account/switch.ts)_
191
195
 
192
196
  ## `sp autocomplete [SHELL]`
193
197
 
@@ -246,7 +250,7 @@ EXAMPLES
246
250
  $ sp files download ./resource.json ./pathToDownload
247
251
  ```
248
252
 
249
- _See code: [src/commands/files/download.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/files/download.ts)_
253
+ _See code: [src/commands/files/download.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/files/download.ts)_
250
254
 
251
255
  ## `sp files upload PATH`
252
256
 
@@ -280,7 +284,7 @@ EXAMPLES
280
284
  $ sp files upload ./file.txt
281
285
  ```
282
286
 
283
- _See code: [src/commands/files/upload.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/files/upload.ts)_
287
+ _See code: [src/commands/files/upload.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/files/upload.ts)_
284
288
 
285
289
  ## `sp help [COMMAND]`
286
290
 
@@ -308,11 +312,12 @@ Login to SuperProtocol (sign up or sign in).
308
312
 
309
313
  ```
310
314
  USAGE
311
- $ sp login [--json] [--tty] [--name <value>] [--privateKey <value>] [-y]
315
+ $ sp login [--json] [--tty] [--name <value>] [--privateKey <value>] [--path <value>] [-y]
312
316
 
313
317
  FLAGS
314
318
  -y, --yes Skip questions (generate keys when needed).
315
319
  --name=<value> Account readable name
320
+ --path=<value> Path to account(configuration) file to import
316
321
  --privateKey=<value> Account private key used for authentication
317
322
 
318
323
  GLOBAL FLAGS
@@ -356,7 +361,7 @@ EXAMPLES
356
361
  $ sp storage create --not-default
357
362
  ```
358
363
 
359
- _See code: [src/commands/storage/create.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/storage/create.ts)_
364
+ _See code: [src/commands/storage/create.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/storage/create.ts)_
360
365
 
361
366
  ## `sp storage select`
362
367
 
@@ -377,7 +382,7 @@ EXAMPLES
377
382
  $ sp storage select
378
383
  ```
379
384
 
380
- _See code: [src/commands/storage/select.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/storage/select.ts)_
385
+ _See code: [src/commands/storage/select.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/storage/select.ts)_
381
386
 
382
387
  ## `sp storage show`
383
388
 
@@ -398,7 +403,7 @@ EXAMPLES
398
403
  $ sp storage show
399
404
  ```
400
405
 
401
- _See code: [src/commands/storage/show.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/storage/show.ts)_
406
+ _See code: [src/commands/storage/show.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/storage/show.ts)_
402
407
 
403
408
  ## `sp storage update`
404
409
 
@@ -425,7 +430,7 @@ EXAMPLES
425
430
  $ sp storage update --id=2de3e3a4-0000-1111-2222-333344445555
426
431
  ```
427
432
 
428
- _See code: [src/commands/storage/update.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/storage/update.ts)_
433
+ _See code: [src/commands/storage/update.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/storage/update.ts)_
429
434
 
430
435
  ## `sp workflows extend-lease ORDERID`
431
436
 
@@ -458,5 +463,5 @@ EXAMPLES
458
463
  $ sp workflows extend-lease <orderId> --sppi 2 --yes
459
464
  ```
460
465
 
461
- _See code: [src/commands/workflows/extend-lease.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.8/src/commands/workflows/extend-lease.ts)_
466
+ _See code: [src/commands/workflows/extend-lease.ts](https://github.com/Super-Protocol/sp-cli/blob/v0.0.9/src/commands/workflows/extend-lease.ts)_
462
467
  <!-- commandsstop -->
@@ -15,15 +15,16 @@ export class BaseAccountCommand extends BaseCommand {
15
15
  async interactWithUser(accountManager) {
16
16
  this.warn("Account doesn't exist in current configuration");
17
17
  const ask = await promptService.confirm({
18
- message: 'Do you want to create a new account?',
18
+ message: 'Do you already have an account?',
19
19
  });
20
- if (ask) {
20
+ if (!ask) {
21
21
  const account = await accountManager.create();
22
22
  this.log('Generated new keyPair for new account.');
23
23
  return account;
24
24
  }
25
25
  const privateKeyInput = await promptService.password({
26
- message: 'Please input your privateKey for using in account',
26
+ message: 'Please input your privateKey',
27
+ validate: (value) => value?.trim(),
27
28
  });
28
29
  const privateKey = privateKeyInput.trim();
29
30
  try {
@@ -5,7 +5,7 @@ export default class AccountForget extends BaseAccountCommand<typeof AccountForg
5
5
  static examples: string[];
6
6
  static flags: {
7
7
  force: import("@oclif/core/interfaces").BooleanFlag<boolean>;
8
- name: import("@oclif/core/interfaces").OptionFlag<string, import("@oclif/core/interfaces").CustomOptions>;
8
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
9
  };
10
10
  private configFileManager;
11
11
  init(): Promise<void>;
@@ -1,18 +1,7 @@
1
1
  import { Flags } from '@oclif/core';
2
2
  import { AppContainer } from '../../lib/container.js';
3
+ import { promptService } from '../../utils/prompt.service.js';
3
4
  import { BaseAccountCommand } from './base.js';
4
- const buildConfigNameFlag = (options = []) => options.length
5
- ? Flags.option({
6
- char: 'n',
7
- description: 'Configuration name to delete',
8
- options,
9
- required: true,
10
- })()
11
- : Flags.string({
12
- char: 'n',
13
- description: 'Configuration name to delete',
14
- required: true,
15
- });
16
5
  export default class AccountForget extends BaseAccountCommand {
17
6
  static authenticate = false;
18
7
  static description = 'Purge all account details';
@@ -25,9 +14,12 @@ export default class AccountForget extends BaseAccountCommand {
25
14
  force: Flags.boolean({
26
15
  char: 'f',
27
16
  default: false,
28
- description: 'Force deletion without confirmation',
17
+ description: 'Force forget without confirmation',
18
+ }),
19
+ name: Flags.string({
20
+ char: 'n',
21
+ description: 'Account name to forget',
29
22
  }),
30
- name: buildConfigNameFlag(),
31
23
  };
32
24
  configFileManager;
33
25
  async init() {
@@ -38,12 +30,69 @@ export default class AccountForget extends BaseAccountCommand {
38
30
  }
39
31
  async run() {
40
32
  const { force, name } = this.flags;
41
- await this.configFileManager.deleteConfigByName(name, force);
33
+ const configs = this.configFileManager.getConfigsWithNames();
34
+ if (configs.length === 0) {
35
+ this.error('Not found any account', { exit: 1 });
36
+ }
37
+ let configToDelete;
38
+ let displayName;
39
+ if (name) {
40
+ const config = configs.find((item) => item.name === name || item.file === name);
41
+ if (!config) {
42
+ this.error(`Account with name: ${name} not found`);
43
+ }
44
+ configToDelete = config.file;
45
+ displayName = config.name || config.file;
46
+ }
47
+ else {
48
+ const selection = await promptService.select({
49
+ message: 'Select account to forget:',
50
+ options: configs.map((config) => ({
51
+ label: config.name || config.file,
52
+ value: config.file,
53
+ })),
54
+ });
55
+ const config = configs.find((item) => item.file === selection);
56
+ configToDelete = selection;
57
+ displayName = config?.name || selection;
58
+ }
59
+ if (!force) {
60
+ const confirmed = await promptService.confirm({
61
+ initialValue: false,
62
+ message: `Are you sure you want to forget "${displayName}"?`,
63
+ });
64
+ if (!confirmed) {
65
+ this.log('Cancelled. Use --force to skip confirmation.');
66
+ return;
67
+ }
68
+ }
69
+ try {
70
+ await this.configFileManager.deleteConfigByName(configToDelete);
71
+ this.log(`Successfully forgot account: ${displayName}`);
72
+ const newCurrent = this.configFileManager.getCurrentConfigFile();
73
+ if (newCurrent) {
74
+ const configsAfter = this.configFileManager.getConfigsWithNames();
75
+ const newCurrentConfig = configsAfter.find((c) => c.file === newCurrent);
76
+ this.log(`Current account is now: ${newCurrentConfig?.name || newCurrent}`);
77
+ }
78
+ else {
79
+ this.log('No accounts remaining');
80
+ this.log('Create a new account with: sp account login --name "Account Name" or sp account login');
81
+ }
82
+ }
83
+ catch (error) {
84
+ const message = error instanceof Error ? error.message : String(error);
85
+ this.error(`Failed to forget account: ${message}`, { exit: 1 });
86
+ }
42
87
  }
43
88
  async updateNameFlagOptions() {
44
89
  const { configFileManager } = await AppContainer.container.initConfigFileManager().build();
45
90
  const configs = configFileManager.getConfigsWithNames();
46
91
  const options = configs.map((config) => config.name);
47
- this.constructor.flags.name = buildConfigNameFlag(options);
92
+ this.constructor.flags.name = Flags.option({
93
+ char: 'n',
94
+ description: 'Account name to forget',
95
+ options,
96
+ })();
48
97
  }
49
98
  }
@@ -55,11 +55,11 @@ export class InfoCommand extends BaseAccountCommand {
55
55
  const currentConfig = configs.find((c) => c.file === current);
56
56
  const displayName = currentConfig?.name || current;
57
57
  const configData = configFileManager.getConfigData(current);
58
- this.log(`Current configuration: ${displayName}`);
58
+ this.log(`Current account: ${displayName}`);
59
59
  this.log(`Configuration file: ${current}`);
60
60
  this.log(`Configuration directory: ${configDir}`);
61
61
  if (configData) {
62
- this.log('\nConfiguration details:');
62
+ this.log('\nAccount details:');
63
63
  if (configData.providerUrl) {
64
64
  this.log(` Provider URL: ${configData.providerUrl}`);
65
65
  }
@@ -81,9 +81,9 @@ export class InfoCommand extends BaseAccountCommand {
81
81
  }
82
82
  }
83
83
  else {
84
- this.log('No configuration is currently set');
85
- this.log(`Configuration directory: ${configDir}`);
86
- this.log('Create a new configuration with: sp account login --privateKey "<PRIVATE_KEY>" --name "Account Name"');
84
+ this.log('No account is currently set');
85
+ this.log(`Account directory: ${configDir}`);
86
+ this.log('Create a new account with: sp account login --privateKey "<PRIVATE_KEY>" --name "Account Name"');
87
87
  }
88
88
  }
89
89
  return {
@@ -7,6 +7,7 @@ export default class AccountLoginCommand extends BaseAccountCommand<typeof Accou
7
7
  static flags: {
8
8
  name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
9
9
  privateKey: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
+ path: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
10
11
  url: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
12
  yes: import("@oclif/core/interfaces").BooleanFlag<boolean>;
12
13
  };
@@ -26,9 +27,14 @@ export default class AccountLoginCommand extends BaseAccountCommand<typeof Accou
26
27
  private confirmPrompt;
27
28
  private createAccountFromKey;
28
29
  private createConfigIfMissing;
30
+ private updateConfigDisplayName;
29
31
  private findConfigsByPrivateKey;
30
32
  private getPrivateKeyOrGenerate;
31
33
  private resolveByName;
34
+ private assertConfigHasAccount;
35
+ private loadConfigFromPath;
36
+ private resolveConfigFileName;
37
+ private resolveByPath;
32
38
  private resolveByPrivateKey;
33
39
  private resolveConfiguration;
34
40
  private static maskPrivateKey;
@@ -1,11 +1,13 @@
1
1
  import { existsSync } from 'node:fs';
2
2
  import path from 'node:path';
3
3
  import { Flags } from '@oclif/core';
4
+ import { Value } from 'typebox/value';
4
5
  import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
6
+ import { cliConfigSchema } from '../../config/config.schema.js';
5
7
  import { PROVIDER_URL } from '../../constants.js';
6
8
  import { AuthService } from '../../services/auth.service.js';
7
9
  import { StorageService } from '../../services/storage.service.js';
8
- import { getConfigNameFromCredentials } from '../../utils/helper.js';
10
+ import { getConfigName, getConfigNameFromCredentials, preparePath, readJsonFile, } from '../../utils/helper.js';
9
11
  import { promptService } from '../../utils/prompt.service.js';
10
12
  import { BaseAccountCommand } from './base.js';
11
13
  export default class AccountLoginCommand extends BaseAccountCommand {
@@ -23,6 +25,9 @@ export default class AccountLoginCommand extends BaseAccountCommand {
23
25
  privateKey: Flags.string({
24
26
  description: 'Account private key used for authentication',
25
27
  }),
28
+ path: Flags.string({
29
+ description: 'Path to account(configuration) file to import',
30
+ }),
26
31
  url: Flags.string({
27
32
  description: 'Provider base URL',
28
33
  hidden: true,
@@ -90,7 +95,7 @@ export default class AccountLoginCommand extends BaseAccountCommand {
90
95
  }
91
96
  async askName(fallback) {
92
97
  const value = await this.inputPrompt({
93
- message: 'Configuration name:',
98
+ message: 'Account name:',
94
99
  placeholder: fallback,
95
100
  });
96
101
  const trimmed = value.trim();
@@ -115,10 +120,22 @@ export default class AccountLoginCommand extends BaseAccountCommand {
115
120
  async createConfigIfMissing(configFile, displayName, providerUrl, account) {
116
121
  const configPath = path.join(this.configFileManager.getConfigDir(), configFile);
117
122
  if (existsSync(configPath)) {
123
+ await this.configFileManager.updateConfigName(configFile, displayName);
118
124
  return;
119
125
  }
120
126
  await this.configFileManager.createConfig(configFile, displayName, providerUrl, account);
121
- this.log(`Created configuration: ${displayName}`);
127
+ this.log(`Created account: ${displayName}`);
128
+ }
129
+ async updateConfigDisplayName(configFile, displayName) {
130
+ if (!displayName) {
131
+ return;
132
+ }
133
+ const updated = await this.configFileManager.updateConfigName(configFile, displayName);
134
+ if (updated) {
135
+ this.log(`Updated account name to "${displayName}"`);
136
+ return;
137
+ }
138
+ this.log(`Account name: "${displayName}"`);
122
139
  }
123
140
  findConfigsByPrivateKey(privateKey, providerUrl, matchProviderUrl) {
124
141
  const normalizedKey = privateKey.trim().toLowerCase();
@@ -159,7 +176,7 @@ export default class AccountLoginCommand extends BaseAccountCommand {
159
176
  }
160
177
  let shouldCreate = true;
161
178
  if (!this.flags.yes) {
162
- shouldCreate = await this.confirmPrompt(`Configuration "${name}" not found. Create it?`, true);
179
+ shouldCreate = await this.confirmPrompt(`Account with "${name}" not found. Create it?`, true);
163
180
  }
164
181
  if (!shouldCreate) {
165
182
  this.shouldSkipLogin = true;
@@ -171,7 +188,7 @@ export default class AccountLoginCommand extends BaseAccountCommand {
171
188
  await this.createConfigIfMissing(configFile, name, providerUrl, account);
172
189
  let shouldSwitch = true;
173
190
  if (!this.flags.yes) {
174
- shouldSwitch = await this.confirmPrompt(`Switch to configuration "${name}" now?`, true);
191
+ shouldSwitch = await this.confirmPrompt(`Switch to account "${name}" now?`, true);
175
192
  }
176
193
  if (!shouldSwitch) {
177
194
  this.shouldSkipLogin = true;
@@ -179,41 +196,120 @@ export default class AccountLoginCommand extends BaseAccountCommand {
179
196
  }
180
197
  await this.configFileManager.setCurrentConfig(configFile);
181
198
  }
182
- async resolveByPrivateKey(privateKey, providerUrl, matchProviderUrl) {
199
+ assertConfigHasAccount(config) {
200
+ if (!config.account?.privateKey) {
201
+ throw new Error('Account private key is required for import.');
202
+ }
203
+ }
204
+ async loadConfigFromPath(configPath) {
205
+ 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;
212
+ }
213
+ 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);
223
+ }
224
+ const baseName = path.basename(configPath, path.extname(configPath));
225
+ return getConfigName(baseName);
226
+ }
227
+ async resolveByPath(rawPath, name) {
228
+ const sourcePath = preparePath(rawPath);
229
+ const config = await this.loadConfigFromPath(sourcePath);
230
+ const targetFile = this.resolveConfigFileName(sourcePath, config, name);
231
+ const targetPath = path.join(this.configFileManager.getConfigDir(), targetFile);
232
+ const desiredName = name?.trim();
233
+ const privateKey = config.account.privateKey;
234
+ const providerUrl = (config.providerUrl ?? PROVIDER_URL).trim();
235
+ const matches = this.findConfigsByPrivateKey(privateKey, providerUrl, true);
236
+ 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
+ }
249
+ this.log('Account already exists. Nothing to import.');
250
+ await this.updateConfigDisplayName(selectedFile, desiredName);
251
+ await this.configFileManager.setCurrentConfig(selectedFile);
252
+ return;
253
+ }
254
+ if (existsSync(targetPath)) {
255
+ const existingConfig = this.configFileManager.getConfigData(targetFile);
256
+ const existingKey = existingConfig?.account?.privateKey?.trim().toLowerCase();
257
+ const existingUrl = (existingConfig?.providerUrl ?? PROVIDER_URL).trim();
258
+ const importedKey = privateKey.trim().toLowerCase();
259
+ if (!existingKey || existingKey !== importedKey || existingUrl !== providerUrl) {
260
+ this.error(`Account file "${targetFile}" already exists with different credentials. Use a different name or remove the existing file before importing.`, { exit: 1 });
261
+ }
262
+ this.log(`Account already exists. Switching to ${name || targetFile}`);
263
+ await this.configFileManager.setCurrentConfig(targetFile);
264
+ return;
265
+ }
266
+ try {
267
+ const importedFile = await this.configFileManager.importConfig(sourcePath, name);
268
+ await this.configFileManager.setCurrentConfig(importedFile);
269
+ }
270
+ catch (error) {
271
+ this.error(error instanceof Error ? error.message : String(error), { exit: 1 });
272
+ }
273
+ }
274
+ async resolveByPrivateKey(privateKey, providerUrl, matchProviderUrl, name) {
183
275
  const matches = this.findConfigsByPrivateKey(privateKey, providerUrl, matchProviderUrl);
276
+ const desiredName = name?.trim();
184
277
  if (matches.length === 1) {
278
+ await this.updateConfigDisplayName(matches[0].file, desiredName);
185
279
  await this.configFileManager.setCurrentConfig(matches[0].file);
186
280
  return;
187
281
  }
188
282
  if (matches.length > 1) {
189
283
  if (this.flags.yes) {
190
- this.log(`Too many matched configs: ${matches.map((conf) => conf.name).join(',')} Selected first: ${matches[0].name}`);
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);
191
286
  await this.configFileManager.setCurrentConfig(matches[0].file);
192
287
  return;
193
288
  }
194
289
  const selected = await this.selectPrompt({
195
- message: 'Multiple configurations found for this private key. Select one:',
290
+ message: 'Multiple accounts found for this private key. Select one:',
196
291
  options: matches.map((match) => ({ label: match.name, value: match.file })),
197
292
  });
293
+ await this.updateConfigDisplayName(selected, desiredName);
198
294
  await this.configFileManager.setCurrentConfig(selected);
199
295
  return;
200
296
  }
201
297
  let shouldCreate = true;
202
298
  if (!this.flags.yes) {
203
- shouldCreate = await this.confirmPrompt('No configuration found for this private key. Create a new one?', true);
299
+ shouldCreate = await this.confirmPrompt('No accounts found for this private key. Create a new one?', true);
204
300
  }
205
301
  if (!shouldCreate) {
206
302
  this.shouldSkipLogin = true;
207
303
  return;
208
304
  }
209
305
  const account = this.createAccountFromKey(privateKey);
210
- const suggestedName = this.flags.name?.trim() || account.address;
306
+ const suggestedName = desiredName || account.address;
211
307
  const displayName = this.flags.yes ? suggestedName : await this.askName(suggestedName);
212
308
  const configFile = getConfigNameFromCredentials(privateKey, providerUrl);
213
309
  await this.createConfigIfMissing(configFile, displayName, providerUrl, account);
214
310
  let shouldSwitch = true;
215
311
  if (!this.flags.yes) {
216
- shouldSwitch = await this.confirmPrompt(`Switch to configuration "${displayName}" now?`, true);
312
+ shouldSwitch = await this.confirmPrompt(`Switch to account "${displayName}" now?`, true);
217
313
  }
218
314
  if (!shouldSwitch) {
219
315
  this.shouldSkipLogin = true;
@@ -223,18 +319,27 @@ export default class AccountLoginCommand extends BaseAccountCommand {
223
319
  }
224
320
  async resolveConfiguration() {
225
321
  const name = this.flags.name?.trim();
322
+ const configPath = this.flags.path?.trim();
323
+ if (configPath !== undefined && !configPath) {
324
+ this.error('Account path cannot be empty.', { exit: 1 });
325
+ }
226
326
  const privateKey = this.flags.privateKey?.trim();
227
327
  const url = this.flags.url?.trim();
228
328
  if (url !== undefined && !url) {
229
329
  this.error('Provider URL cannot be empty.', { exit: 1 });
230
330
  }
231
331
  const providerUrl = (url || PROVIDER_URL).trim();
232
- if (name) {
233
- await this.resolveByName(name, privateKey, providerUrl);
332
+ if (configPath) {
333
+ await this.resolveByPath(configPath, name);
234
334
  return;
235
335
  }
236
336
  if (privateKey) {
237
- await this.resolveByPrivateKey(privateKey, providerUrl, Boolean(url));
337
+ await this.resolveByPrivateKey(privateKey, providerUrl, Boolean(url), name);
338
+ return;
339
+ }
340
+ if (name) {
341
+ await this.resolveByName(name, privateKey, providerUrl);
342
+ return;
238
343
  }
239
344
  }
240
345
  static maskPrivateKey(privateKey) {
@@ -6,6 +6,9 @@ export default class AccountSwitchCommand extends BaseAccountCommand<typeof Acco
6
6
  static examples: string[];
7
7
  protected configFileManager: ConfigFileManager;
8
8
  protected configManager: ConfigManager;
9
+ static flags: {
10
+ name: import("@oclif/core/interfaces").OptionFlag<string | undefined, import("@oclif/core/interfaces").CustomOptions>;
11
+ };
9
12
  init(): Promise<void>;
10
13
  run(): Promise<void>;
11
14
  private switchConfig;
@@ -1,3 +1,4 @@
1
+ import { Flags } from '@oclif/core';
1
2
  import { BaseAccountCommand } from './base.js';
2
3
  export default class AccountSwitchCommand extends BaseAccountCommand {
3
4
  static authenticate = false;
@@ -5,6 +6,12 @@ export default class AccountSwitchCommand extends BaseAccountCommand {
5
6
  static examples = ['<%= config.bin %> <%= command.id %>'];
6
7
  configFileManager;
7
8
  configManager;
9
+ static flags = {
10
+ name: Flags.string({
11
+ char: 'n',
12
+ description: 'Account name to switch',
13
+ }),
14
+ };
8
15
  async init() {
9
16
  await super.init();
10
17
  await this.container.initConfigFileManager().build();
@@ -12,28 +19,41 @@ export default class AccountSwitchCommand extends BaseAccountCommand {
12
19
  await this.container.initConfigManager().build();
13
20
  }
14
21
  async run() {
22
+ const { name } = this.flags;
15
23
  const configs = this.configFileManager.getConfigsWithNames();
16
24
  const currentConfig = this.configFileManager.getCurrentConfigFile();
17
25
  if (configs.length === 0) {
18
26
  this.log('No accounts found');
19
- this.log('Create a new account with: sp account login --privateKey "<PRIVATE_KEY>" --name "Account Name"');
27
+ this.log('Create a new account with: sp account login --privateKey "<PRIVATE_KEY>" --name "Account Name" or sp account login');
20
28
  return;
21
29
  }
22
- const selected = await this.selectPrompt({
23
- initialValue: currentConfig,
24
- message: 'Select an account:',
25
- options: configs.map((config) => ({
26
- label: config.name ?? config.file,
27
- value: config.file,
28
- })),
29
- });
30
+ let selected;
31
+ if (name) {
32
+ try {
33
+ const config = this.configFileManager.getConfigWithName(name);
34
+ selected = config.file;
35
+ }
36
+ catch {
37
+ this.error(`Account with name ${name} not found`);
38
+ }
39
+ }
40
+ else {
41
+ selected = await this.selectPrompt({
42
+ initialValue: currentConfig,
43
+ message: 'Select an account:',
44
+ options: configs.map((config) => ({
45
+ label: config.name ?? config.file,
46
+ value: config.file,
47
+ })),
48
+ });
49
+ }
30
50
  const selectedConfig = configs.find((c) => c.file === selected);
31
51
  await this.switchConfig(selected, selectedConfig?.name || selected);
32
52
  }
33
53
  async switchConfig(configFile, displayName) {
34
54
  await this.configFileManager.setCurrentConfig(configFile);
35
55
  this.log(`Switched to account: ${displayName || configFile}`);
36
- await this.initAccountContext({ enableAuth: false, enableCookies: true, rebuild: true });
56
+ await this.container.initConfigManager(true).build();
37
57
  this.configFileManager = this.container.configFileManager;
38
58
  this.configManager = this.container.configManager;
39
59
  }
@@ -63,6 +63,7 @@ export class BaseCommand extends Command {
63
63
  strict: false,
64
64
  });
65
65
  const isInteractive = isInteractiveMode(parsedResult.flags.tty);
66
+ promptService.setInteractiveMode(isInteractive);
66
67
  const missingFlags = await findMissingRequiredFlags(parsedResult.flags, allFlags);
67
68
  const missingArgs = await findMissingRequiredArgs(parsedResult.args, this.ctor.args);
68
69
  if (missingFlags.length > 0 || missingArgs.length > 0) {
@@ -2,6 +2,7 @@ import { BaseCommand } from '../../commands/base.js';
2
2
  import { AppContainer } from '../../lib/container.js';
3
3
  import logger from '../../logger.js';
4
4
  import { AuthService } from '../../services/auth.service.js';
5
+ import { isInteractiveMode } from '../../utils/tty.js';
5
6
  const isBaseCommandClass = (commandClass) => commandClass.prototype instanceof BaseCommand;
6
7
  const getConfigFlag = (commandClass, argv) => {
7
8
  if (!isBaseCommandClass(commandClass)) {
@@ -47,7 +48,7 @@ const hook = async (opts) => {
47
48
  const { configManager, accountManager, providerClient } = container;
48
49
  const credentials = await configManager.get('auth');
49
50
  if (!credentials?.accessKey) {
50
- if (!process.stdin.isTTY || !process.stdout.isTTY) {
51
+ if (!isInteractiveMode()) {
51
52
  context.error('Authorization not found. Please run: sp account login', { exit: 1 });
52
53
  }
53
54
  const args = configFile ? ['--config', configFile] : [];
@@ -1,16 +1,8 @@
1
- import { confirm, select } from '@clack/prompts';
2
- import { ux } from '@oclif/core';
3
1
  import type pino from 'pino';
4
2
  import { type Account, type CliConfig } from '../config/config.schema.js';
5
3
  import type { IManager } from '../interfaces/manager.interface.js';
6
- type ConfirmPrompt = typeof confirm;
7
- type SelectPrompt = typeof select;
8
- type UxLike = Pick<typeof ux, 'error' | 'stdout'>;
9
4
  interface ConfigFileManagerOptions {
10
- confirmPrompt?: ConfirmPrompt;
11
5
  runtimeConfigFile?: string;
12
- selectPrompt?: SelectPrompt;
13
- ux?: UxLike;
14
6
  }
15
7
  export declare class ConfigFileManager implements IManager {
16
8
  private readonly logger;
@@ -20,17 +12,19 @@ export declare class ConfigFileManager implements IManager {
20
12
  private readonly configFilePath;
21
13
  private configs;
22
14
  private readonly configsDir;
23
- private readonly confirmPrompt;
24
15
  private runtimeConfigFile?;
25
- private readonly selectPrompt;
26
- private readonly ux;
27
16
  constructor(configDir: string, logger: pino.BaseLogger, options?: ConfigFileManagerOptions);
28
17
  createConfig(configFileName: string, name: string, url?: string, account?: Account): Promise<void>;
18
+ updateConfigName(configFileName: string, name: string): Promise<boolean>;
29
19
  deleteConfig(configName: string): Promise<void>;
30
- deleteConfigByName(name?: string, force?: boolean): Promise<void>;
20
+ deleteConfigByName(name?: string): Promise<void>;
31
21
  getConfigData(configName: string): CliConfig | undefined;
32
22
  getConfigDir(): string;
33
23
  getConfigs(): string[];
24
+ getConfigWithName(name: string): {
25
+ file: string;
26
+ name: string;
27
+ };
34
28
  getConfigsWithNames(): Array<{
35
29
  file: string;
36
30
  name: string;
@@ -1,7 +1,5 @@
1
1
  import * as fs from 'node:fs';
2
2
  import path from 'node:path';
3
- import { confirm, isCancel, select } from '@clack/prompts';
4
- import { ux } from '@oclif/core';
5
3
  import { Value } from 'typebox/value';
6
4
  import { cliConfigSchema } from '../config/config.schema.js';
7
5
  import { DEFAULT_USER_CONFIG_FILE, DEFAULT_USER_CONFIG_NAME, DIR_ACCESS_PERMS, FILE_ACCESS_PERMS, } from '../constants.js';
@@ -16,24 +14,18 @@ export class ConfigFileManager {
16
14
  currentConfig: undefined,
17
15
  };
18
16
  configsDir;
19
- confirmPrompt;
20
17
  runtimeConfigFile;
21
- selectPrompt;
22
- ux;
23
18
  constructor(configDir, logger, options = {}) {
24
19
  this.logger = logger;
25
20
  this.configDir = configDir;
26
21
  this.configFilePath = path.join(this.configDir, ConfigFileManager.CONFIG_FILE);
27
22
  this.configsDir = path.join(this.configDir, ConfigFileManager.CONFIGS_DIR);
28
- this.confirmPrompt = options.confirmPrompt ?? confirm;
29
- this.selectPrompt = options.selectPrompt ?? select;
30
- this.ux = options.ux ?? ux;
31
23
  this.runtimeConfigFile = options.runtimeConfigFile;
32
24
  }
33
25
  async createConfig(configFileName, name, url, account) {
34
26
  const configPath = path.join(this.configsDir, configFileName);
35
27
  if (fs.existsSync(configPath)) {
36
- throw new Error(`Configuration file already exists: ${configFileName}`);
28
+ throw new Error(`Account file already exists: ${configFileName}`);
37
29
  }
38
30
  const config = { name };
39
31
  if (url) {
@@ -49,70 +41,69 @@ export class ConfigFileManager {
49
41
  });
50
42
  this.logger.info({ configFileName, name, url }, 'Created new configuration');
51
43
  }
44
+ async updateConfigName(configFileName, name) {
45
+ const configPath = path.join(this.configsDir, configFileName);
46
+ const normalizedName = name.trim();
47
+ if (!normalizedName) {
48
+ throw new Error('Configuration name cannot be empty');
49
+ }
50
+ if (!fs.existsSync(configPath)) {
51
+ throw new Error(`Account file not found: ${configFileName}`);
52
+ }
53
+ let config;
54
+ try {
55
+ const raw = fs.readFileSync(configPath, 'utf8');
56
+ config = JSON.parse(raw);
57
+ }
58
+ catch (error) {
59
+ this.logger.warn({ configFileName, err: error }, 'Failed to read config data');
60
+ throw new Error(`Failed to read configuration: ${configFileName}`);
61
+ }
62
+ if (config.name === normalizedName) {
63
+ return false;
64
+ }
65
+ config.name = normalizedName;
66
+ fs.writeFileSync(configPath, JSON.stringify(config, undefined, 2), {
67
+ encoding: 'utf8',
68
+ flag: 'w',
69
+ mode: FILE_ACCESS_PERMS,
70
+ });
71
+ this.logger.info({ configFileName, name: normalizedName }, 'Updated configuration name');
72
+ return true;
73
+ }
52
74
  async deleteConfig(configName) {
53
75
  const configPath = path.join(this.configsDir, configName);
54
76
  if (!fs.existsSync(configPath)) {
55
- throw new Error(`Configuration file not found: ${configName}`);
77
+ throw new Error(`Account file not found: ${configName}`);
56
78
  }
57
79
  await this.removeConfig(configName);
58
80
  fs.unlinkSync(configPath);
59
81
  this.logger.info({ configName }, 'Deleted configuration file');
60
82
  }
61
- async deleteConfigByName(name, force) {
83
+ async deleteConfigByName(name) {
62
84
  const configs = this.getConfigsWithNames();
63
85
  if (configs.length === 0) {
64
- this.ux.stdout('No configurations found');
65
- return;
86
+ throw new Error('No configurations found');
66
87
  }
67
- let configToDelete;
68
- if (name) {
69
- const config = configs.find((c) => c.name === name);
70
- if (!config) {
71
- this.ux.error(`Configuration not found: ${name}`);
72
- }
73
- configToDelete = config.file;
88
+ if (!name) {
89
+ throw new Error('Configuration name is required');
74
90
  }
75
- else {
76
- const selection = await this.selectPrompt({
77
- message: 'Select configuration to delete:',
78
- options: configs.map((config) => ({
79
- label: config.name,
80
- value: config.file,
81
- })),
82
- });
83
- if (isCancel(selection)) {
84
- this.ux.stdout('Deletion cancelled');
85
- return;
86
- }
87
- configToDelete = selection;
88
- }
89
- const configToDeleteData = configs.find((c) => c.file === configToDelete);
90
- const displayName = configToDeleteData?.name || configToDelete;
91
- if (!force) {
92
- const confirmed = await this.confirmPrompt({
93
- initialValue: false,
94
- message: `Are you sure you want to delete configuration "${displayName}"?`,
95
- });
96
- if (isCancel(confirmed) || !confirmed) {
97
- this.ux.stdout('Deletion cancelled');
98
- return;
99
- }
91
+ const configToDeleteData = configs.find((c) => c.name === name || c.file === name);
92
+ if (!configToDeleteData) {
93
+ throw new Error(`Configuration not found: ${name}`);
100
94
  }
101
- try {
102
- await this.deleteConfig(configToDelete);
103
- this.ux.stdout(`Successfully deleted configuration: ${displayName}`);
104
- const newCurrent = this.getCurrentConfigFile();
105
- if (newCurrent) {
106
- const newCurrentConfig = configs.find((c) => c.file === newCurrent);
107
- this.ux.stdout(`Current configuration is now: ${newCurrentConfig?.name || newCurrent}`);
108
- }
109
- else {
110
- this.ux.stdout('No configurations remaining');
111
- this.ux.stdout('Create a new account with: sp account login --name "Account Name" or sp auth login');
112
- }
95
+ const configToDelete = configToDeleteData.file;
96
+ const displayName = configToDeleteData.name || configToDelete;
97
+ await this.deleteConfig(configToDelete);
98
+ this.logger.info({ displayName, configToDelete }, 'Successfully deleted configuration');
99
+ const newCurrent = this.getCurrentConfigFile();
100
+ if (newCurrent) {
101
+ const newCurrentConfig = configs.find((c) => c.file === newCurrent);
102
+ this.logger.info({ currentConfig: newCurrentConfig?.name || newCurrent }, 'Current configuration is now');
113
103
  }
114
- catch (error) {
115
- this.ux.error(`Failed to delete configuration: ${error instanceof Error ? error.message : String(error)}`);
104
+ else {
105
+ this.logger.info('No configurations remaining');
106
+ this.logger.info('Create a new account with: sp account login --name "Account Name" or sp account login');
116
107
  }
117
108
  }
118
109
  getConfigData(configName) {
@@ -138,6 +129,14 @@ export class ConfigFileManager {
138
129
  }
139
130
  return fs.readdirSync(this.configsDir).filter((file) => file.endsWith('.config.json'));
140
131
  }
132
+ getConfigWithName(name) {
133
+ const configs = this.getConfigsWithNames();
134
+ const config = configs.filter((config) => config.name === name || config.file === name).at(0);
135
+ if (!config) {
136
+ throw new Error(`Account with name ${name} not found`);
137
+ }
138
+ return config;
139
+ }
141
140
  getConfigsWithNames() {
142
141
  if (!fs.existsSync(this.configsDir)) {
143
142
  return [];
@@ -14,6 +14,7 @@ export const createProgressPrinter = ({ action, done = 'completed', start, }) =>
14
14
  progressBar.stop(`${action} ${lastKey} ${done}`);
15
15
  }
16
16
  const progressBar = p.progress({ size: 100, style: 'block' });
17
+ lastKey = key;
17
18
  progressBar.start(`${start} ${key}`);
18
19
  progressBars[key] = progressBar;
19
20
  return progressBar;
@@ -4,7 +4,14 @@ export type ConfirmOptions = ClackConfirmOptions;
4
4
  export type SelectOptions<T> = ClackSelectOptions<T>;
5
5
  export type PasswordOptions = ClackPasswordOptions;
6
6
  type ParseValue = (input: string) => string | number | boolean;
7
- export declare class PromptService {
7
+ export declare class NonInteractiveError extends Error {
8
+ constructor(message: string);
9
+ }
10
+ declare class PromptService {
11
+ private _isInteractive;
12
+ setInteractiveMode(value: boolean): void;
13
+ get isInteractive(): boolean;
14
+ private checkInteractive;
8
15
  text(options: ClackTextOptions): Promise<string>;
9
16
  confirm(options: ClackConfirmOptions): Promise<boolean>;
10
17
  select<T>(options: ClackSelectOptions<T>): Promise<T>;
@@ -1,6 +1,29 @@
1
1
  import { confirm as clackConfirm, password as clackPassword, select as clackSelect, text as clackText, isCancel, } from '@clack/prompts';
2
- export class PromptService {
2
+ import { isInteractiveMode } from './tty.js';
3
+ export class NonInteractiveError extends Error {
4
+ constructor(message) {
5
+ super(message);
6
+ this.name = 'NonInteractiveError';
7
+ }
8
+ }
9
+ class PromptService {
10
+ _isInteractive = null;
11
+ setInteractiveMode(value) {
12
+ this._isInteractive = value;
13
+ }
14
+ get isInteractive() {
15
+ if (this._isInteractive === null) {
16
+ this._isInteractive = isInteractiveMode();
17
+ }
18
+ return this._isInteractive;
19
+ }
20
+ checkInteractive(operation) {
21
+ if (!this.isInteractive) {
22
+ throw new NonInteractiveError(`Cannot ${operation} in non-interactive mode. Use appropriate flags or run without --no-tty.`);
23
+ }
24
+ }
3
25
  async text(options) {
26
+ this.checkInteractive('prompt for text input');
4
27
  const result = await clackText(options);
5
28
  if (isCancel(result)) {
6
29
  throw new Error('Operation cancelled.');
@@ -8,6 +31,12 @@ export class PromptService {
8
31
  return result;
9
32
  }
10
33
  async confirm(options) {
34
+ if (!this.isInteractive) {
35
+ if (options.initialValue !== undefined) {
36
+ return options.initialValue;
37
+ }
38
+ throw new NonInteractiveError('Cannot prompt for confirmation in non-interactive mode. Use --force or run without --no-tty.');
39
+ }
11
40
  const result = await clackConfirm(options);
12
41
  if (isCancel(result)) {
13
42
  throw new Error('Operation cancelled.');
@@ -15,6 +44,7 @@ export class PromptService {
15
44
  return result;
16
45
  }
17
46
  async select(options) {
47
+ this.checkInteractive('show selection menu');
18
48
  const result = await clackSelect(options);
19
49
  if (isCancel(result)) {
20
50
  throw new Error('Operation cancelled.');
@@ -22,6 +52,7 @@ export class PromptService {
22
52
  return result;
23
53
  }
24
54
  async password(options) {
55
+ this.checkInteractive('prompt for password');
25
56
  const result = await clackPassword(options);
26
57
  if (isCancel(result)) {
27
58
  throw new Error('Operation cancelled.');
@@ -35,6 +66,7 @@ export class PromptService {
35
66
  return value;
36
67
  }
37
68
  async promptForValue(name, description, type, parseValue) {
69
+ this.checkInteractive('prompt for value');
38
70
  const fullDescription = description ? ` (${description})` : '';
39
71
  if (type === 'boolean') {
40
72
  const message = `Enable ${name}${fullDescription}?`;
@@ -37,16 +37,15 @@
37
37
  },
38
38
  "force": {
39
39
  "char": "f",
40
- "description": "Force deletion without confirmation",
40
+ "description": "Force forget without confirmation",
41
41
  "name": "force",
42
42
  "allowNo": false,
43
43
  "type": "boolean"
44
44
  },
45
45
  "name": {
46
46
  "char": "n",
47
- "description": "Configuration name to delete",
47
+ "description": "Account name to forget",
48
48
  "name": "name",
49
- "required": true,
50
49
  "hasDynamicHelp": false,
51
50
  "multiple": false,
52
51
  "type": "option"
@@ -269,6 +268,13 @@
269
268
  "multiple": false,
270
269
  "type": "option"
271
270
  },
271
+ "path": {
272
+ "description": "Path to account(configuration) file to import",
273
+ "name": "path",
274
+ "hasDynamicHelp": false,
275
+ "multiple": false,
276
+ "type": "option"
277
+ },
272
278
  "url": {
273
279
  "description": "Provider base URL",
274
280
  "hidden": true,
@@ -333,6 +339,14 @@
333
339
  "summary": "Force or disable interactive mode (use --no-tty to disable).",
334
340
  "allowNo": true,
335
341
  "type": "boolean"
342
+ },
343
+ "name": {
344
+ "char": "n",
345
+ "description": "Account name to switch",
346
+ "name": "name",
347
+ "hasDynamicHelp": false,
348
+ "multiple": false,
349
+ "type": "option"
336
350
  }
337
351
  },
338
352
  "hasDynamicHelp": false,
@@ -839,5 +853,5 @@
839
853
  ]
840
854
  }
841
855
  },
842
- "version": "0.0.8"
856
+ "version": "0.0.9"
843
857
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@super-protocol/sp-cli",
3
3
  "description": "SuperProtocol Command line interface",
4
- "version": "0.0.8",
4
+ "version": "0.0.9",
5
5
  "author": "SuperProtocol",
6
6
  "publishConfig": {
7
7
  "access": "public",