@super-protocol/sp-cli 0.0.6 → 0.0.8

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 (92) hide show
  1. package/README.md +116 -234
  2. package/dist/commands/account/base.d.ts +19 -0
  3. package/dist/commands/account/base.js +42 -0
  4. package/dist/commands/account/forget.d.ts +14 -0
  5. package/dist/commands/account/forget.js +49 -0
  6. package/dist/commands/account/get-sppi.d.ts +2 -2
  7. package/dist/commands/account/get-sppi.js +8 -13
  8. package/dist/commands/account/info.d.ts +5 -2
  9. package/dist/commands/account/info.js +53 -3
  10. package/dist/commands/account/list.d.ts +9 -0
  11. package/dist/commands/{config → account}/list.js +16 -9
  12. package/dist/commands/account/login.d.ts +35 -0
  13. package/dist/commands/account/login.js +249 -0
  14. package/dist/commands/account/switch.d.ts +12 -0
  15. package/dist/commands/account/switch.js +40 -0
  16. package/dist/commands/base.d.ts +4 -3
  17. package/dist/commands/base.js +99 -13
  18. package/dist/commands/files/download.js +13 -5
  19. package/dist/commands/files/upload.js +11 -7
  20. package/dist/commands/storage/base.d.ts +2 -2
  21. package/dist/commands/storage/base.js +11 -11
  22. package/dist/commands/storage/create.js +1 -1
  23. package/dist/commands/storage/select.js +6 -8
  24. package/dist/commands/storage/show.js +2 -4
  25. package/dist/commands/storage/update.js +39 -22
  26. package/dist/commands/workflows/extend-lease.d.ts +1 -1
  27. package/dist/commands/workflows/extend-lease.js +21 -13
  28. package/dist/config/config-file.schema.d.ts +1 -2
  29. package/dist/config/config-file.schema.js +0 -1
  30. package/dist/config/config.schema.d.ts +1 -1
  31. package/dist/errors.d.ts +2 -0
  32. package/dist/errors.js +2 -0
  33. package/dist/hooks/finally/shutdown-blockchain.d.ts +1 -1
  34. package/dist/hooks/finally/shutdown-blockchain.js +1 -1
  35. package/dist/hooks/finally/unlock-config.d.ts +3 -0
  36. package/dist/hooks/finally/unlock-config.js +5 -0
  37. package/dist/hooks/init/init-container.d.ts +1 -1
  38. package/dist/hooks/init/init-container.js +2 -3
  39. package/dist/hooks/init/lock-config.d.ts +3 -0
  40. package/dist/hooks/init/lock-config.js +19 -0
  41. package/dist/hooks/prerun/auth.d.ts +3 -0
  42. package/dist/hooks/prerun/auth.js +70 -0
  43. package/dist/index.d.ts +1 -1
  44. package/dist/index.js +1 -1
  45. package/dist/interfaces/account-manager.interface.d.ts +1 -1
  46. package/dist/interfaces/config-manager.interface.d.ts +2 -2
  47. package/dist/lib/cli-instance-lock.d.ts +2 -0
  48. package/dist/lib/cli-instance-lock.js +37 -0
  49. package/dist/lib/container.d.ts +6 -4
  50. package/dist/lib/container.js +108 -38
  51. package/dist/logger.d.ts +1 -1
  52. package/dist/logger.js +4 -1
  53. package/dist/managers/account-manager.d.ts +7 -8
  54. package/dist/managers/account-manager.js +20 -45
  55. package/dist/managers/config-file-manager.d.ts +6 -7
  56. package/dist/managers/config-file-manager.js +35 -32
  57. package/dist/managers/config-manager.d.ts +5 -5
  58. package/dist/managers/config-manager.js +5 -2
  59. package/dist/middlewares/auth-middleware.d.ts +1 -1
  60. package/dist/services/auth.service.d.ts +10 -15
  61. package/dist/services/auth.service.js +71 -5
  62. package/dist/services/storage.service.d.ts +3 -3
  63. package/dist/services/storage.service.js +46 -19
  64. package/dist/utils/helper.d.ts +1 -0
  65. package/dist/utils/helper.js +15 -1
  66. package/dist/utils/prompt-flags.d.ts +20 -0
  67. package/dist/utils/prompt-flags.js +121 -0
  68. package/dist/utils/prompt.service.d.ts +16 -0
  69. package/dist/utils/prompt.service.js +75 -0
  70. package/dist/utils/tty.d.ts +1 -0
  71. package/dist/utils/tty.js +3 -0
  72. package/oclif.manifest.json +206 -242
  73. package/package.json +17 -24
  74. package/dist/commands/auth/login.d.ts +0 -12
  75. package/dist/commands/auth/login.js +0 -50
  76. package/dist/commands/auth/me.d.ts +0 -5
  77. package/dist/commands/auth/me.js +0 -16
  78. package/dist/commands/config/add.d.ts +0 -13
  79. package/dist/commands/config/add.js +0 -73
  80. package/dist/commands/config/base.d.ts +0 -16
  81. package/dist/commands/config/base.js +0 -78
  82. package/dist/commands/config/create.d.ts +0 -11
  83. package/dist/commands/config/create.js +0 -41
  84. package/dist/commands/config/delete.d.ts +0 -10
  85. package/dist/commands/config/delete.js +0 -25
  86. package/dist/commands/config/index.d.ts +0 -6
  87. package/dist/commands/config/index.js +0 -22
  88. package/dist/commands/config/list.d.ts +0 -6
  89. package/dist/commands/config/show.d.ts +0 -6
  90. package/dist/commands/config/show.js +0 -10
  91. package/dist/commands/config/use.d.ts +0 -6
  92. package/dist/commands/config/use.js +0 -27
@@ -0,0 +1,5 @@
1
+ import { releaseCliInstanceLock } from '../../lib/cli-instance-lock.js';
2
+ const hook = async () => {
3
+ await releaseCliInstanceLock();
4
+ };
5
+ export default hook;
@@ -1,3 +1,3 @@
1
- import { Hook } from '@oclif/core';
1
+ import type { Hook } from '@oclif/core';
2
2
  declare const hook: Hook<'init'>;
3
3
  export default hook;
@@ -1,6 +1,5 @@
1
1
  import { AppContainer } from '../../lib/container.js';
2
- const hook = async function (opts) {
3
- AppContainer
4
- .initialize(process.cwd(), opts.config);
2
+ const hook = async (opts) => {
3
+ AppContainer.initialize(process.cwd(), opts.config);
5
4
  };
6
5
  export default hook;
@@ -0,0 +1,3 @@
1
+ import type { Hook } from '@oclif/core';
2
+ declare const hook: Hook<'init'>;
3
+ export default hook;
@@ -0,0 +1,19 @@
1
+ import path from 'node:path';
2
+ import { mkdirSync } from 'node:fs';
3
+ import { acquireCliInstanceLock } from '../../lib/cli-instance-lock.js';
4
+ import { ConfigFileManager } from '../../managers/config-file-manager.js';
5
+ const hook = async (opts) => {
6
+ const { context, config } = opts;
7
+ const lockFile = path.join(config.configDir, ConfigFileManager.CONFIG_FILE);
8
+ mkdirSync(config.configDir, { recursive: true });
9
+ try {
10
+ await acquireCliInstanceLock(lockFile);
11
+ }
12
+ catch (error) {
13
+ if (context) {
14
+ context.error(error, { exit: 1 });
15
+ }
16
+ throw error;
17
+ }
18
+ };
19
+ export default hook;
@@ -0,0 +1,3 @@
1
+ import type { Hook } from '@oclif/core';
2
+ declare const hook: Hook<'prerun'>;
3
+ export default hook;
@@ -0,0 +1,70 @@
1
+ import { BaseCommand } from '../../commands/base.js';
2
+ import { AppContainer } from '../../lib/container.js';
3
+ import logger from '../../logger.js';
4
+ import { AuthService } from '../../services/auth.service.js';
5
+ const isBaseCommandClass = (commandClass) => commandClass.prototype instanceof BaseCommand;
6
+ const getConfigFlag = (commandClass, argv) => {
7
+ if (!isBaseCommandClass(commandClass)) {
8
+ return undefined;
9
+ }
10
+ //Fix Parser.parse throws error Nonexistent flag we dont have the way to ignore it
11
+ for (let index = 0; index < argv.length; index += 1) {
12
+ const token = argv[index];
13
+ if (!token) {
14
+ continue;
15
+ }
16
+ if (token === '--config') {
17
+ const value = argv[index + 1];
18
+ return value && !value.startsWith('-') ? value : undefined;
19
+ }
20
+ if (token.startsWith('--config=')) {
21
+ const value = token.slice('--config='.length);
22
+ return value || undefined;
23
+ }
24
+ }
25
+ return undefined;
26
+ };
27
+ const hook = async (opts) => {
28
+ const { Command, config, context } = opts;
29
+ const commandId = Command.id;
30
+ const CommandClass = Command;
31
+ if (CommandClass.authenticate === false) {
32
+ return;
33
+ }
34
+ const pinoLogger = logger.getPino();
35
+ pinoLogger.info('Prerun hook auth called');
36
+ try {
37
+ const configFile = getConfigFlag(CommandClass, opts.argv ?? []);
38
+ const container = configFile
39
+ ? AppContainer.container.setupRuntimeConfig({ configFile })
40
+ : AppContainer.container;
41
+ await container
42
+ .initConfigFileManager()
43
+ .initConfigManager()
44
+ .initAccountManager()
45
+ .initProviderClient({ enableAuth: false, enableCookies: true })
46
+ .build();
47
+ const { configManager, accountManager, providerClient } = container;
48
+ const credentials = await configManager.get('auth');
49
+ if (!credentials?.accessKey) {
50
+ if (!process.stdin.isTTY || !process.stdout.isTTY) {
51
+ context.error('Authorization not found. Please run: sp account login', { exit: 1 });
52
+ }
53
+ const args = configFile ? ['--config', configFile] : [];
54
+ pinoLogger.info({ commandId }, 'Authorization not found, redirecting to account:login');
55
+ await config.runCommand('account:login', args);
56
+ }
57
+ const authService = new AuthService(accountManager, configManager, providerClient, pinoLogger);
58
+ await authService.auth();
59
+ pinoLogger.debug({ commandId }, 'Authentication successful');
60
+ }
61
+ catch (error) {
62
+ if (error instanceof Error && error.message.includes('not initialized')) {
63
+ pinoLogger.debug({ commandId, err: error }, 'Container not initialized, skipping auth');
64
+ return;
65
+ }
66
+ pinoLogger.error({ commandId, err: error }, 'Authentication failed in prerun hook');
67
+ throw error;
68
+ }
69
+ };
70
+ export default hook;
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { Interfaces, run as oclifRun } from '@oclif/core';
1
+ import { type Interfaces, run as oclifRun } from '@oclif/core';
2
2
  export declare const run: (argv: string[], loadOptions?: Interfaces.LoadOptions | URL) => ReturnType<typeof oclifRun>;
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
- import { run as oclifRun } from '@oclif/core';
2
1
  import { fileURLToPath } from 'node:url';
2
+ import { run as oclifRun } from '@oclif/core';
3
3
  import logger from './logger.js';
4
4
  const normalizeLoadOptions = (loadOptions) => {
5
5
  if (!loadOptions) {
@@ -1,4 +1,4 @@
1
- import { IManager } from './manager.interface.js';
1
+ import type { IManager } from './manager.interface.js';
2
2
  export interface IAccountManager extends IManager {
3
3
  createSign(nonce: string): Promise<string>;
4
4
  getAddress(): string;
@@ -1,5 +1,5 @@
1
- import { CliConfig } from '../config/config.schema.js';
2
- import { IManager } from './manager.interface.js';
1
+ import type { CliConfig } from '../config/config.schema.js';
2
+ import type { IManager } from './manager.interface.js';
3
3
  export interface IConfigManager extends IManager {
4
4
  get<K extends keyof CliConfig, V extends CliConfig[K]>(key: K): Promise<undefined | V>;
5
5
  load(): Promise<CliConfig>;
@@ -0,0 +1,2 @@
1
+ export declare const acquireCliInstanceLock: (lockTarget: string) => Promise<void>;
2
+ export declare const releaseCliInstanceLock: () => Promise<void>;
@@ -0,0 +1,37 @@
1
+ import { lock } from 'proper-lockfile';
2
+ let release;
3
+ let locked = false;
4
+ const isLockHeldError = (error) => Boolean(error &&
5
+ typeof error === 'object' &&
6
+ 'code' in error &&
7
+ error.code === 'ELOCKED');
8
+ export const acquireCliInstanceLock = async (lockTarget) => {
9
+ if (locked) {
10
+ return;
11
+ }
12
+ try {
13
+ release = await lock(lockTarget, {
14
+ realpath: false,
15
+ retries: 0,
16
+ stale: 30_000,
17
+ });
18
+ locked = true;
19
+ }
20
+ catch (error) {
21
+ if (isLockHeldError(error)) {
22
+ const lockHeldError = Object.assign(new Error('Another instance of spcli is already running.'), { code: 'ELOCKED' });
23
+ throw lockHeldError;
24
+ }
25
+ throw error;
26
+ }
27
+ };
28
+ export const releaseCliInstanceLock = async () => {
29
+ if (!release) {
30
+ locked = false;
31
+ return;
32
+ }
33
+ const currentRelease = release;
34
+ release = undefined;
35
+ locked = false;
36
+ await currentRelease();
37
+ };
@@ -1,5 +1,5 @@
1
- import { Config } from '@oclif/core';
2
- import { ProviderClient } from '@super-protocol/provider-client';
1
+ import type { Config } from '@oclif/core';
2
+ import { type ProviderClient } from '@super-protocol/provider-client';
3
3
  import { AccountManager, ConfigFileManager, ConfigManager } from '../managers/index.js';
4
4
  interface RuntimeConfig {
5
5
  configFile?: string;
@@ -12,9 +12,10 @@ export declare class AppContainer {
12
12
  private _configFileManager?;
13
13
  private _configManager?;
14
14
  private _providerClient?;
15
- private initPromises;
15
+ private providerClientOptions?;
16
16
  private readonly initQueue;
17
17
  private logger;
18
+ private providerClientInitToken;
18
19
  private runtimeConfigFile?;
19
20
  private constructor();
20
21
  static initialize(currentDir: string, config: Config): AppContainer;
@@ -28,12 +29,13 @@ export declare class AppContainer {
28
29
  initBlockchainConnector(): this;
29
30
  initConfigFileManager(rebuild?: boolean): this;
30
31
  initConfigManager(rebuild?: boolean): this;
31
- initProviderClient({ enableAuth, enableCookies, rebuild }?: {
32
+ initProviderClient({ enableAuth, enableCookies, rebuild, }?: {
32
33
  enableAuth?: boolean;
33
34
  enableCookies?: boolean;
34
35
  rebuild?: boolean;
35
36
  }): this;
36
37
  setupRuntimeConfig(config: RuntimeConfig): this;
38
+ private getProviderClientForBootstrap;
37
39
  private queueInit;
38
40
  private waitFor;
39
41
  }
@@ -1,9 +1,9 @@
1
+ import path from 'node:path';
1
2
  import { createProviderClient, loggerMiddleware, requestIdMiddleware, } from '@super-protocol/provider-client';
2
3
  import { BlockchainConnector } from '@super-protocol/sdk-js';
3
- import path from 'node:path';
4
4
  import { PROVIDER_URL } from '../constants.js';
5
5
  import logger from '../logger.js';
6
- import { AccountManager, ConfigFileManager, ConfigManager, } from '../managers/index.js';
6
+ import { AccountManager, ConfigFileManager, ConfigManager } from '../managers/index.js';
7
7
  import { createAuthMiddleware } from '../middlewares/auth-middleware.js';
8
8
  import { createCookieMiddleware } from '../middlewares/cookies-middleware.js';
9
9
  export class AppContainer {
@@ -14,9 +14,10 @@ export class AppContainer {
14
14
  _configFileManager;
15
15
  _configManager;
16
16
  _providerClient;
17
- initPromises = [];
17
+ providerClientOptions;
18
18
  initQueue = new Map();
19
19
  logger;
20
+ providerClientInitToken = 0;
20
21
  runtimeConfigFile;
21
22
  constructor(cwdDir, oclifConfig) {
22
23
  this.cwdDir = cwdDir;
@@ -60,16 +61,19 @@ export class AppContainer {
60
61
  return this._providerClient;
61
62
  }
62
63
  async build() {
63
- await Promise.all(this.initPromises);
64
+ await Promise.all(this.initQueue.values());
64
65
  return this;
65
66
  }
66
67
  initAccountManager(rebuild = true) {
67
68
  this.initConfigManager(rebuild);
68
69
  return this.queueInit('accountManager', async () => {
69
- await this.waitFor('configManager');
70
- if (this._accountManager) {
70
+ await this.waitFor('configManager', { required: true });
71
+ if (this._accountManager && !rebuild) {
71
72
  return;
72
73
  }
74
+ if (rebuild) {
75
+ this._accountManager = undefined;
76
+ }
73
77
  this.logger.info('Initializing account manager');
74
78
  const accountManager = new AccountManager(this.configManager, this.logger);
75
79
  await accountManager.init();
@@ -79,11 +83,11 @@ export class AppContainer {
79
83
  initBlockchainConnector() {
80
84
  this.initConfigManager();
81
85
  return this.queueInit('blockchainConnector', async () => {
82
- await this.waitFor('configManager');
86
+ await this.waitFor('configManager', { required: true });
83
87
  let blockchainConfig = await this.configManager.get('blockchain');
84
88
  if (!blockchainConfig) {
85
- await this.waitFor('providerClient');
86
- const { data, error } = await this.providerClient.GET('/api/config');
89
+ const client = await this.getProviderClientForBootstrap();
90
+ const { data, error } = await client.GET('/api/config');
87
91
  if (error || !data) {
88
92
  throw new Error('Could not get configuration from provider');
89
93
  }
@@ -99,9 +103,12 @@ export class AppContainer {
99
103
  }
100
104
  initConfigFileManager(rebuild = false) {
101
105
  return this.queueInit('configFileManager', async () => {
102
- if (this._configFileManager) {
106
+ if (this._configFileManager && !rebuild) {
103
107
  return;
104
108
  }
109
+ if (rebuild) {
110
+ this._configFileManager = undefined;
111
+ }
105
112
  const configFileManager = new ConfigFileManager(this.oclifConfig.configDir, this.logger, {
106
113
  runtimeConfigFile: this.runtimeConfigFile,
107
114
  });
@@ -112,10 +119,13 @@ export class AppContainer {
112
119
  initConfigManager(rebuild = false) {
113
120
  this.initConfigFileManager(rebuild);
114
121
  return this.queueInit('configManager', async () => {
115
- await this.waitFor('configFileManager');
116
- if (this._configManager) {
122
+ await this.waitFor('configFileManager', { required: true });
123
+ if (this._configManager && !rebuild) {
117
124
  return;
118
125
  }
126
+ if (rebuild) {
127
+ this._configManager = undefined;
128
+ }
119
129
  const configFilePath = await this.configFileManager.getCurrentConfigPath();
120
130
  if (!configFilePath) {
121
131
  throw new Error('Config file not found');
@@ -126,14 +136,30 @@ export class AppContainer {
126
136
  this._configManager = configManager;
127
137
  }, rebuild);
128
138
  }
129
- initProviderClient({ enableAuth = true, enableCookies = true, rebuild = false } = {}) {
139
+ initProviderClient({ enableAuth = true, enableCookies = true, rebuild = false, } = {}) {
130
140
  this.initConfigManager();
141
+ const options = { enableAuth, enableCookies };
142
+ const optionsChanged = this.providerClientOptions &&
143
+ (this.providerClientOptions.enableAuth !== options.enableAuth ||
144
+ this.providerClientOptions.enableCookies !== options.enableCookies);
145
+ if (optionsChanged) {
146
+ rebuild = true;
147
+ this.logger.debug({ newOptions: options, oldOptions: this.providerClientOptions }, 'Provider client options changed, rebuilding');
148
+ }
149
+ const shouldSchedule = rebuild || !this.initQueue.has('providerClient');
150
+ const initToken = shouldSchedule
151
+ ? ++this.providerClientInitToken
152
+ : this.providerClientInitToken;
131
153
  return this.queueInit('providerClient', async () => {
132
154
  if (this._providerClient && !rebuild) {
133
155
  return;
134
156
  }
135
- await this.waitFor('configManager');
136
- const providerUrl = await this.configManager.get('providerUrl') || PROVIDER_URL;
157
+ if (rebuild && this._providerClient) {
158
+ this.logger.debug('Clearing existing provider client for rebuild');
159
+ this._providerClient = undefined;
160
+ }
161
+ await this.waitFor('configManager', { required: true });
162
+ const providerUrl = (await this.configManager.get('providerUrl')) || PROVIDER_URL;
137
163
  this.logger.info('Initializing provider client');
138
164
  const providerClient = createProviderClient({
139
165
  baseUrl: providerUrl,
@@ -150,28 +176,28 @@ export class AppContainer {
150
176
  const authMiddleware = await createAuthMiddleware(this.configManager, providerClient, this.logger);
151
177
  providerClient.use(authMiddleware);
152
178
  }
153
- providerClient.use({
154
- onRequest: async ({ request }) => {
155
- const req = request.clone();
156
- const readBody = await req.body?.getReader().read();
157
- let body;
158
- if (readBody && readBody.value) {
159
- body = Buffer.from(readBody.value).toString('utf8');
160
- }
161
- this.logger.debug({
162
- body,
163
- token: req.headers.get('Authorization'),
164
- url: req.url,
165
- }, 'Requesting');
166
- },
167
- });
179
+ if (process.env.SP_CLI_DEBUG_REQUESTS === '1') {
180
+ providerClient.use({
181
+ onRequest: async ({ request }) => {
182
+ this.logger.debug({
183
+ hasAuthHeader: Boolean(request.headers.get('Authorization')),
184
+ hasBody: Boolean(request.body),
185
+ method: request.method,
186
+ url: request.url,
187
+ }, 'Requesting');
188
+ },
189
+ });
190
+ }
191
+ if (initToken !== this.providerClientInitToken) {
192
+ this.logger.debug({ initToken, providerClientInitToken: this.providerClientInitToken }, 'Skipping provider client assignment (superseded)');
193
+ return;
194
+ }
168
195
  this._providerClient = providerClient;
196
+ this.providerClientOptions = options;
169
197
  }, rebuild);
170
198
  }
171
199
  setupRuntimeConfig(config) {
172
- if (this.initQueue.size > 0) {
173
- throw new Error('Cannot change runtime config after initialization has started');
174
- }
200
+ const previousRuntimeConfigFile = this.runtimeConfigFile;
175
201
  if (config.configFile) {
176
202
  this.runtimeConfigFile = path.isAbsolute(config.configFile)
177
203
  ? config.configFile
@@ -180,18 +206,62 @@ export class AppContainer {
180
206
  else {
181
207
  this.runtimeConfigFile = undefined;
182
208
  }
209
+ const changed = previousRuntimeConfigFile !== this.runtimeConfigFile;
210
+ if (changed) {
211
+ this.logger.debug({ previousRuntimeConfigFile, runtimeConfigFile: this.runtimeConfigFile }, 'Runtime config changed; clearing dependent services');
212
+ this._configManager = undefined;
213
+ this._accountManager = undefined;
214
+ this._providerClient = undefined;
215
+ this.providerClientOptions = undefined;
216
+ this.initQueue.delete('configManager');
217
+ this.initQueue.delete('accountManager');
218
+ this.initQueue.delete('providerClient');
219
+ this.initQueue.delete('blockchainConnector');
220
+ this.initQueue.delete('configFileManager');
221
+ }
222
+ if (this._configFileManager) {
223
+ this._configFileManager.setRuntimeConfigFile(this.runtimeConfigFile);
224
+ }
183
225
  return this;
184
226
  }
227
+ async getProviderClientForBootstrap() {
228
+ if (this._providerClient) {
229
+ return this._providerClient;
230
+ }
231
+ const queued = this.initQueue.get('providerClient');
232
+ if (queued) {
233
+ await queued;
234
+ return this.providerClient;
235
+ }
236
+ await this.waitFor('configManager', { required: true });
237
+ const providerUrl = (await this.configManager.get('providerUrl')) || PROVIDER_URL;
238
+ this.logger.debug({ providerUrl }, 'Creating bootstrap provider client');
239
+ return createProviderClient({
240
+ baseUrl: providerUrl,
241
+ logger: this.logger,
242
+ middlewares: [requestIdMiddleware, loggerMiddleware],
243
+ });
244
+ }
185
245
  queueInit(name, task, force = false) {
186
- if (!this.initQueue.has(name) || force) {
187
- const promise = task();
188
- this.initQueue.set(name, promise);
189
- this.initPromises.push(promise);
246
+ if (force) {
247
+ this.initQueue.delete(name);
248
+ }
249
+ if (!this.initQueue.has(name)) {
250
+ const wrappedPromise = task().catch((error) => {
251
+ if (this.initQueue.get(name) === wrappedPromise) {
252
+ this.initQueue.delete(name);
253
+ }
254
+ throw error;
255
+ });
256
+ this.initQueue.set(name, wrappedPromise);
190
257
  }
191
258
  return this;
192
259
  }
193
- async waitFor(name) {
260
+ async waitFor(name, options = {}) {
194
261
  const promise = this.initQueue.get(name);
262
+ if (!promise && options.required) {
263
+ throw new Error(`Dependency "${name}" was not queued for initialization`);
264
+ }
195
265
  if (promise) {
196
266
  await promise;
197
267
  }
package/dist/logger.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { Interfaces } from '@oclif/core';
1
+ import type { Interfaces } from '@oclif/core';
2
2
  import pino from 'pino';
3
3
  declare const _default: Interfaces.Logger & {
4
4
  getPino(): pino.Logger;
package/dist/logger.js CHANGED
@@ -43,7 +43,10 @@ class PinoLogger {
43
43
  this.forward('warn', formatter, args);
44
44
  }
45
45
  forward(level, formatter, args) {
46
- this.logger[level].apply(this.logger, [formatter, ...args]);
46
+ this.logger[level].apply(this.logger, [
47
+ formatter,
48
+ ...args,
49
+ ]);
47
50
  }
48
51
  }
49
52
  const logger = (namespace) => {
@@ -1,7 +1,7 @@
1
- import pino from 'pino';
2
- import { Account } from '../config/config.schema.js';
3
- import { IAccountManager } from '../interfaces/account-manager.interface.js';
4
- import { IConfigManager } from '../interfaces/config-manager.interface.js';
1
+ import type pino from 'pino';
2
+ import { type Account } from '../config/config.schema.js';
3
+ import type { IAccountManager } from '../interfaces/account-manager.interface.js';
4
+ import type { IConfigManager } from '../interfaces/config-manager.interface.js';
5
5
  export declare class AccountManager implements IAccountManager {
6
6
  private readonly configManager;
7
7
  private readonly logger;
@@ -12,9 +12,8 @@ export declare class AccountManager implements IAccountManager {
12
12
  createFromKey(privateKey: string): Promise<Account>;
13
13
  createSign(nonce: string): Promise<string>;
14
14
  getAddress(): string;
15
+ hasAccount(): Promise<boolean>;
15
16
  init(): Promise<void>;
16
- interactWithUser(): Promise<{
17
- address: string;
18
- privateKey: string;
19
- }>;
17
+ private loadAccount;
18
+ private save;
20
19
  }
@@ -1,16 +1,7 @@
1
- var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
2
- var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
3
- if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
4
- else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
5
- return c > 3 && r && Object.defineProperty(target, key, r), r;
6
- };
7
- import { confirm, isCancel, password } from '@clack/prompts';
8
- import { ux } from '@oclif/core';
9
1
  import { Value } from 'typebox/value';
10
- import { Retryable } from 'typescript-retry-decorator';
11
2
  import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
12
3
  import { accountSchema } from '../config/config.schema.js';
13
- import { NotInitializedError } from '../errors.js';
4
+ import { AccountNotFoundError, NotInitializedError } from '../errors.js';
14
5
  export class AccountManager {
15
6
  configManager;
16
7
  logger;
@@ -27,7 +18,7 @@ export class AccountManager {
27
18
  address: account.address,
28
19
  privateKey: this.privateKey,
29
20
  };
30
- await this.configManager.set('account', this.account);
21
+ await this.save();
31
22
  this.logger.debug('Created new account');
32
23
  return this.account;
33
24
  }
@@ -38,7 +29,7 @@ export class AccountManager {
38
29
  address: account.address,
39
30
  privateKey: this.privateKey,
40
31
  };
41
- await this.configManager.set('account', this.account);
32
+ await this.save();
42
33
  this.logger.debug('Created new account from key');
43
34
  return this.account;
44
35
  }
@@ -57,48 +48,32 @@ export class AccountManager {
57
48
  }
58
49
  return this.account.address;
59
50
  }
60
- async init() {
51
+ async hasAccount() {
61
52
  const account = await this.configManager.get('account');
53
+ return Value.Check(accountSchema, account);
54
+ }
55
+ async init() {
62
56
  try {
63
- this.account = Value.Parse(accountSchema, account);
64
- this.privateKey = this.account.privateKey;
57
+ await this.loadAccount();
65
58
  }
66
59
  catch (error) {
67
- this.logger.warn({ err: error }, 'Incorrect config file');
68
- await this.interactWithUser();
60
+ if (!(error instanceof AccountNotFoundError)) {
61
+ this.logger.warn({ err: error }, 'Unexpected error loading account');
62
+ }
69
63
  }
70
64
  }
71
- async interactWithUser() {
72
- ux.warn("Account doesn't exist in current configuration");
73
- const ask = await confirm({
74
- message: 'Do you want to create a new account?',
75
- });
76
- if (isCancel(ask)) {
77
- ux.error('Operation cancelled.', { exit: 1 });
78
- }
79
- if (ask) {
80
- const account = await this.create();
81
- ux.stdout('Generated new keyPair for new account.');
82
- return account;
83
- }
84
- const privateKeyInput = await password({ message: 'Please input your privateKey for using in account' });
85
- if (isCancel(privateKeyInput)) {
86
- ux.error('Operation cancelled.', { exit: 1 });
87
- }
88
- const privateKey = privateKeyInput.trim();
65
+ async loadAccount() {
66
+ const account = await this.configManager.get('account');
89
67
  try {
90
- const account = await this.createFromKey(privateKey);
91
- ux.stdout('Your privateKey will be used in config. Keypair created');
92
- return account;
68
+ this.account = Value.Parse(accountSchema, account);
69
+ this.privateKey = this.account.privateKey;
93
70
  }
94
71
  catch (error) {
95
- ux.warn(error);
96
- ux.error('Invalid private key format. Please try again.');
72
+ this.logger.warn({ err: error }, 'Incorrect config file');
73
+ throw new AccountNotFoundError('Account not found in config file');
97
74
  }
98
75
  }
76
+ async save() {
77
+ await this.configManager.set('account', this.account);
78
+ }
99
79
  }
100
- __decorate([
101
- Retryable({
102
- maxAttempts: 3,
103
- })
104
- ], AccountManager.prototype, "interactWithUser", null);
@@ -1,8 +1,8 @@
1
1
  import { confirm, select } from '@clack/prompts';
2
2
  import { ux } from '@oclif/core';
3
- import pino from 'pino';
4
- import { CliConfig } from '../config/config.schema.js';
5
- import { IManager } from '../interfaces/manager.interface.js';
3
+ import type pino from 'pino';
4
+ import { type Account, type CliConfig } from '../config/config.schema.js';
5
+ import type { IManager } from '../interfaces/manager.interface.js';
6
6
  type ConfirmPrompt = typeof confirm;
7
7
  type SelectPrompt = typeof select;
8
8
  type UxLike = Pick<typeof ux, 'error' | 'stdout'>;
@@ -14,7 +14,7 @@ interface ConfigFileManagerOptions {
14
14
  }
15
15
  export declare class ConfigFileManager implements IManager {
16
16
  private readonly logger;
17
- private static readonly CONFIG_FILE;
17
+ static readonly CONFIG_FILE = "config.json";
18
18
  private static readonly CONFIGS_DIR;
19
19
  private readonly configDir;
20
20
  private readonly configFilePath;
@@ -25,8 +25,7 @@ export declare class ConfigFileManager implements IManager {
25
25
  private readonly selectPrompt;
26
26
  private readonly ux;
27
27
  constructor(configDir: string, logger: pino.BaseLogger, options?: ConfigFileManagerOptions);
28
- addConfig(configName: string): Promise<void>;
29
- createConfig(configFileName: string, name: string, url?: string): Promise<void>;
28
+ createConfig(configFileName: string, name: string, url?: string, account?: Account): Promise<void>;
30
29
  deleteConfig(configName: string): Promise<void>;
31
30
  deleteConfigByName(name?: string, force?: boolean): Promise<void>;
32
31
  getConfigData(configName: string): CliConfig | undefined;
@@ -40,7 +39,7 @@ export declare class ConfigFileManager implements IManager {
40
39
  getCurrentConfigPath(): Promise<string>;
41
40
  importConfig(sourcePath: string, targetName?: string): Promise<string>;
42
41
  init(): Promise<void>;
43
- removeConfig(configName: string): Promise<void>;
42
+ private removeConfig;
44
43
  setCurrentConfig(configName: string): Promise<void>;
45
44
  setRuntimeConfigFile(runtimeConfigFile?: string): void;
46
45
  private ensureDirectories;