@super-protocol/sp-cli 0.0.7 → 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 +63 -44
- package/dist/commands/account/base.d.ts +1 -2
- package/dist/commands/account/base.js +8 -14
- package/dist/commands/account/forget.d.ts +1 -0
- package/dist/commands/account/forget.js +69 -3
- package/dist/commands/account/info.js +5 -5
- package/dist/commands/account/login.d.ts +6 -0
- package/dist/commands/account/login.js +122 -22
- package/dist/commands/account/switch.d.ts +3 -0
- package/dist/commands/account/switch.js +30 -10
- package/dist/commands/base.js +97 -12
- package/dist/commands/storage/base.js +11 -11
- package/dist/commands/storage/select.js +3 -3
- package/dist/commands/storage/update.js +10 -10
- package/dist/commands/workflows/extend-lease.d.ts +1 -1
- package/dist/commands/workflows/extend-lease.js +2 -3
- package/dist/hooks/prerun/auth.js +2 -1
- package/dist/managers/config-file-manager.d.ts +6 -12
- package/dist/managers/config-file-manager.js +58 -59
- package/dist/utils/progress.js +1 -0
- package/dist/utils/prompt-flags.d.ts +20 -0
- package/dist/utils/prompt-flags.js +121 -0
- package/dist/utils/prompt.service.d.ts +23 -0
- package/dist/utils/prompt.service.js +107 -0
- package/dist/utils/tty.d.ts +1 -0
- package/dist/utils/tty.js +3 -0
- package/oclif.manifest.json +281 -162
- package/package.json +1 -1
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { existsSync } from 'node:fs';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { confirm, isCancel, text } from '@clack/prompts';
|
|
4
3
|
import { Flags } from '@oclif/core';
|
|
4
|
+
import { Value } from 'typebox/value';
|
|
5
5
|
import { generatePrivateKey, privateKeyToAccount } from 'viem/accounts';
|
|
6
|
+
import { cliConfigSchema } from '../../config/config.schema.js';
|
|
6
7
|
import { PROVIDER_URL } from '../../constants.js';
|
|
7
8
|
import { AuthService } from '../../services/auth.service.js';
|
|
8
9
|
import { StorageService } from '../../services/storage.service.js';
|
|
9
|
-
import { getConfigNameFromCredentials } from '../../utils/helper.js';
|
|
10
|
+
import { getConfigName, getConfigNameFromCredentials, preparePath, readJsonFile, } from '../../utils/helper.js';
|
|
11
|
+
import { promptService } from '../../utils/prompt.service.js';
|
|
10
12
|
import { BaseAccountCommand } from './base.js';
|
|
11
13
|
export default class AccountLoginCommand extends BaseAccountCommand {
|
|
12
14
|
static aliases = ['login'];
|
|
@@ -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,
|
|
@@ -36,8 +41,7 @@ export default class AccountLoginCommand extends BaseAccountCommand {
|
|
|
36
41
|
configFileManager;
|
|
37
42
|
shouldSkipLogin = false;
|
|
38
43
|
async inputPrompt(options) {
|
|
39
|
-
|
|
40
|
-
return this.ensurePromptValue(result);
|
|
44
|
+
return promptService.text(options);
|
|
41
45
|
}
|
|
42
46
|
async checkStorage() {
|
|
43
47
|
await this.container
|
|
@@ -91,18 +95,14 @@ export default class AccountLoginCommand extends BaseAccountCommand {
|
|
|
91
95
|
}
|
|
92
96
|
async askName(fallback) {
|
|
93
97
|
const value = await this.inputPrompt({
|
|
94
|
-
message: '
|
|
98
|
+
message: 'Account name:',
|
|
95
99
|
placeholder: fallback,
|
|
96
100
|
});
|
|
97
101
|
const trimmed = value.trim();
|
|
98
102
|
return trimmed || fallback;
|
|
99
103
|
}
|
|
100
104
|
async confirmPrompt(message, initialValue = true) {
|
|
101
|
-
|
|
102
|
-
if (isCancel(result)) {
|
|
103
|
-
this.error('Operation cancelled.', { exit: 1 });
|
|
104
|
-
}
|
|
105
|
-
return Boolean(result);
|
|
105
|
+
return promptService.confirm({ initialValue, message });
|
|
106
106
|
}
|
|
107
107
|
createAccountFromKey(privateKey) {
|
|
108
108
|
try {
|
|
@@ -120,10 +120,22 @@ export default class AccountLoginCommand extends BaseAccountCommand {
|
|
|
120
120
|
async createConfigIfMissing(configFile, displayName, providerUrl, account) {
|
|
121
121
|
const configPath = path.join(this.configFileManager.getConfigDir(), configFile);
|
|
122
122
|
if (existsSync(configPath)) {
|
|
123
|
+
await this.configFileManager.updateConfigName(configFile, displayName);
|
|
123
124
|
return;
|
|
124
125
|
}
|
|
125
126
|
await this.configFileManager.createConfig(configFile, displayName, providerUrl, account);
|
|
126
|
-
this.log(`Created
|
|
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}"`);
|
|
127
139
|
}
|
|
128
140
|
findConfigsByPrivateKey(privateKey, providerUrl, matchProviderUrl) {
|
|
129
141
|
const normalizedKey = privateKey.trim().toLowerCase();
|
|
@@ -164,7 +176,7 @@ export default class AccountLoginCommand extends BaseAccountCommand {
|
|
|
164
176
|
}
|
|
165
177
|
let shouldCreate = true;
|
|
166
178
|
if (!this.flags.yes) {
|
|
167
|
-
shouldCreate = await this.confirmPrompt(`
|
|
179
|
+
shouldCreate = await this.confirmPrompt(`Account with "${name}" not found. Create it?`, true);
|
|
168
180
|
}
|
|
169
181
|
if (!shouldCreate) {
|
|
170
182
|
this.shouldSkipLogin = true;
|
|
@@ -176,7 +188,7 @@ export default class AccountLoginCommand extends BaseAccountCommand {
|
|
|
176
188
|
await this.createConfigIfMissing(configFile, name, providerUrl, account);
|
|
177
189
|
let shouldSwitch = true;
|
|
178
190
|
if (!this.flags.yes) {
|
|
179
|
-
shouldSwitch = await this.confirmPrompt(`Switch to
|
|
191
|
+
shouldSwitch = await this.confirmPrompt(`Switch to account "${name}" now?`, true);
|
|
180
192
|
}
|
|
181
193
|
if (!shouldSwitch) {
|
|
182
194
|
this.shouldSkipLogin = true;
|
|
@@ -184,41 +196,120 @@ export default class AccountLoginCommand extends BaseAccountCommand {
|
|
|
184
196
|
}
|
|
185
197
|
await this.configFileManager.setCurrentConfig(configFile);
|
|
186
198
|
}
|
|
187
|
-
|
|
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) {
|
|
188
275
|
const matches = this.findConfigsByPrivateKey(privateKey, providerUrl, matchProviderUrl);
|
|
276
|
+
const desiredName = name?.trim();
|
|
189
277
|
if (matches.length === 1) {
|
|
278
|
+
await this.updateConfigDisplayName(matches[0].file, desiredName);
|
|
190
279
|
await this.configFileManager.setCurrentConfig(matches[0].file);
|
|
191
280
|
return;
|
|
192
281
|
}
|
|
193
282
|
if (matches.length > 1) {
|
|
194
283
|
if (this.flags.yes) {
|
|
195
|
-
this.log(`Too many matched
|
|
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);
|
|
196
286
|
await this.configFileManager.setCurrentConfig(matches[0].file);
|
|
197
287
|
return;
|
|
198
288
|
}
|
|
199
289
|
const selected = await this.selectPrompt({
|
|
200
|
-
message: 'Multiple
|
|
290
|
+
message: 'Multiple accounts found for this private key. Select one:',
|
|
201
291
|
options: matches.map((match) => ({ label: match.name, value: match.file })),
|
|
202
292
|
});
|
|
293
|
+
await this.updateConfigDisplayName(selected, desiredName);
|
|
203
294
|
await this.configFileManager.setCurrentConfig(selected);
|
|
204
295
|
return;
|
|
205
296
|
}
|
|
206
297
|
let shouldCreate = true;
|
|
207
298
|
if (!this.flags.yes) {
|
|
208
|
-
shouldCreate = await this.confirmPrompt('No
|
|
299
|
+
shouldCreate = await this.confirmPrompt('No accounts found for this private key. Create a new one?', true);
|
|
209
300
|
}
|
|
210
301
|
if (!shouldCreate) {
|
|
211
302
|
this.shouldSkipLogin = true;
|
|
212
303
|
return;
|
|
213
304
|
}
|
|
214
305
|
const account = this.createAccountFromKey(privateKey);
|
|
215
|
-
const suggestedName =
|
|
306
|
+
const suggestedName = desiredName || account.address;
|
|
216
307
|
const displayName = this.flags.yes ? suggestedName : await this.askName(suggestedName);
|
|
217
308
|
const configFile = getConfigNameFromCredentials(privateKey, providerUrl);
|
|
218
309
|
await this.createConfigIfMissing(configFile, displayName, providerUrl, account);
|
|
219
310
|
let shouldSwitch = true;
|
|
220
311
|
if (!this.flags.yes) {
|
|
221
|
-
shouldSwitch = await this.confirmPrompt(`Switch to
|
|
312
|
+
shouldSwitch = await this.confirmPrompt(`Switch to account "${displayName}" now?`, true);
|
|
222
313
|
}
|
|
223
314
|
if (!shouldSwitch) {
|
|
224
315
|
this.shouldSkipLogin = true;
|
|
@@ -228,18 +319,27 @@ export default class AccountLoginCommand extends BaseAccountCommand {
|
|
|
228
319
|
}
|
|
229
320
|
async resolveConfiguration() {
|
|
230
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
|
+
}
|
|
231
326
|
const privateKey = this.flags.privateKey?.trim();
|
|
232
327
|
const url = this.flags.url?.trim();
|
|
233
328
|
if (url !== undefined && !url) {
|
|
234
329
|
this.error('Provider URL cannot be empty.', { exit: 1 });
|
|
235
330
|
}
|
|
236
331
|
const providerUrl = (url || PROVIDER_URL).trim();
|
|
237
|
-
if (
|
|
238
|
-
await this.
|
|
332
|
+
if (configPath) {
|
|
333
|
+
await this.resolveByPath(configPath, name);
|
|
239
334
|
return;
|
|
240
335
|
}
|
|
241
336
|
if (privateKey) {
|
|
242
|
-
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;
|
|
243
343
|
}
|
|
244
344
|
}
|
|
245
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
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
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.
|
|
56
|
+
await this.container.initConfigManager(true).build();
|
|
37
57
|
this.configFileManager = this.container.configFileManager;
|
|
38
58
|
this.configManager = this.container.configManager;
|
|
39
59
|
}
|
package/dist/commands/base.js
CHANGED
|
@@ -1,7 +1,21 @@
|
|
|
1
|
-
import { isCancel } from '@clack/prompts';
|
|
2
1
|
import { Command, Flags } from '@oclif/core';
|
|
3
2
|
import { AppContainer } from '../lib/container.js';
|
|
4
3
|
import logger from '../logger.js';
|
|
4
|
+
import { findMissingRequiredArgs, findMissingRequiredFlags, formatMissingArgsError, formatMissingFlagsError, promptForArg, promptForFlag, } from '../utils/prompt-flags.js';
|
|
5
|
+
import { promptService } from '../utils/prompt.service.js';
|
|
6
|
+
import { isInteractiveMode } from '../utils/tty.js';
|
|
7
|
+
const relaxRequired = (definitions) => {
|
|
8
|
+
if (!definitions) {
|
|
9
|
+
return definitions;
|
|
10
|
+
}
|
|
11
|
+
return Object.fromEntries(Object.entries(definitions).map(([name, definition]) => [
|
|
12
|
+
name,
|
|
13
|
+
{
|
|
14
|
+
...definition,
|
|
15
|
+
required: false,
|
|
16
|
+
},
|
|
17
|
+
]));
|
|
18
|
+
};
|
|
5
19
|
export class BaseCommand extends Command {
|
|
6
20
|
static authenticate = true;
|
|
7
21
|
static baseFlags = {
|
|
@@ -11,6 +25,12 @@ export class BaseCommand extends Command {
|
|
|
11
25
|
required: false,
|
|
12
26
|
summary: 'Specify config file.',
|
|
13
27
|
}),
|
|
28
|
+
tty: Flags.boolean({
|
|
29
|
+
allowNo: true,
|
|
30
|
+
helpGroup: 'GLOBAL',
|
|
31
|
+
required: false,
|
|
32
|
+
summary: 'Force or disable interactive mode (use --no-tty to disable).',
|
|
33
|
+
}),
|
|
14
34
|
};
|
|
15
35
|
static enableJsonFlag = true;
|
|
16
36
|
args;
|
|
@@ -22,24 +42,89 @@ export class BaseCommand extends Command {
|
|
|
22
42
|
this.logger = logger.getPino();
|
|
23
43
|
}
|
|
24
44
|
ensurePromptValue(value, errorMessage = 'Operation cancelled.') {
|
|
25
|
-
|
|
26
|
-
|
|
45
|
+
try {
|
|
46
|
+
return promptService.ensureValue(value, errorMessage);
|
|
47
|
+
}
|
|
48
|
+
catch (error) {
|
|
49
|
+
this.error(error instanceof Error ? error.message : String(error));
|
|
27
50
|
}
|
|
28
|
-
return value;
|
|
29
51
|
}
|
|
30
52
|
async init() {
|
|
31
53
|
await super.init();
|
|
32
|
-
const {
|
|
33
|
-
|
|
34
|
-
|
|
54
|
+
const allFlags = { ...this.ctor.flags, ...super.ctor.baseFlags };
|
|
55
|
+
const relaxedArgs = relaxRequired(this.ctor.args);
|
|
56
|
+
const relaxedFlags = relaxRequired(this.ctor.flags);
|
|
57
|
+
const relaxedBaseFlags = relaxRequired(super.ctor.baseFlags);
|
|
58
|
+
let parsedResult = await this.parse({
|
|
59
|
+
args: relaxedArgs,
|
|
60
|
+
baseFlags: relaxedBaseFlags,
|
|
35
61
|
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
36
|
-
flags:
|
|
37
|
-
strict:
|
|
62
|
+
flags: relaxedFlags,
|
|
63
|
+
strict: false,
|
|
38
64
|
});
|
|
39
|
-
|
|
40
|
-
|
|
65
|
+
const isInteractive = isInteractiveMode(parsedResult.flags.tty);
|
|
66
|
+
promptService.setInteractiveMode(isInteractive);
|
|
67
|
+
const missingFlags = await findMissingRequiredFlags(parsedResult.flags, allFlags);
|
|
68
|
+
const missingArgs = await findMissingRequiredArgs(parsedResult.args, this.ctor.args);
|
|
69
|
+
if (missingFlags.length > 0 || missingArgs.length > 0) {
|
|
70
|
+
if (!isInteractive) {
|
|
71
|
+
const errors = [];
|
|
72
|
+
if (missingFlags.length > 0) {
|
|
73
|
+
errors.push(formatMissingFlagsError(missingFlags));
|
|
74
|
+
}
|
|
75
|
+
if (missingArgs.length > 0) {
|
|
76
|
+
errors.push(formatMissingArgsError(missingArgs));
|
|
77
|
+
}
|
|
78
|
+
this.error(errors.join(' '), { exit: 1 });
|
|
79
|
+
}
|
|
80
|
+
const argValues = [];
|
|
81
|
+
for (const missingArg of missingArgs) {
|
|
82
|
+
const value = await promptForArg(missingArg);
|
|
83
|
+
argValues.push(String(value));
|
|
84
|
+
}
|
|
85
|
+
const flagPairs = [];
|
|
86
|
+
for (const missingFlag of missingFlags) {
|
|
87
|
+
const value = await promptForFlag(missingFlag);
|
|
88
|
+
if (missingFlag.type === 'boolean') {
|
|
89
|
+
const boolValue = Boolean(value);
|
|
90
|
+
if (boolValue) {
|
|
91
|
+
flagPairs.push(`--${missingFlag.name}`);
|
|
92
|
+
}
|
|
93
|
+
else if ('allowNo' in missingFlag.definition &&
|
|
94
|
+
Boolean(missingFlag.definition.allowNo)) {
|
|
95
|
+
flagPairs.push(`--no-${missingFlag.name}`);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
this.error(`Flag --${missingFlag.name} is required and only supports "true".`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
flagPairs.push(`--${missingFlag.name}`, String(value));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
const updatedArgv = [...this.argv, ...argValues, ...flagPairs];
|
|
106
|
+
this.argv = updatedArgv;
|
|
107
|
+
parsedResult = await this.parse({
|
|
108
|
+
args: this.ctor.args,
|
|
109
|
+
baseFlags: super.ctor.baseFlags,
|
|
110
|
+
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
111
|
+
flags: this.ctor.flags,
|
|
112
|
+
strict: this.ctor.strict,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else if (this.ctor.strict) {
|
|
116
|
+
parsedResult = await this.parse({
|
|
117
|
+
args: this.ctor.args,
|
|
118
|
+
baseFlags: super.ctor.baseFlags,
|
|
119
|
+
enableJsonFlag: this.ctor.enableJsonFlag,
|
|
120
|
+
flags: this.ctor.flags,
|
|
121
|
+
strict: true,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
this.flags = parsedResult.flags;
|
|
125
|
+
this.args = parsedResult.args;
|
|
41
126
|
this.container = AppContainer.container.setupRuntimeConfig({
|
|
42
|
-
configFile: flags.config,
|
|
127
|
+
configFile: parsedResult.flags.config,
|
|
43
128
|
});
|
|
44
129
|
}
|
|
45
130
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import { password, select, text } from '@clack/prompts';
|
|
2
1
|
import { StorageType } from '@super-protocol/provider-client';
|
|
3
2
|
import { BaseCommand } from '../../commands/base.js';
|
|
4
3
|
import { S3_REGION } from '../../constants.js';
|
|
5
4
|
import { StorageService } from '../../services/storage.service.js';
|
|
5
|
+
import { promptService } from '../../utils/prompt.service.js';
|
|
6
6
|
export class BaseStorageCommand extends BaseCommand {
|
|
7
7
|
currentDir = process.cwd();
|
|
8
8
|
storageService;
|
|
@@ -14,25 +14,25 @@ export class BaseStorageCommand extends BaseCommand {
|
|
|
14
14
|
}
|
|
15
15
|
async promptS3Credentials() {
|
|
16
16
|
const region = S3_REGION;
|
|
17
|
-
const readAccessKeyId =
|
|
17
|
+
const readAccessKeyId = (await promptService.text({
|
|
18
18
|
message: 'Read access key ID',
|
|
19
19
|
validate(value) {
|
|
20
20
|
return value ? undefined : 'Read access key ID is required';
|
|
21
21
|
},
|
|
22
22
|
})).trim();
|
|
23
|
-
const readSecretAccessKey =
|
|
23
|
+
const readSecretAccessKey = (await promptService.password({
|
|
24
24
|
message: 'Read secret access key',
|
|
25
25
|
validate(value) {
|
|
26
26
|
return value ? undefined : 'Read secret access key is required';
|
|
27
27
|
},
|
|
28
28
|
})).trim();
|
|
29
|
-
const writeAccessKeyId =
|
|
29
|
+
const writeAccessKeyId = (await promptService.text({
|
|
30
30
|
message: 'Write access key ID',
|
|
31
31
|
validate(value) {
|
|
32
32
|
return value ? undefined : 'Write access key ID is required';
|
|
33
33
|
},
|
|
34
34
|
})).trim();
|
|
35
|
-
const writeSecretAccessKey =
|
|
35
|
+
const writeSecretAccessKey = (await promptService.password({
|
|
36
36
|
message: 'Write secret access key',
|
|
37
37
|
validate(value) {
|
|
38
38
|
return value ? undefined : 'Write secret access key is required';
|
|
@@ -47,20 +47,20 @@ export class BaseStorageCommand extends BaseCommand {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
async promptStoragePayload() {
|
|
50
|
-
const storageType =
|
|
50
|
+
const storageType = await promptService.select({
|
|
51
51
|
message: 'Select storage type',
|
|
52
52
|
options: [
|
|
53
53
|
{ label: 'S3', value: StorageType.S3 },
|
|
54
54
|
{ label: 'StorJ', value: StorageType.StorJ },
|
|
55
55
|
],
|
|
56
|
-
})
|
|
57
|
-
const bucket =
|
|
56
|
+
});
|
|
57
|
+
const bucket = (await promptService.text({
|
|
58
58
|
message: 'Bucket name',
|
|
59
59
|
validate(value) {
|
|
60
60
|
return value ? undefined : 'Bucket is required';
|
|
61
61
|
},
|
|
62
62
|
})).trim();
|
|
63
|
-
const prefix =
|
|
63
|
+
const prefix = (await promptService.text({
|
|
64
64
|
defaultValue: '/',
|
|
65
65
|
initialValue: '/',
|
|
66
66
|
message: 'Prefix',
|
|
@@ -82,13 +82,13 @@ export class BaseStorageCommand extends BaseCommand {
|
|
|
82
82
|
return storage;
|
|
83
83
|
}
|
|
84
84
|
async promptStorJCredentials() {
|
|
85
|
-
const readAccessToken =
|
|
85
|
+
const readAccessToken = (await promptService.password({
|
|
86
86
|
message: 'Read access token',
|
|
87
87
|
validate(value) {
|
|
88
88
|
return value ? undefined : 'Read access token is required';
|
|
89
89
|
},
|
|
90
90
|
})).trim();
|
|
91
|
-
const writeAccessToken =
|
|
91
|
+
const writeAccessToken = (await promptService.password({
|
|
92
92
|
message: 'Write access token',
|
|
93
93
|
validate(value) {
|
|
94
94
|
return value ? undefined : 'Write access token is required';
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { select } from '@clack/prompts';
|
|
2
1
|
import { StoragesUndefinedError } from '../../services/storage.service.js';
|
|
2
|
+
import { promptService } from '../../utils/prompt.service.js';
|
|
3
3
|
import { BaseStorageCommand } from './base.js';
|
|
4
4
|
export default class StorageSelect extends BaseStorageCommand {
|
|
5
5
|
static description = 'Select a storage that will be used by subsequent commands.';
|
|
@@ -7,13 +7,13 @@ export default class StorageSelect extends BaseStorageCommand {
|
|
|
7
7
|
async run() {
|
|
8
8
|
try {
|
|
9
9
|
const storages = await this.storageService.requestStorages();
|
|
10
|
-
const storageId =
|
|
10
|
+
const storageId = await promptService.select({
|
|
11
11
|
message: 'Select storage',
|
|
12
12
|
options: storages.map((storage) => ({
|
|
13
13
|
label: this.storageService.getLabel(storage),
|
|
14
14
|
value: storage.id,
|
|
15
15
|
})),
|
|
16
|
-
})
|
|
16
|
+
});
|
|
17
17
|
if (!storageId) {
|
|
18
18
|
this.error('Storage ID is required');
|
|
19
19
|
}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import fs from 'node:fs/promises';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { confirm, select, text } from '@clack/prompts';
|
|
4
3
|
import { Flags } from '@oclif/core';
|
|
5
4
|
import { StorageType, } from '@super-protocol/provider-client';
|
|
6
5
|
import { StoragesUndefinedError } from '../../services/storage.service.js';
|
|
6
|
+
import { promptService } from '../../utils/prompt.service.js';
|
|
7
7
|
import { BaseStorageCommand } from './base.js';
|
|
8
8
|
export default class StorageUpdate extends BaseStorageCommand {
|
|
9
9
|
static description = 'Update the configuration of an existing storage.';
|
|
@@ -39,10 +39,10 @@ export default class StorageUpdate extends BaseStorageCommand {
|
|
|
39
39
|
if (!options || options.length === 0) {
|
|
40
40
|
throw new StoragesUndefinedError('No storages available to update');
|
|
41
41
|
}
|
|
42
|
-
storageId =
|
|
42
|
+
storageId = await promptService.select({
|
|
43
43
|
message: 'Select storage to update',
|
|
44
44
|
options,
|
|
45
|
-
})
|
|
45
|
+
});
|
|
46
46
|
}
|
|
47
47
|
if (!storageId) {
|
|
48
48
|
this.error('Storage ID is required');
|
|
@@ -148,14 +148,14 @@ export default class StorageUpdate extends BaseStorageCommand {
|
|
|
148
148
|
}
|
|
149
149
|
async promptStorageUpdate(existingStorage) {
|
|
150
150
|
const payload = {};
|
|
151
|
-
const bucket =
|
|
151
|
+
const bucket = (await promptService.text({
|
|
152
152
|
defaultValue: existingStorage?.bucket ?? '',
|
|
153
153
|
message: 'Bucket name (leave empty to keep current)',
|
|
154
154
|
})).trim();
|
|
155
155
|
if (bucket) {
|
|
156
156
|
payload.bucket = bucket;
|
|
157
157
|
}
|
|
158
|
-
const prefix =
|
|
158
|
+
const prefix = (await promptService.text({
|
|
159
159
|
defaultValue: existingStorage?.prefix ?? '',
|
|
160
160
|
message: 'Prefix (leave empty to keep current)',
|
|
161
161
|
})).trim();
|
|
@@ -163,27 +163,27 @@ export default class StorageUpdate extends BaseStorageCommand {
|
|
|
163
163
|
payload.prefix = prefix;
|
|
164
164
|
}
|
|
165
165
|
let newStorageType;
|
|
166
|
-
const shouldChangeType =
|
|
166
|
+
const shouldChangeType = await promptService.confirm({
|
|
167
167
|
initialValue: false,
|
|
168
168
|
message: existingStorage
|
|
169
169
|
? `Change storage type? (current: ${existingStorage.storageType})`
|
|
170
170
|
: 'Change storage type?',
|
|
171
|
-
})
|
|
171
|
+
});
|
|
172
172
|
if (shouldChangeType) {
|
|
173
|
-
newStorageType =
|
|
173
|
+
newStorageType = await promptService.select({
|
|
174
174
|
message: 'Select new storage type',
|
|
175
175
|
options: [
|
|
176
176
|
{ label: 'Amazon S3', value: StorageType.S3 },
|
|
177
177
|
{ label: 'StorJ', value: StorageType.StorJ },
|
|
178
178
|
],
|
|
179
|
-
})
|
|
179
|
+
});
|
|
180
180
|
payload.storageType = newStorageType;
|
|
181
181
|
}
|
|
182
182
|
const effectiveType = newStorageType ?? existingStorage?.storageType;
|
|
183
183
|
const credentialsRequired = Boolean(newStorageType);
|
|
184
184
|
if (effectiveType) {
|
|
185
185
|
const updateCredentials = credentialsRequired ||
|
|
186
|
-
|
|
186
|
+
(await promptService.confirm({
|
|
187
187
|
initialValue: false,
|
|
188
188
|
message: `Update ${effectiveType} credentials?`,
|
|
189
189
|
}));
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type SelectOptions } from '
|
|
1
|
+
import { type SelectOptions } from '../../utils/prompt.service.js';
|
|
2
2
|
import { BaseCommand } from '../base.js';
|
|
3
3
|
export default class WorkflowsExtendLease extends BaseCommand<typeof WorkflowsExtendLease> {
|
|
4
4
|
static args: {
|