@super-protocol/sp-cli 0.0.7 → 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.
@@ -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 = this.ensurePromptValue(await text({
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 = this.ensurePromptValue(await password({
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 = this.ensurePromptValue(await text({
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 = this.ensurePromptValue(await password({
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 = this.ensurePromptValue(await select({
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 = this.ensurePromptValue(await text({
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 = this.ensurePromptValue(await text({
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 = this.ensurePromptValue(await password({
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 = this.ensurePromptValue(await password({
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 = this.ensurePromptValue(await select({
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 = this.ensurePromptValue(await select({
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 = this.ensurePromptValue(await text({
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 = this.ensurePromptValue(await text({
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 = this.ensurePromptValue(await confirm({
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 = this.ensurePromptValue(await select({
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
- this.ensurePromptValue(await confirm({
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 '@clack/prompts';
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: {
@@ -1,7 +1,7 @@
1
- import { select } from '@clack/prompts';
2
1
  import { Args, Flags } from '@oclif/core';
3
2
  import { Orders } from '@super-protocol/sdk-js';
4
3
  import { formatEther, parseEther } from 'viem/utils';
4
+ import { promptService } from '../../utils/prompt.service.js';
5
5
  import { BaseCommand } from '../base.js';
6
6
  export default class WorkflowsExtendLease extends BaseCommand {
7
7
  static args = {
@@ -97,7 +97,6 @@ export default class WorkflowsExtendLease extends BaseCommand {
97
97
  }
98
98
  }
99
99
  async selectPrompt(options) {
100
- const result = await select(options);
101
- return this.ensurePromptValue(result);
100
+ return promptService.select(options);
102
101
  }
103
102
  }
@@ -0,0 +1,20 @@
1
+ import type { Args, Flags } from '@oclif/core';
2
+ type FlagDefinition = ReturnType<typeof Flags.string> | ReturnType<typeof Flags.integer> | ReturnType<typeof Flags.boolean>;
3
+ type ArgDefinition = ReturnType<typeof Args.string> | ReturnType<typeof Args.integer> | ReturnType<typeof Args.boolean>;
4
+ export interface MissingFlag {
5
+ name: string;
6
+ definition: FlagDefinition;
7
+ type: 'string' | 'integer' | 'boolean';
8
+ }
9
+ export interface MissingArg {
10
+ name: string;
11
+ definition: ArgDefinition;
12
+ type: 'string' | 'integer' | 'boolean';
13
+ }
14
+ export declare const findMissingRequiredFlags: (flags: Record<string, unknown>, flagDefinitions: Record<string, FlagDefinition> | undefined) => Promise<MissingFlag[]>;
15
+ export declare const findMissingRequiredArgs: (args: Record<string, unknown>, argDefinitions: Record<string, ArgDefinition> | undefined) => Promise<MissingArg[]>;
16
+ export declare const promptForFlag: (missingFlag: MissingFlag) => Promise<string | number | boolean>;
17
+ export declare const promptForArg: (missingArg: MissingArg) => Promise<string | number | boolean>;
18
+ export declare const formatMissingFlagsError: (missingFlags: MissingFlag[]) => string;
19
+ export declare const formatMissingArgsError: (missingArgs: MissingArg[]) => string;
20
+ export {};
@@ -0,0 +1,121 @@
1
+ import { promptService } from './prompt.service.js';
2
+ const getSampleValue = (definition) => {
3
+ if (typeof definition.min === 'number') {
4
+ return String(definition.min);
5
+ }
6
+ if (typeof definition.max === 'number' && definition.max < 1) {
7
+ return String(definition.max);
8
+ }
9
+ return '1';
10
+ };
11
+ const inferTypeFromDefinition = async (definition, allowNo) => {
12
+ if (definition && typeof definition === 'object') {
13
+ const typedDefinition = definition;
14
+ if (typedDefinition.type === 'boolean' || typedDefinition.type === 'flag') {
15
+ return 'boolean';
16
+ }
17
+ if (typedDefinition.type === 'integer' || typedDefinition.type === 'number') {
18
+ return 'integer';
19
+ }
20
+ if (allowNo && typeof typedDefinition.allowNo === 'boolean') {
21
+ return 'boolean';
22
+ }
23
+ }
24
+ const parse = definition?.parse;
25
+ if (typeof parse === 'function') {
26
+ const sample = getSampleValue(definition);
27
+ try {
28
+ const parsed = await parse(sample, {}, definition);
29
+ if (typeof parsed === 'number') {
30
+ return 'integer';
31
+ }
32
+ if (typeof parsed === 'boolean') {
33
+ return 'boolean';
34
+ }
35
+ }
36
+ catch {
37
+ // Ignore parsing errors for type inference.
38
+ }
39
+ }
40
+ return 'string';
41
+ };
42
+ const parseIntegerValue = (name, input, definition) => {
43
+ if (!/^-?\d+$/.test(input)) {
44
+ throw new Error(`${name} must be a number`);
45
+ }
46
+ const num = Number.parseInt(input, 10);
47
+ if (definition.min !== undefined && num < definition.min) {
48
+ throw new Error(`Expected an integer greater than or equal to ${definition.min} but received: ${input}`);
49
+ }
50
+ if (definition.max !== undefined && num > definition.max) {
51
+ throw new Error(`Expected an integer less than or equal to ${definition.max} but received: ${input}`);
52
+ }
53
+ return num;
54
+ };
55
+ const getFlagType = async (definition) => inferTypeFromDefinition(definition, true);
56
+ export const findMissingRequiredFlags = async (flags, flagDefinitions) => {
57
+ if (!flagDefinitions) {
58
+ return [];
59
+ }
60
+ const missing = [];
61
+ for (const [name, definition] of Object.entries(flagDefinitions)) {
62
+ if (definition.required && (flags[name] === undefined || flags[name] === null)) {
63
+ missing.push({
64
+ name,
65
+ definition,
66
+ type: await getFlagType(definition),
67
+ });
68
+ }
69
+ }
70
+ return missing;
71
+ };
72
+ const getArgType = async (definition) => inferTypeFromDefinition(definition, false);
73
+ export const findMissingRequiredArgs = async (args, argDefinitions) => {
74
+ if (!argDefinitions) {
75
+ return [];
76
+ }
77
+ const missing = [];
78
+ for (const [name, definition] of Object.entries(argDefinitions)) {
79
+ if (definition.required && (args[name] === undefined || args[name] === null)) {
80
+ missing.push({
81
+ name,
82
+ definition,
83
+ type: await getArgType(definition),
84
+ });
85
+ }
86
+ }
87
+ return missing;
88
+ };
89
+ export const promptForFlag = async (missingFlag) => {
90
+ const description = missingFlag.definition.description;
91
+ const optionValues = missingFlag.definition.options;
92
+ if (missingFlag.type === 'string' && Array.isArray(optionValues) && optionValues.length > 0) {
93
+ const message = description ? `Select ${description}` : `Select ${missingFlag.name}`;
94
+ return promptService.select({
95
+ message,
96
+ options: optionValues.map((value) => ({
97
+ label: value,
98
+ value,
99
+ })),
100
+ });
101
+ }
102
+ const parseValue = missingFlag.type === 'integer'
103
+ ? (input) => parseIntegerValue(missingFlag.name, input, missingFlag.definition)
104
+ : undefined;
105
+ return promptService.promptForValue(missingFlag.name, description, missingFlag.type, parseValue);
106
+ };
107
+ export const promptForArg = async (missingArg) => {
108
+ const description = missingArg.definition.description;
109
+ const parseValue = missingArg.type === 'integer'
110
+ ? (input) => parseIntegerValue(missingArg.name, input, missingArg.definition)
111
+ : undefined;
112
+ return promptService.promptForValue(missingArg.name, description, missingArg.type, parseValue);
113
+ };
114
+ export const formatMissingFlagsError = (missingFlags) => {
115
+ const flagNames = missingFlags.map((f) => `--${f.name}`).join(', ');
116
+ return `Missing required flags: ${flagNames}. Please provide them or run in interactive mode.`;
117
+ };
118
+ export const formatMissingArgsError = (missingArgs) => {
119
+ const argNames = missingArgs.map((a) => `<${a.name}>`).join(', ');
120
+ return `Missing required arguments: ${argNames}. Please provide them or run in interactive mode.`;
121
+ };
@@ -0,0 +1,16 @@
1
+ import { type ConfirmOptions as ClackConfirmOptions, type PasswordOptions as ClackPasswordOptions, type SelectOptions as ClackSelectOptions, type TextOptions as ClackTextOptions } from '@clack/prompts';
2
+ export type TextOptions = ClackTextOptions;
3
+ export type ConfirmOptions = ClackConfirmOptions;
4
+ export type SelectOptions<T> = ClackSelectOptions<T>;
5
+ export type PasswordOptions = ClackPasswordOptions;
6
+ type ParseValue = (input: string) => string | number | boolean;
7
+ export declare class PromptService {
8
+ text(options: ClackTextOptions): Promise<string>;
9
+ confirm(options: ClackConfirmOptions): Promise<boolean>;
10
+ select<T>(options: ClackSelectOptions<T>): Promise<T>;
11
+ password(options: ClackPasswordOptions): Promise<string>;
12
+ ensureValue<T>(value: symbol | T, errorMessage?: string): T;
13
+ promptForValue(name: string, description: string | undefined, type: 'string' | 'integer' | 'boolean', parseValue?: ParseValue): Promise<string | number | boolean>;
14
+ }
15
+ export declare const promptService: PromptService;
16
+ export {};
@@ -0,0 +1,75 @@
1
+ import { confirm as clackConfirm, password as clackPassword, select as clackSelect, text as clackText, isCancel, } from '@clack/prompts';
2
+ export class PromptService {
3
+ async text(options) {
4
+ const result = await clackText(options);
5
+ if (isCancel(result)) {
6
+ throw new Error('Operation cancelled.');
7
+ }
8
+ return result;
9
+ }
10
+ async confirm(options) {
11
+ const result = await clackConfirm(options);
12
+ if (isCancel(result)) {
13
+ throw new Error('Operation cancelled.');
14
+ }
15
+ return result;
16
+ }
17
+ async select(options) {
18
+ const result = await clackSelect(options);
19
+ if (isCancel(result)) {
20
+ throw new Error('Operation cancelled.');
21
+ }
22
+ return result;
23
+ }
24
+ async password(options) {
25
+ const result = await clackPassword(options);
26
+ if (isCancel(result)) {
27
+ throw new Error('Operation cancelled.');
28
+ }
29
+ return result;
30
+ }
31
+ ensureValue(value, errorMessage = 'Operation cancelled.') {
32
+ if (isCancel(value)) {
33
+ throw new Error(errorMessage);
34
+ }
35
+ return value;
36
+ }
37
+ async promptForValue(name, description, type, parseValue) {
38
+ const fullDescription = description ? ` (${description})` : '';
39
+ if (type === 'boolean') {
40
+ const message = `Enable ${name}${fullDescription}?`;
41
+ return await this.confirm({ message });
42
+ }
43
+ const message = `Please provide ${name}${fullDescription}:`;
44
+ const parseInput = (value) => {
45
+ if (parseValue) {
46
+ return parseValue(value);
47
+ }
48
+ if (type === 'integer') {
49
+ const num = Number.parseInt(value, 10);
50
+ if (Number.isNaN(num)) {
51
+ throw new Error(`${name} must be a number`);
52
+ }
53
+ return num;
54
+ }
55
+ return value;
56
+ };
57
+ const result = await this.text({
58
+ message,
59
+ validate: (value) => {
60
+ if (!value || value.trim() === '') {
61
+ return `${name} is required`;
62
+ }
63
+ try {
64
+ parseInput(value);
65
+ }
66
+ catch (error) {
67
+ return error instanceof Error ? error.message : String(error);
68
+ }
69
+ return;
70
+ },
71
+ });
72
+ return parseInput(result);
73
+ }
74
+ }
75
+ export const promptService = new PromptService();
@@ -0,0 +1 @@
1
+ export declare const isInteractiveMode: (ttyFlag?: boolean) => boolean;
@@ -0,0 +1,3 @@
1
+ export const isInteractiveMode = (ttyFlag) => {
2
+ return ttyFlag ?? Boolean(process.stdin.isTTY && process.stdout.isTTY);
3
+ };