@super-protocol/sp-cli 0.0.6 → 0.0.7

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 (83) hide show
  1. package/README.md +97 -229
  2. package/dist/commands/account/base.d.ts +20 -0
  3. package/dist/commands/account/base.js +49 -0
  4. package/dist/commands/{config/delete.d.ts → account/forget.d.ts} +5 -2
  5. package/dist/commands/{config/delete.js → account/forget.js} +12 -5
  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 +254 -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 +3 -1
  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/create.js +1 -1
  22. package/dist/commands/storage/select.js +3 -5
  23. package/dist/commands/storage/show.js +2 -4
  24. package/dist/commands/storage/update.js +31 -14
  25. package/dist/commands/workflows/extend-lease.d.ts +1 -1
  26. package/dist/commands/workflows/extend-lease.js +19 -10
  27. package/dist/config/config-file.schema.d.ts +1 -2
  28. package/dist/config/config-file.schema.js +0 -1
  29. package/dist/config/config.schema.d.ts +1 -1
  30. package/dist/errors.d.ts +2 -0
  31. package/dist/errors.js +2 -0
  32. package/dist/hooks/finally/shutdown-blockchain.d.ts +1 -1
  33. package/dist/hooks/finally/shutdown-blockchain.js +1 -1
  34. package/dist/hooks/finally/unlock-config.d.ts +3 -0
  35. package/dist/hooks/finally/unlock-config.js +5 -0
  36. package/dist/hooks/init/init-container.d.ts +1 -1
  37. package/dist/hooks/init/init-container.js +2 -3
  38. package/dist/hooks/init/lock-config.d.ts +3 -0
  39. package/dist/hooks/init/lock-config.js +19 -0
  40. package/dist/hooks/prerun/auth.d.ts +3 -0
  41. package/dist/hooks/prerun/auth.js +70 -0
  42. package/dist/index.d.ts +1 -1
  43. package/dist/index.js +1 -1
  44. package/dist/interfaces/account-manager.interface.d.ts +1 -1
  45. package/dist/interfaces/config-manager.interface.d.ts +2 -2
  46. package/dist/lib/cli-instance-lock.d.ts +2 -0
  47. package/dist/lib/cli-instance-lock.js +37 -0
  48. package/dist/lib/container.d.ts +6 -4
  49. package/dist/lib/container.js +108 -38
  50. package/dist/logger.d.ts +1 -1
  51. package/dist/logger.js +4 -1
  52. package/dist/managers/account-manager.d.ts +7 -8
  53. package/dist/managers/account-manager.js +20 -45
  54. package/dist/managers/config-file-manager.d.ts +6 -7
  55. package/dist/managers/config-file-manager.js +35 -32
  56. package/dist/managers/config-manager.d.ts +5 -5
  57. package/dist/managers/config-manager.js +5 -2
  58. package/dist/middlewares/auth-middleware.d.ts +1 -1
  59. package/dist/services/auth.service.d.ts +10 -15
  60. package/dist/services/auth.service.js +71 -5
  61. package/dist/services/storage.service.d.ts +3 -3
  62. package/dist/services/storage.service.js +46 -19
  63. package/dist/utils/helper.d.ts +1 -0
  64. package/dist/utils/helper.js +15 -1
  65. package/oclif.manifest.json +258 -399
  66. package/package.json +17 -24
  67. package/dist/commands/auth/login.d.ts +0 -12
  68. package/dist/commands/auth/login.js +0 -50
  69. package/dist/commands/auth/me.d.ts +0 -5
  70. package/dist/commands/auth/me.js +0 -16
  71. package/dist/commands/config/add.d.ts +0 -13
  72. package/dist/commands/config/add.js +0 -73
  73. package/dist/commands/config/base.d.ts +0 -16
  74. package/dist/commands/config/base.js +0 -78
  75. package/dist/commands/config/create.d.ts +0 -11
  76. package/dist/commands/config/create.js +0 -41
  77. package/dist/commands/config/index.d.ts +0 -6
  78. package/dist/commands/config/index.js +0 -22
  79. package/dist/commands/config/list.d.ts +0 -6
  80. package/dist/commands/config/show.d.ts +0 -6
  81. package/dist/commands/config/show.js +0 -10
  82. package/dist/commands/config/use.d.ts +0 -6
  83. package/dist/commands/config/use.js +0 -27
@@ -1,7 +1,7 @@
1
- import { ProviderClient } from '@super-protocol/provider-client';
2
- import pino from 'pino';
3
- import { AccountManager } from '../managers/account-manager.js';
4
- import { ConfigManager } from '../managers/config-manager.js';
1
+ import type { ProviderClient } from '@super-protocol/provider-client';
2
+ import type pino from 'pino';
3
+ import type { AccountManager } from '../managers/account-manager.js';
4
+ import type { ConfigManager } from '../managers/config-manager.js';
5
5
  export declare class AuthError extends Error {
6
6
  }
7
7
  export declare class AuthService {
@@ -10,15 +10,10 @@ export declare class AuthService {
10
10
  private readonly providerClient;
11
11
  private readonly logger;
12
12
  constructor(accountManager: AccountManager, configManager: ConfigManager, providerClient: ProviderClient, logger: pino.BaseLogger);
13
- auth(): Promise<void>;
14
- getTokens(nonce: string): Promise<void>;
15
- getUserNonce(address: string): Promise<{
16
- error: {
17
- message: string;
18
- statusCode: number;
19
- } | undefined;
20
- user: {
21
- nonce?: string;
22
- } | undefined;
23
- }>;
13
+ private hasValidRefreshCookie;
14
+ auth(options?: {
15
+ force?: boolean;
16
+ }): Promise<void>;
17
+ private getTokens;
18
+ private getUserNonce;
24
19
  }
@@ -1,5 +1,31 @@
1
+ import { decode } from 'jsonwebtoken';
2
+ import { CookieJar } from 'tough-cookie';
3
+ import { PROVIDER_URL } from '../constants.js';
1
4
  export class AuthError extends Error {
2
5
  }
6
+ function isAccessKeyExpired(accessKey) {
7
+ try {
8
+ const payload = decode(accessKey);
9
+ if (!payload || typeof payload.exp !== 'number') {
10
+ return true;
11
+ }
12
+ const now = Math.floor(Date.now() / 1000);
13
+ return now >= payload.exp;
14
+ }
15
+ catch {
16
+ return true;
17
+ }
18
+ }
19
+ function isCookieExpired(cookie) {
20
+ if (!cookie.expires || cookie.expires === 'Infinity') {
21
+ return false;
22
+ }
23
+ const expiresAt = cookie.expires.getTime();
24
+ if (!Number.isFinite(expiresAt)) {
25
+ return true;
26
+ }
27
+ return Date.now() >= expiresAt;
28
+ }
3
29
  export class AuthService {
4
30
  accountManager;
5
31
  configManager;
@@ -11,7 +37,45 @@ export class AuthService {
11
37
  this.providerClient = providerClient;
12
38
  this.logger = logger;
13
39
  }
14
- async auth() {
40
+ async hasValidRefreshCookie(baseUrl) {
41
+ let stored;
42
+ try {
43
+ stored = await this.configManager.getCookies();
44
+ }
45
+ catch (error) {
46
+ this.logger.warn({ err: error }, 'Failed to read cookies from config');
47
+ return false;
48
+ }
49
+ if (!stored || typeof stored !== 'object') {
50
+ return false;
51
+ }
52
+ try {
53
+ const jar = CookieJar.fromJSON(stored);
54
+ const cookies = await jar.getCookies(baseUrl, { expire: false });
55
+ return cookies.some((cookie) => cookie.key.toLowerCase().includes('refresh') && cookie.value && !isCookieExpired(cookie));
56
+ }
57
+ catch (error) {
58
+ this.logger.warn({ err: error }, 'Failed to parse cookies');
59
+ return false;
60
+ }
61
+ }
62
+ async auth(options = {}) {
63
+ if (!options.force) {
64
+ const credentials = await this.configManager.getCredentials();
65
+ const accessKey = credentials?.accessKey;
66
+ if (accessKey && !isAccessKeyExpired(accessKey)) {
67
+ this.logger.debug('Access key is not expired, skipping authorization');
68
+ return;
69
+ }
70
+ if (accessKey) {
71
+ const providerUrl = (await this.configManager.get('providerUrl')) || PROVIDER_URL;
72
+ const hasRefreshCookie = await this.hasValidRefreshCookie(providerUrl);
73
+ if (hasRefreshCookie) {
74
+ this.logger.debug('Access key expired but refresh cookie exists, skipping authorization');
75
+ return;
76
+ }
77
+ }
78
+ }
15
79
  const address = this.accountManager.getAddress();
16
80
  const { error: nonceError, user } = await this.getUserNonce(address);
17
81
  if (nonceError && nonceError.statusCode === 404) {
@@ -25,7 +89,7 @@ export class AuthService {
25
89
  this.logger.debug({ signUpError }, 'Error signing up');
26
90
  const { error: nonceError, user } = await this.getUserNonce(address);
27
91
  this.logger.debug({ nonceError, user }, 'Requesting nonce again');
28
- if (user && user.nonce) {
92
+ if (user?.nonce) {
29
93
  await this.getTokens(user.nonce);
30
94
  }
31
95
  else {
@@ -45,7 +109,7 @@ export class AuthService {
45
109
  await this.getTokens(nonce);
46
110
  }
47
111
  }
48
- else if (user && user.nonce) {
112
+ else if (user?.nonce) {
49
113
  this.logger.debug({ user }, 'Requesting for existed user');
50
114
  await this.getTokens(user.nonce);
51
115
  }
@@ -55,7 +119,7 @@ export class AuthService {
55
119
  }
56
120
  async getTokens(nonce) {
57
121
  const signature = await this.accountManager.createSign(nonce);
58
- const { data: tokenResponse, error, } = await this.providerClient.POST('/api/auth/token', {
122
+ const { data: tokenResponse, error } = await this.providerClient.POST('/api/auth/token', {
59
123
  body: {
60
124
  address: this.accountManager.getAddress(),
61
125
  provider: 'sp-cli',
@@ -71,7 +135,9 @@ export class AuthService {
71
135
  this.logger.error({ response: tokenResponse }, 'Unexpected token response');
72
136
  throw new AuthError('Provider returned an unexpected token response.');
73
137
  }
74
- await this.configManager.setCredentials({ accessKey: accessToken });
138
+ await this.configManager.setCredentials({
139
+ accessKey: accessToken,
140
+ });
75
141
  }
76
142
  async getUserNonce(address) {
77
143
  try {
@@ -1,6 +1,6 @@
1
- import { EncryptionWithIV, Resource, RuntimeInputInfo } from '@super-protocol/dto-js';
2
- import { AddStorageDto, ProviderClient, StorageResponseDto, UpdateStorageDto } from '@super-protocol/provider-client';
3
- import pino from 'pino';
1
+ import { type EncryptionWithIV, type Resource, type RuntimeInputInfo } from '@super-protocol/dto-js';
2
+ import { type AddStorageDto, type ProviderClient, type StorageResponseDto, type UpdateStorageDto } from '@super-protocol/provider-client';
3
+ import type pino from 'pino';
4
4
  export declare class StorageError extends Error {
5
5
  }
6
6
  export declare class StoragesUndefinedError extends StorageError {
@@ -4,11 +4,11 @@ var __decorate = (this && this.__decorate) || function (decorators, target, key,
4
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
5
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6
6
  };
7
+ import * as fs from 'node:fs/promises';
8
+ import { basename, dirname } from 'node:path';
7
9
  import { StorageResourceSchema, StorageType, } from '@super-protocol/dto-js';
8
10
  import { ResourceType, } from '@super-protocol/provider-client';
9
11
  import { download, upload } from '@super-protocol/sp-files-addon';
10
- import * as fs from 'node:fs/promises';
11
- import { basename, dirname } from 'node:path';
12
12
  import { Value } from 'typebox/value';
13
13
  import { Retryable } from 'typescript-retry-decorator';
14
14
  import { S3_ENDPOINT } from '../constants.js';
@@ -24,7 +24,9 @@ export class StorageUpdateError extends StorageError {
24
24
  }
25
25
  export class StorageGetError extends StorageError {
26
26
  }
27
- export const generateExternalId = () => Array.from({ length: 16 }).map(() => Math.floor(Math.random() * 16).toString(16)).join('');
27
+ export const generateExternalId = () => Array.from({ length: 16 })
28
+ .map(() => Math.floor(Math.random() * 16).toString(16))
29
+ .join('');
28
30
  export class StorageService {
29
31
  providerClient;
30
32
  logger;
@@ -38,7 +40,7 @@ export class StorageService {
38
40
  const { data, error } = await this.providerClient.POST('/api/storages', {
39
41
  body: storage,
40
42
  });
41
- if (error && error.message) {
43
+ if (error?.message) {
42
44
  this.logger.error({ err: error }, 'Failed to create storage');
43
45
  throw new StorageCreateError(error.message);
44
46
  }
@@ -51,12 +53,16 @@ export class StorageService {
51
53
  }
52
54
  catch (error) {
53
55
  this.logger.error({ err: error }, 'Storage creation failed');
54
- const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
56
+ const error_ = error instanceof StorageError
57
+ ? error
58
+ : new ProviderClientError('Request failed please try again later');
55
59
  throw error_;
56
60
  }
57
61
  }
58
62
  async download(params, progressCb) {
59
- const resourceFile = await readJsonFile({ path: params.resourcePath });
63
+ const resourceFile = await readJsonFile({
64
+ path: params.resourcePath,
65
+ });
60
66
  if (!Value.Check(StorageResourceSchema, resourceFile)) {
61
67
  throw new StorageError('Invalid resource file format, please verify resource.json against the schema.');
62
68
  }
@@ -90,7 +96,7 @@ export class StorageService {
90
96
  async getCentralizedStorage() {
91
97
  try {
92
98
  const storages = await this.requestStorages();
93
- const centralized = storages.find(storage => storage.isCentralized);
99
+ const centralized = storages.find((storage) => storage.isCentralized);
94
100
  if (!centralized) {
95
101
  const storage = await this.initCentralizedStorage();
96
102
  return storage;
@@ -114,7 +120,7 @@ export class StorageService {
114
120
  }
115
121
  const { activeStorageId } = data || {};
116
122
  const storages = await this.requestStorages();
117
- const storage = storages.find(storage => storage.id === activeStorageId);
123
+ const storage = storages.find((storage) => storage.id === activeStorageId);
118
124
  if (!storage) {
119
125
  throw new StorageError('Selected storage not found please select storage first');
120
126
  }
@@ -122,7 +128,9 @@ export class StorageService {
122
128
  }
123
129
  catch (error) {
124
130
  this.logger.error({ err: error }, 'Failed to get current storage');
125
- const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
131
+ const error_ = error instanceof StorageError
132
+ ? error
133
+ : new ProviderClientError('Request failed please try again later');
126
134
  throw error_;
127
135
  }
128
136
  }
@@ -146,7 +154,9 @@ export class StorageService {
146
154
  }
147
155
  catch (error) {
148
156
  this.logger.error({ err: error }, 'Storage initialization failed');
149
- const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
157
+ const error_ = error instanceof StorageError
158
+ ? error
159
+ : new ProviderClientError('Request failed please try again later');
150
160
  throw error_;
151
161
  }
152
162
  }
@@ -167,7 +177,9 @@ export class StorageService {
167
177
  }
168
178
  catch (error) {
169
179
  this.logger.error({ err: error }, 'Storage initialization failed');
170
- const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
180
+ const error_ = error instanceof StorageError
181
+ ? error
182
+ : new ProviderClientError('Request failed please try again later');
171
183
  throw error_;
172
184
  }
173
185
  }
@@ -190,7 +202,9 @@ export class StorageService {
190
202
  }
191
203
  catch (error) {
192
204
  this.logger.error({ err: error }, 'Failed to request storages');
193
- const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
205
+ const error_ = error instanceof StorageError
206
+ ? error
207
+ : new ProviderClientError('Request failed please try again later');
194
208
  throw error_;
195
209
  }
196
210
  }
@@ -219,7 +233,9 @@ export class StorageService {
219
233
  }
220
234
  catch (error) {
221
235
  this.logger.error({ err: error }, 'Failed to save selected storage');
222
- const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
236
+ const error_ = error instanceof StorageError
237
+ ? error
238
+ : new ProviderClientError('Request failed please try again later');
223
239
  throw error_;
224
240
  }
225
241
  }
@@ -243,13 +259,17 @@ export class StorageService {
243
259
  }
244
260
  catch (error) {
245
261
  this.logger.error({ err: error, storageId: id }, 'Failed to update storage');
246
- const error_ = error instanceof StorageError ? error : new ProviderClientError('Request failed please try again later');
262
+ const error_ = error instanceof StorageError
263
+ ? error
264
+ : new ProviderClientError('Request failed please try again later');
247
265
  throw error_;
248
266
  }
249
267
  }
250
268
  async upload(params, progressCb) {
251
269
  const storage = await this.getCurrentStorage();
252
- const remotePath = params.remotePath ? params.remotePath.replaceAll('..', '').replace(/^\/+/, '') : generateExternalId();
270
+ const remotePath = params.remotePath
271
+ ? params.remotePath.replaceAll('..', '').replace(/^\/+/, '')
272
+ : generateExternalId();
253
273
  const resourceFilePath = preparePath(params.outputPath ?? 'resource.json');
254
274
  try {
255
275
  await fs.stat(resourceFilePath);
@@ -262,7 +282,9 @@ export class StorageService {
262
282
  }
263
283
  let metadata = {};
264
284
  if (params.metadataPath) {
265
- metadata = await readJsonFile({ path: preparePath(params.metadataPath) });
285
+ metadata = await readJsonFile({
286
+ path: preparePath(params.metadataPath),
287
+ });
266
288
  }
267
289
  const { readCredentials, writeCredentials } = this.getStorageCredentials(storage, remotePath);
268
290
  const info = await fs.stat(params.localPath);
@@ -315,7 +337,8 @@ export class StorageService {
315
337
  bucket: storage.bucket,
316
338
  prefix: joinPaths(storage.prefix, remotePath),
317
339
  token: readAccessToken,
318
- }, writeCredentials: {
340
+ },
341
+ writeCredentials: {
319
342
  bucket: storage.bucket,
320
343
  prefix: joinPaths(storage.prefix, remotePath),
321
344
  token: writeAccessToken,
@@ -334,7 +357,8 @@ export class StorageService {
334
357
  endpoint: S3_ENDPOINT,
335
358
  prefix: joinPaths(storage.prefix, remotePath),
336
359
  secretKey: readSecretAccessKey,
337
- }, writeCredentials: {
360
+ },
361
+ writeCredentials: {
338
362
  accessKeyId: writeAccessKeyId,
339
363
  bucket: storage.bucket,
340
364
  endpoint: S3_ENDPOINT,
@@ -347,5 +371,8 @@ export class StorageService {
347
371
  }
348
372
  }
349
373
  __decorate([
350
- Retryable({ maxAttempts: 3, value: [StoragesUndefinedError, StorageCreateError, ProviderClientError] })
374
+ Retryable({
375
+ maxAttempts: 3,
376
+ value: [StoragesUndefinedError, StorageCreateError, ProviderClientError],
377
+ })
351
378
  ], StorageService.prototype, "getCentralizedStorage", null);
@@ -1,4 +1,5 @@
1
1
  export declare const getConfigName: (name: string) => string;
2
+ export declare const getConfigNameFromCredentials: (privateKey: string, url: string) => string;
2
3
  export declare const preparePath: (rawPath: string) => string;
3
4
  export declare const joinPaths: (...args: string[]) => string;
4
5
  export type ReadJsonFileParams = {
@@ -1,7 +1,21 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import { existsSync } from 'node:fs';
2
3
  import * as fs from 'node:fs/promises';
3
4
  import path from 'node:path';
4
- export const getConfigName = (name) => name.toLowerCase().replaceAll(/[^a-z0-9]+/g, '-') + '.config.json';
5
+ export const getConfigName = (name) => `${name.toLowerCase().replaceAll(/[^a-z0-9]+/g, '-')}.config.json`;
6
+ export const getConfigNameFromCredentials = (privateKey, url) => {
7
+ const normalizedKey = privateKey.trim();
8
+ const normalizedUrl = url.trim();
9
+ if (!normalizedKey || !normalizedUrl) {
10
+ throw new Error('Private key and URL must not be empty');
11
+ }
12
+ const hashBuilder = createHash('sha256');
13
+ hashBuilder.update(normalizedKey);
14
+ hashBuilder.update('|');
15
+ hashBuilder.update(normalizedUrl);
16
+ const hash = hashBuilder.digest('hex');
17
+ return `${hash}.config.json`;
18
+ };
5
19
  export const preparePath = (rawPath) => {
6
20
  if (path.isAbsolute(rawPath))
7
21
  return rawPath;