@api-now/cli 1.0.4 → 1.2.0
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 +11 -1
- package/dist/index.js +170 -79
- package/package.json +7 -3
- package/dist/commands/auth.js +0 -314
- package/dist/commands/catalog.js +0 -289
- package/dist/commands/config.js +0 -59
- package/dist/commands/files.js +0 -293
- package/dist/commands/orgs.js +0 -104
- package/dist/commands/tokens.js +0 -145
- package/dist/utils/api.js +0 -54
- package/dist/utils/config.js +0 -202
- package/dist/utils/formatter.js +0 -195
- package/dist/utils/org-setup.js +0 -96
- package/dist/utils/prompt.js +0 -41
package/dist/utils/config.js
DELETED
|
@@ -1,202 +0,0 @@
|
|
|
1
|
-
import os from 'node:os';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
/**
|
|
5
|
-
* The fallback API URL when not configured.
|
|
6
|
-
*/
|
|
7
|
-
const DEFAULT_API_URL = 'http://localhost:8080';
|
|
8
|
-
/**
|
|
9
|
-
* The folder name for the CLI configuration.
|
|
10
|
-
*/
|
|
11
|
-
const CLI_FOLDER = 'apinow-cli';
|
|
12
|
-
/**
|
|
13
|
-
* The config file name.
|
|
14
|
-
*/
|
|
15
|
-
const CONFIG_FILE = 'config.json';
|
|
16
|
-
/**
|
|
17
|
-
* Manager class for resolving, loading, and saturating the CLI configuration.
|
|
18
|
-
*/
|
|
19
|
-
class CliConfigManager {
|
|
20
|
-
_resolved = null;
|
|
21
|
-
/**
|
|
22
|
-
* The resolved read-only CLI configuration.
|
|
23
|
-
*/
|
|
24
|
-
get resolved() {
|
|
25
|
-
if (!this._resolved) {
|
|
26
|
-
this.load();
|
|
27
|
-
}
|
|
28
|
-
const res = this._resolved;
|
|
29
|
-
if (!res) {
|
|
30
|
-
throw new Error('Configuration could not be loaded.');
|
|
31
|
-
}
|
|
32
|
-
return res;
|
|
33
|
-
}
|
|
34
|
-
/**
|
|
35
|
-
* The directory path for storing configuration files.
|
|
36
|
-
*/
|
|
37
|
-
configDir = this.#getConfigDir();
|
|
38
|
-
/**
|
|
39
|
-
* The absolute configuration file path.
|
|
40
|
-
*/
|
|
41
|
-
configPath = path.join(this.configDir, CONFIG_FILE);
|
|
42
|
-
/**
|
|
43
|
-
* Loads configuration from disk and environment variables.
|
|
44
|
-
*/
|
|
45
|
-
load() {
|
|
46
|
-
const diskConfig = this.#readConfig();
|
|
47
|
-
const token = process.env.APINOW_API_TOKEN || process.env.APINOW_TOKEN || diskConfig.token;
|
|
48
|
-
const apiUrl = process.env.APINOW_API_URL || diskConfig.apiUrl || DEFAULT_API_URL;
|
|
49
|
-
const org = diskConfig.org || '';
|
|
50
|
-
this._resolved = {
|
|
51
|
-
token,
|
|
52
|
-
apiUrl,
|
|
53
|
-
org,
|
|
54
|
-
format: 'text',
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Saturates/overrides configuration fields with commander options.
|
|
59
|
-
*/
|
|
60
|
-
saturate(options) {
|
|
61
|
-
if (!this._resolved) {
|
|
62
|
-
this.load();
|
|
63
|
-
}
|
|
64
|
-
const current = this._resolved;
|
|
65
|
-
if (!current) {
|
|
66
|
-
throw new Error('Configuration could not be loaded.');
|
|
67
|
-
}
|
|
68
|
-
this._resolved = {
|
|
69
|
-
token: current.token,
|
|
70
|
-
apiUrl: options.apiUrl || current.apiUrl,
|
|
71
|
-
org: current.org,
|
|
72
|
-
format: options.format === 'json' ? 'json' : 'text',
|
|
73
|
-
};
|
|
74
|
-
}
|
|
75
|
-
/**
|
|
76
|
-
* Returns the directory path for storing configuration files depending on the OS platform.
|
|
77
|
-
*
|
|
78
|
-
* - Windows: `%APPDATA%/apinow-cli`
|
|
79
|
-
* - MacOS: `~/Library/Preferences/apinow-cli`
|
|
80
|
-
* - Linux: `~/.config/apinow-cli` (or respects `$XDG_CONFIG_HOME`)
|
|
81
|
-
*
|
|
82
|
-
* @returns The absolute directory path.
|
|
83
|
-
*/
|
|
84
|
-
#getConfigDir() {
|
|
85
|
-
if (process.platform === 'win32') {
|
|
86
|
-
const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming');
|
|
87
|
-
if (!appData) {
|
|
88
|
-
throw new Error('Could not determine the configuration directory.');
|
|
89
|
-
}
|
|
90
|
-
return path.join(appData, CLI_FOLDER);
|
|
91
|
-
}
|
|
92
|
-
if (process.platform === 'darwin') {
|
|
93
|
-
return path.join(os.homedir(), 'Library', 'Preferences', CLI_FOLDER);
|
|
94
|
-
}
|
|
95
|
-
return path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), '.config'), CLI_FOLDER);
|
|
96
|
-
}
|
|
97
|
-
/**
|
|
98
|
-
* Reads and parses the CLI configuration from the configuration file.
|
|
99
|
-
* If the configuration file does not exist, returns default settings.
|
|
100
|
-
*
|
|
101
|
-
* @example
|
|
102
|
-
* ```typescript
|
|
103
|
-
* const config = this.#readConfig();
|
|
104
|
-
* console.log(config.apiUrl);
|
|
105
|
-
* ```
|
|
106
|
-
*
|
|
107
|
-
* @returns The current configuration object.
|
|
108
|
-
*/
|
|
109
|
-
#readConfig() {
|
|
110
|
-
try {
|
|
111
|
-
if (!fs.existsSync(this.configPath)) {
|
|
112
|
-
return { apiUrl: DEFAULT_API_URL };
|
|
113
|
-
}
|
|
114
|
-
const content = fs.readFileSync(this.configPath, 'utf8');
|
|
115
|
-
const config = JSON.parse(content);
|
|
116
|
-
return {
|
|
117
|
-
apiUrl: DEFAULT_API_URL,
|
|
118
|
-
...config,
|
|
119
|
-
};
|
|
120
|
-
}
|
|
121
|
-
catch (_error) {
|
|
122
|
-
return { apiUrl: DEFAULT_API_URL };
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
/**
|
|
126
|
-
* Writes a single property to the configuration file on disk.
|
|
127
|
-
*/
|
|
128
|
-
writeProperty(key, value) {
|
|
129
|
-
const current = this.#readConfig();
|
|
130
|
-
current[key] = value;
|
|
131
|
-
this.#writeConfig(current);
|
|
132
|
-
}
|
|
133
|
-
/**
|
|
134
|
-
* Merges and writes the provided settings to the configuration file on disk.
|
|
135
|
-
* Creates the directory structure if it does not exist.
|
|
136
|
-
*
|
|
137
|
-
* @example
|
|
138
|
-
* ```typescript
|
|
139
|
-
* writeConfig({ token: 'my-secret-token' });
|
|
140
|
-
* ```
|
|
141
|
-
*
|
|
142
|
-
* @param config A partial config object containing keys to set or update.
|
|
143
|
-
*/
|
|
144
|
-
#writeConfig(config) {
|
|
145
|
-
try {
|
|
146
|
-
const current = this.#readConfig();
|
|
147
|
-
const updated = { ...current, ...config };
|
|
148
|
-
if (!fs.existsSync(this.configDir)) {
|
|
149
|
-
fs.mkdirSync(this.configDir, { recursive: true });
|
|
150
|
-
}
|
|
151
|
-
fs.writeFileSync(this.configPath, JSON.stringify(updated, null, 2), 'utf8');
|
|
152
|
-
}
|
|
153
|
-
catch (error) {
|
|
154
|
-
console.error('Failed to write configuration:', error);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Clears/removes a specific configuration key from the configuration file.
|
|
159
|
-
*
|
|
160
|
-
* @example
|
|
161
|
-
* ```typescript
|
|
162
|
-
* clearConfigKey('token');
|
|
163
|
-
* ```
|
|
164
|
-
*
|
|
165
|
-
* @param key The key of the CliConfig to remove.
|
|
166
|
-
*/
|
|
167
|
-
clearConfigKey(key) {
|
|
168
|
-
try {
|
|
169
|
-
const current = this.#readConfig();
|
|
170
|
-
// eslint-disable-next-line @typescript-eslint/no-dynamic-delete
|
|
171
|
-
delete current[key];
|
|
172
|
-
if (!fs.existsSync(this.configDir)) {
|
|
173
|
-
fs.mkdirSync(this.configDir, { recursive: true });
|
|
174
|
-
}
|
|
175
|
-
fs.writeFileSync(this.configPath, JSON.stringify(current, null, 2), 'utf8');
|
|
176
|
-
}
|
|
177
|
-
catch (error) {
|
|
178
|
-
console.error(`Failed to clear key ${key} from configuration:`, error);
|
|
179
|
-
}
|
|
180
|
-
}
|
|
181
|
-
/**
|
|
182
|
-
* Resets/removes the configuration file and directory.
|
|
183
|
-
*/
|
|
184
|
-
reset() {
|
|
185
|
-
try {
|
|
186
|
-
if (fs.existsSync(this.configPath)) {
|
|
187
|
-
fs.unlinkSync(this.configPath);
|
|
188
|
-
}
|
|
189
|
-
if (fs.existsSync(this.configDir)) {
|
|
190
|
-
const files = fs.readdirSync(this.configDir);
|
|
191
|
-
if (files.length === 0) {
|
|
192
|
-
fs.rmdirSync(this.configDir);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
this._resolved = null;
|
|
196
|
-
}
|
|
197
|
-
catch (error) {
|
|
198
|
-
console.error('Failed to reset configuration:', error);
|
|
199
|
-
}
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
export const config = new CliConfigManager();
|
package/dist/utils/formatter.js
DELETED
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
import { styleText } from 'node:util';
|
|
2
|
-
import { createRequire } from 'node:module';
|
|
3
|
-
const require = createRequire(import.meta.url);
|
|
4
|
-
const pkg = require('../../package.json');
|
|
5
|
-
const CLI_VERSION = pkg.version;
|
|
6
|
-
/**
|
|
7
|
-
* Standard error codes for the CLI.
|
|
8
|
-
*/
|
|
9
|
-
export var CliErrorCode;
|
|
10
|
-
(function (CliErrorCode) {
|
|
11
|
-
CliErrorCode["INVALID_ARGUMENT"] = "CLI_ERR_INVALID_ARGUMENT";
|
|
12
|
-
CliErrorCode["MISSING_REQUIRED"] = "CLI_ERR_MISSING_REQUIRED";
|
|
13
|
-
CliErrorCode["UNAUTHORIZED"] = "CLI_ERR_UNAUTHORIZED";
|
|
14
|
-
CliErrorCode["API_FAILURE"] = "CLI_ERR_API_FAILURE";
|
|
15
|
-
CliErrorCode["UNKNOWN"] = "CLI_ERR_UNKNOWN";
|
|
16
|
-
})(CliErrorCode || (CliErrorCode = {}));
|
|
17
|
-
/**
|
|
18
|
-
* Global CLI text formatting and styling helper object using Node.js's native styleText.
|
|
19
|
-
*/
|
|
20
|
-
export const style = {
|
|
21
|
-
green: (str) => styleText('green', str),
|
|
22
|
-
red: (str) => styleText('red', str),
|
|
23
|
-
yellow: (str) => styleText('yellow', str),
|
|
24
|
-
cyan: (str) => styleText('cyan', str),
|
|
25
|
-
dim: (str) => styleText('dim', str),
|
|
26
|
-
bold: (str) => styleText('bold', str),
|
|
27
|
-
};
|
|
28
|
-
/**
|
|
29
|
-
* Global CLI debug logger.
|
|
30
|
-
* Prints verbose info to stderr to avoid polluting stdout when piping/formatting.
|
|
31
|
-
*/
|
|
32
|
-
export function debug(module, message) {
|
|
33
|
-
const isDebug = process.argv.includes('--debug') ||
|
|
34
|
-
process.argv.includes('--verbose') ||
|
|
35
|
-
(typeof process !== 'undefined' &&
|
|
36
|
-
process.env.DEBUG &&
|
|
37
|
-
(process.env.DEBUG === '*' || process.env.DEBUG.includes('apinow')));
|
|
38
|
-
if (isDebug) {
|
|
39
|
-
console.error(style.dim(`[debug][${module}] ${message}`));
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
/**
|
|
43
|
-
* Aligning helper that formats rows and headers into a aligned column grid.
|
|
44
|
-
*
|
|
45
|
-
* @param rows - The data records to draw.
|
|
46
|
-
* @param columns - The property keys of the records to draw.
|
|
47
|
-
* @param customHeaders - Optional override titles for headers.
|
|
48
|
-
* @returns The formatted string table.
|
|
49
|
-
*/
|
|
50
|
-
function formatTable(rows, columns, customHeaders) {
|
|
51
|
-
if (rows.length === 0)
|
|
52
|
-
return 'No data available.';
|
|
53
|
-
const widths = {};
|
|
54
|
-
const displayHeaders = customHeaders || columns;
|
|
55
|
-
// Initialize widths with header sizes
|
|
56
|
-
for (let i = 0; i < columns.length; i++) {
|
|
57
|
-
const col = columns[i];
|
|
58
|
-
const headerName = displayHeaders[i] || col;
|
|
59
|
-
widths[col] = headerName.length;
|
|
60
|
-
}
|
|
61
|
-
// Adjust column widths based on content size
|
|
62
|
-
for (const row of rows) {
|
|
63
|
-
for (const col of columns) {
|
|
64
|
-
const val = typeof row[col] === 'object' && row[col] !== null ? JSON.stringify(row[col]) : String(row[col] ?? '');
|
|
65
|
-
if (val.length > (widths[col] || 0)) {
|
|
66
|
-
widths[col] = val.length;
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
// Build header row and separator
|
|
71
|
-
const header = columns
|
|
72
|
-
.map((col, idx) => {
|
|
73
|
-
const name = displayHeaders[idx] || col;
|
|
74
|
-
return name.toUpperCase().padEnd(widths[col]);
|
|
75
|
-
})
|
|
76
|
-
.join(' ');
|
|
77
|
-
const separator = columns.map((col) => '-'.repeat(widths[col])).join(' ');
|
|
78
|
-
// Build content rows
|
|
79
|
-
const formattedRows = rows.map((row) => {
|
|
80
|
-
return columns
|
|
81
|
-
.map((col) => {
|
|
82
|
-
const val = typeof row[col] === 'object' && row[col] !== null ? JSON.stringify(row[col]) : String(row[col] ?? '');
|
|
83
|
-
return val.padEnd(widths[col]);
|
|
84
|
-
})
|
|
85
|
-
.join(' ');
|
|
86
|
-
});
|
|
87
|
-
return [header, separator, ...formattedRows].join('\n');
|
|
88
|
-
}
|
|
89
|
-
/**
|
|
90
|
-
* Output formatter class providing type-safe methods to write to standard output / error.
|
|
91
|
-
*/
|
|
92
|
-
export class Formatter {
|
|
93
|
-
format;
|
|
94
|
-
/**
|
|
95
|
-
* Constructs a Formatter instance.
|
|
96
|
-
* @param options - Formatting configuration.
|
|
97
|
-
*/
|
|
98
|
-
constructor(options = {}) {
|
|
99
|
-
this.format = options.format || 'text';
|
|
100
|
-
}
|
|
101
|
-
/**
|
|
102
|
-
* Outputs a success message to console.
|
|
103
|
-
*
|
|
104
|
-
* @param message - Short description of success.
|
|
105
|
-
* @param data - Optional data payload to print under the success.
|
|
106
|
-
*/
|
|
107
|
-
success(message, data) {
|
|
108
|
-
if (this.format === 'json') {
|
|
109
|
-
console.log(JSON.stringify({ success: true, message, data }, null, 2));
|
|
110
|
-
}
|
|
111
|
-
else {
|
|
112
|
-
console.log(style.green(`✔ ${message}`));
|
|
113
|
-
if (data !== undefined) {
|
|
114
|
-
if (typeof data === 'object' && data !== null) {
|
|
115
|
-
this.object(data);
|
|
116
|
-
}
|
|
117
|
-
else {
|
|
118
|
-
console.log(data);
|
|
119
|
-
}
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
}
|
|
123
|
-
error(error) {
|
|
124
|
-
const code = error.code || CliErrorCode.UNKNOWN;
|
|
125
|
-
if (this.format === 'json') {
|
|
126
|
-
console.error(JSON.stringify({ success: false, code, cliVersion: CLI_VERSION, ...error }, null, 2));
|
|
127
|
-
}
|
|
128
|
-
else {
|
|
129
|
-
console.error(style.red(`✖ Error [${code}]: ${error.message}`));
|
|
130
|
-
if (error.detail) {
|
|
131
|
-
console.error(style.dim(error.detail));
|
|
132
|
-
}
|
|
133
|
-
if (error.errors && error.errors.length > 0) {
|
|
134
|
-
console.error('\nValidation errors:');
|
|
135
|
-
for (const err of error.errors) {
|
|
136
|
-
console.error(` - ${style.yellow(err.field)}: ${err.message}`);
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
console.error(style.dim(`(cli v${CLI_VERSION})`));
|
|
140
|
-
if (code === CliErrorCode.UNKNOWN) {
|
|
141
|
-
const repoUrl = 'https://github.com/api-now/cli';
|
|
142
|
-
const title = encodeURIComponent(`[BUG] CLI Crash: ${error.message}`);
|
|
143
|
-
const body = encodeURIComponent(`**CLI Version:** ${CLI_VERSION}
|
|
144
|
-
**Error Code:** ${code}
|
|
145
|
-
|
|
146
|
-
**Error Message:**
|
|
147
|
-
${error.message}
|
|
148
|
-
|
|
149
|
-
**Error Details / Stack:**
|
|
150
|
-
\`\`\`
|
|
151
|
-
${error.detail || 'No stack trace available'}
|
|
152
|
-
\`\`\`
|
|
153
|
-
`);
|
|
154
|
-
const bugReportUrl = `${repoUrl}/issues/new?title=${title}&body=${body}`;
|
|
155
|
-
console.error(`\nPlease report this bug: ${bugReportUrl}`);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
}
|
|
159
|
-
/**
|
|
160
|
-
* Formats and prints a collection of records.
|
|
161
|
-
*
|
|
162
|
-
* @param rows - The array of items/records.
|
|
163
|
-
* @param columns - The keys to extract and show as table columns.
|
|
164
|
-
* @param options - Customization options such as custom headers.
|
|
165
|
-
*/
|
|
166
|
-
list(rows, columns, options = {}) {
|
|
167
|
-
if (this.format === 'json') {
|
|
168
|
-
console.log(JSON.stringify(rows, null, 2));
|
|
169
|
-
}
|
|
170
|
-
else {
|
|
171
|
-
if (rows.length === 0) {
|
|
172
|
-
console.log('No records found.');
|
|
173
|
-
return;
|
|
174
|
-
}
|
|
175
|
-
console.log(formatTable(rows, columns, options.headers));
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
/**
|
|
179
|
-
* Prints a single record/object as key-value pairs.
|
|
180
|
-
*
|
|
181
|
-
* @param data - The object to print.
|
|
182
|
-
*/
|
|
183
|
-
object(data) {
|
|
184
|
-
const record = data;
|
|
185
|
-
if (this.format === 'json') {
|
|
186
|
-
console.log(JSON.stringify(record, null, 2));
|
|
187
|
-
}
|
|
188
|
-
else {
|
|
189
|
-
for (const [key, value] of Object.entries(record)) {
|
|
190
|
-
const valStr = typeof value === 'object' && value !== null ? JSON.stringify(value) : String(value);
|
|
191
|
-
console.log(`${style.bold(key)}: ${valStr}`);
|
|
192
|
-
}
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
}
|
package/dist/utils/org-setup.js
DELETED
|
@@ -1,96 +0,0 @@
|
|
|
1
|
-
import { config } from './config.js';
|
|
2
|
-
import { style } from './formatter.js';
|
|
3
|
-
import { askInput, askConfirm } from './prompt.js';
|
|
4
|
-
import { handleApiError } from './api.js';
|
|
5
|
-
/**
|
|
6
|
-
* Checks if the user has any organizations. If not, guides them through setting up their first organization.
|
|
7
|
-
*
|
|
8
|
-
* @param client - The StoreSdk client.
|
|
9
|
-
* @param formatter - The current output formatter.
|
|
10
|
-
*/
|
|
11
|
-
export async function setupFirstOrganizationIfNeeded(client, formatter) {
|
|
12
|
-
// 1. Fetch organization list
|
|
13
|
-
let orgs;
|
|
14
|
-
try {
|
|
15
|
-
const res = await client.organizations.list({ token: client.token });
|
|
16
|
-
orgs = res.data || [];
|
|
17
|
-
}
|
|
18
|
-
catch (_err) {
|
|
19
|
-
// If we fail to fetch orgs (e.g. server is down or auth issue), return early
|
|
20
|
-
return;
|
|
21
|
-
}
|
|
22
|
-
// If the user already has organizations, check if a default is set.
|
|
23
|
-
if (orgs.length > 0) {
|
|
24
|
-
if (!config.resolved.org) {
|
|
25
|
-
const firstOrg = orgs[0];
|
|
26
|
-
const firstOrgId = firstOrg.key;
|
|
27
|
-
config.writeProperty('org', firstOrgId);
|
|
28
|
-
if (formatter.format !== 'json') {
|
|
29
|
-
console.log('\n' + style.cyan(`ℹ Automatically set default organization to "${firstOrg.name}" (${firstOrgId}).`));
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
34
|
-
// If the format is JSON, do not prompt interactively.
|
|
35
|
-
if (formatter.format === 'json') {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
console.log('\n' + style.yellow('⚠ It looks like you do not have any organizations yet.'));
|
|
39
|
-
console.log("Let's set up your first organization now.");
|
|
40
|
-
let name = '';
|
|
41
|
-
while (!name) {
|
|
42
|
-
name = await askInput('Enter organization name:');
|
|
43
|
-
if (!name) {
|
|
44
|
-
console.log(style.red('Error: Organization name cannot be empty.'));
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
// 2. Generate slug from name using backend API
|
|
48
|
-
let slug;
|
|
49
|
-
try {
|
|
50
|
-
slug = await client.organizations.slugs.generate(name, { token: client.token });
|
|
51
|
-
}
|
|
52
|
-
catch (_err) {
|
|
53
|
-
slug = name
|
|
54
|
-
.toLowerCase()
|
|
55
|
-
.replace(/[^a-z0-9]+/g, '-')
|
|
56
|
-
.replace(/(^-|-$)/g, '');
|
|
57
|
-
}
|
|
58
|
-
const useSuggested = await askConfirm(`Use suggested organization slug "${slug}"?`, true);
|
|
59
|
-
if (!useSuggested) {
|
|
60
|
-
let isValid = false;
|
|
61
|
-
while (!isValid) {
|
|
62
|
-
const customSlug = await askInput('Enter organization slug:');
|
|
63
|
-
if (!customSlug) {
|
|
64
|
-
console.log(style.red('Error: Slug cannot be empty.'));
|
|
65
|
-
continue;
|
|
66
|
-
}
|
|
67
|
-
try {
|
|
68
|
-
const valData = await client.organizations.slugs.validate(customSlug, { token: client.token });
|
|
69
|
-
if (valData.valid) {
|
|
70
|
-
slug = customSlug;
|
|
71
|
-
isValid = true;
|
|
72
|
-
}
|
|
73
|
-
else {
|
|
74
|
-
console.log(style.red(`Error: ${valData.reason || 'Invalid slug'}`));
|
|
75
|
-
}
|
|
76
|
-
}
|
|
77
|
-
catch (_err) {
|
|
78
|
-
console.log(style.red('Error validating slug with server. Please try another slug.'));
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
// 3. Create the organization
|
|
83
|
-
console.log(`\nCreating organization "${name}" with slug "${slug}"...`);
|
|
84
|
-
try {
|
|
85
|
-
const createdOrg = await client.organizations.create(name, slug, { token: client.token });
|
|
86
|
-
const orgId = createdOrg.key;
|
|
87
|
-
// 4. Set as default
|
|
88
|
-
config.writeProperty('org', orgId);
|
|
89
|
-
console.log('\n' + style.green(`✔ Organization "${name}" created successfully!`));
|
|
90
|
-
console.log(`Set as default organization: ${style.cyan(orgId)}`);
|
|
91
|
-
}
|
|
92
|
-
catch (err) {
|
|
93
|
-
const apiErr = handleApiError(err);
|
|
94
|
-
console.log(style.red(`Error creating organization: ${apiErr.message}`));
|
|
95
|
-
}
|
|
96
|
-
}
|
package/dist/utils/prompt.js
DELETED
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { input, confirm, select } from '@inquirer/prompts';
|
|
2
|
-
/**
|
|
3
|
-
* Prompts the user with a text input using @inquirer/prompts.
|
|
4
|
-
*
|
|
5
|
-
* @param message - The prompt message to show.
|
|
6
|
-
* @param defaultValue - An optional default value.
|
|
7
|
-
* @returns A promise resolving to the user's input.
|
|
8
|
-
*/
|
|
9
|
-
export async function askInput(message, defaultValue) {
|
|
10
|
-
const result = await input({
|
|
11
|
-
message,
|
|
12
|
-
default: defaultValue,
|
|
13
|
-
});
|
|
14
|
-
return result.trim();
|
|
15
|
-
}
|
|
16
|
-
/**
|
|
17
|
-
* Prompts the user with a confirmation (y/n) question.
|
|
18
|
-
*
|
|
19
|
-
* @param message - The prompt message to show.
|
|
20
|
-
* @param defaultValue - The default boolean choice (defaults to true).
|
|
21
|
-
* @returns A promise resolving to the user's choice.
|
|
22
|
-
*/
|
|
23
|
-
export async function askConfirm(message, defaultValue = true) {
|
|
24
|
-
return confirm({
|
|
25
|
-
message,
|
|
26
|
-
default: defaultValue,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
/**
|
|
30
|
-
* Prompts the user to make a choice from a select dropdown.
|
|
31
|
-
*
|
|
32
|
-
* @param message - The prompt message to show.
|
|
33
|
-
* @param choices - List of choice objects.
|
|
34
|
-
* @returns A promise resolving to the selected value.
|
|
35
|
-
*/
|
|
36
|
-
export async function askSelect(message, choices) {
|
|
37
|
-
return select({
|
|
38
|
-
message,
|
|
39
|
-
choices,
|
|
40
|
-
});
|
|
41
|
-
}
|