wedos-cli 1.0.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.
@@ -0,0 +1,329 @@
1
+ import { Command } from 'commander';
2
+ import inquirer from 'inquirer';
3
+ import ora from 'ora';
4
+ import chalk from 'chalk';
5
+ import { WapiClient, isSuccess } from '../lib/wapi.js';
6
+ import { getCredentials, getConfig } from '../lib/config.js';
7
+ import { formatResponse, printError, printSuccess, printWarning, printInfo } from '../lib/output.js';
8
+
9
+ export function createDomainCommand() {
10
+ const domain = new Command('domain')
11
+ .description('Správa domén');
12
+
13
+ // List domains
14
+ domain
15
+ .command('list')
16
+ .alias('ls')
17
+ .description('Seznam všech domén v účtu')
18
+ .option('-s, --status <status>', 'Filtrovat podle stavu (active, expired, deleted)')
19
+ .option('--json', 'Výstup jako JSON')
20
+ .action(async (options) => {
21
+ const spinner = ora('Načítám domény...').start();
22
+ try {
23
+ const client = new WapiClient(getCredentials());
24
+ const response = await client.domainsList(options.status);
25
+ spinner.stop();
26
+ formatResponse(response, { format: options.json ? 'json' : undefined });
27
+ } catch (error) {
28
+ spinner.stop();
29
+ printError(error);
30
+ process.exit(1);
31
+ }
32
+ });
33
+
34
+ // Check domain availability
35
+ domain
36
+ .command('check <domains...>')
37
+ .description('Kontrola dostupnosti domény (lze zadat více)')
38
+ .option('--json', 'Výstup jako JSON')
39
+ .action(async (domains, options) => {
40
+ const spinner = ora('Kontroluji dostupnost...').start();
41
+ try {
42
+ const client = new WapiClient(getCredentials());
43
+ const response = await client.domainCheck(domains);
44
+ spinner.stop();
45
+
46
+ if (options.json) {
47
+ formatResponse(response, { format: 'json' });
48
+ } else {
49
+ const code = parseInt(response.code);
50
+ if (isSuccess(code) && response.data) {
51
+ console.log(chalk.bold('\nDostupnost domén:\n'));
52
+ for (const [domain, info] of Object.entries(response.data)) {
53
+ const status = info.avail === '1' || info.avail === 1;
54
+ const icon = status ? chalk.green('✓') : chalk.red('✗');
55
+ const text = status ? chalk.green('Volná') : chalk.red('Zaregistrovaná');
56
+ console.log(`${icon} ${domain} - ${text}`);
57
+ }
58
+ } else {
59
+ formatResponse(response);
60
+ }
61
+ }
62
+ } catch (error) {
63
+ spinner.stop();
64
+ printError(error);
65
+ process.exit(1);
66
+ }
67
+ });
68
+
69
+ // Get domain info
70
+ domain
71
+ .command('info <domains...>')
72
+ .description('Detailní informace o doméně')
73
+ .option('--json', 'Výstup jako JSON')
74
+ .action(async (domains, options) => {
75
+ const spinner = ora('Načítám informace o doméně...').start();
76
+ try {
77
+ const client = new WapiClient(getCredentials());
78
+ const response = await client.domainInfo(domains);
79
+ spinner.stop();
80
+ formatResponse(response, { format: options.json ? 'json' : undefined });
81
+ } catch (error) {
82
+ spinner.stop();
83
+ printError(error);
84
+ process.exit(1);
85
+ }
86
+ });
87
+
88
+ // Register new domain
89
+ domain
90
+ .command('register <domain>')
91
+ .alias('create')
92
+ .description('Registrace nové domény')
93
+ .option('-p, --period <years>', 'Délka registrace v letech', '1')
94
+ .option('-c, --contact <id>', 'ID kontaktu vlastníka')
95
+ .option('-n, --nsset <nsset>', 'Název NSSET (např. WEDOS)')
96
+ .option('-d, --dns <servers>', 'Vlastní DNS servery (oddělené čárkou, např. "ns1.example.com,ns2.example.com")')
97
+ .option('-r, --rules <name>', 'Osoba, která souhlasila s podmínkami (povinné)')
98
+ .option('-i, --interactive', 'Interaktivní režim')
99
+ .option('--json', 'Výstup jako JSON')
100
+ .action(async (domainName, options) => {
101
+ try {
102
+ const client = new WapiClient(getCredentials());
103
+ const config = getConfig();
104
+
105
+ // First check availability
106
+ const spinner = ora('Kontroluji dostupnost domény...').start();
107
+ const checkResponse = await client.domainCheck(domainName);
108
+ spinner.stop();
109
+
110
+ const checkCode = parseInt(checkResponse.code);
111
+ if (!isSuccess(checkCode)) {
112
+ formatResponse(checkResponse);
113
+ process.exit(1);
114
+ }
115
+
116
+ const domainData = checkResponse.data?.[domainName];
117
+ if (!domainData || domainData.avail !== '1') {
118
+ printError(`Doména ${domainName} není k dispozici pro registraci`);
119
+ if (domainData?.reason) {
120
+ printInfo(`Důvod: ${domainData.reason}`);
121
+ }
122
+ process.exit(1);
123
+ }
124
+
125
+ printSuccess(`Doména ${domainName} je volná!`);
126
+
127
+ // Get contact if not provided
128
+ let ownerContact = options.contact || config.defaultContact;
129
+ let rules = options.rules;
130
+
131
+ if (!ownerContact || !rules) {
132
+ if (!options.interactive) {
133
+ printError('ID kontaktu a jméno pro pravidla jsou povinné. Použij --contact a --rules, nebo --interactive');
134
+ console.log('\nPříklad:');
135
+ console.log(` wedos domain register ${domainName} --contact MYCONTACT-CZ --rules "Jan Novák"`);
136
+ process.exit(1);
137
+ }
138
+
139
+ // Interactive mode
140
+ const answers = await inquirer.prompt([
141
+ {
142
+ type: 'input',
143
+ name: 'contact',
144
+ message: 'ID kontaktu vlastníka:',
145
+ when: !ownerContact,
146
+ validate: (input) => input.length > 0 || 'ID kontaktu je povinné',
147
+ },
148
+ {
149
+ type: 'input',
150
+ name: 'rules',
151
+ message: 'Celé jméno osoby souhlasící s podmínkami:',
152
+ when: !rules,
153
+ validate: (input) => input.length > 0 || 'Jméno je povinné',
154
+ },
155
+ ]);
156
+
157
+ ownerContact = ownerContact || answers.contact;
158
+ rules = rules || answers.rules;
159
+ }
160
+
161
+ // Register domain
162
+ const registerSpinner = ora('Registruji doménu...').start();
163
+
164
+ // Build DNS configuration
165
+ const createOptions = {
166
+ name: domainName,
167
+ period: parseInt(options.period),
168
+ ownerContact,
169
+ rules,
170
+ };
171
+
172
+ // Use custom DNS servers if provided, otherwise use NSSET
173
+ if (options.dns) {
174
+ // Parse comma-separated DNS servers into proper format
175
+ const servers = options.dns.split(',').map(s => s.trim());
176
+ createOptions.dns = servers.map(server => ({ name: server }));
177
+ printInfo(`Používám vlastní DNS: ${servers.join(', ')}`);
178
+ } else {
179
+ createOptions.nsset = options.nsset || config.defaultNsset || 'WEDOS';
180
+ }
181
+
182
+ const response = await client.domainCreate(createOptions);
183
+ registerSpinner.stop();
184
+
185
+ formatResponse(response, { format: options.json ? 'json' : undefined });
186
+
187
+ if (isSuccess(parseInt(response.code))) {
188
+ printSuccess(`Doména ${domainName} úspěšně zaregistrována!`);
189
+ if (response.data?.expiration) {
190
+ printInfo(`Expirace: ${response.data.expiration}`);
191
+ }
192
+ }
193
+ } catch (error) {
194
+ printError(error);
195
+ process.exit(1);
196
+ }
197
+ });
198
+
199
+ // Renew domain
200
+ domain
201
+ .command('renew <domain>')
202
+ .description('Prodloužení registrace domény')
203
+ .option('-p, --period <years>', 'Délka prodloužení v letech', '1')
204
+ .option('--json', 'Výstup jako JSON')
205
+ .action(async (domainName, options) => {
206
+ const spinner = ora('Prodlužuji doménu...').start();
207
+ try {
208
+ const client = new WapiClient(getCredentials());
209
+ const response = await client.domainRenew(domainName, parseInt(options.period));
210
+ spinner.stop();
211
+ formatResponse(response, { format: options.json ? 'json' : undefined });
212
+ } catch (error) {
213
+ spinner.stop();
214
+ printError(error);
215
+ process.exit(1);
216
+ }
217
+ });
218
+
219
+ // Update DNS/NSSET
220
+ domain
221
+ .command('update-ns <domain>')
222
+ .description('Aktualizace DNS serverů domény')
223
+ .option('-n, --nsset <nsset>', 'Název NSSET')
224
+ .option('--json', 'Výstup jako JSON')
225
+ .action(async (domainName, options) => {
226
+ if (!options.nsset) {
227
+ printError('NSSET je povinný. Použij --nsset <název>');
228
+ process.exit(1);
229
+ }
230
+
231
+ const spinner = ora('Aktualizuji DNS...').start();
232
+ try {
233
+ const client = new WapiClient(getCredentials());
234
+ const response = await client.domainUpdateNs(domainName, options.nsset);
235
+ spinner.stop();
236
+ formatResponse(response, { format: options.json ? 'json' : undefined });
237
+ } catch (error) {
238
+ spinner.stop();
239
+ printError(error);
240
+ process.exit(1);
241
+ }
242
+ });
243
+
244
+ // Transfer check
245
+ domain
246
+ .command('transfer-check <domain>')
247
+ .description('Kontrola možnosti transferu domény')
248
+ .option('--json', 'Výstup jako JSON')
249
+ .action(async (domainName, options) => {
250
+ const spinner = ora('Kontroluji možnost transferu...').start();
251
+ try {
252
+ const client = new WapiClient(getCredentials());
253
+ const response = await client.domainTransferCheck(domainName);
254
+ spinner.stop();
255
+ formatResponse(response, { format: options.json ? 'json' : undefined });
256
+ } catch (error) {
257
+ spinner.stop();
258
+ printError(error);
259
+ process.exit(1);
260
+ }
261
+ });
262
+
263
+ // Transfer domain
264
+ domain
265
+ .command('transfer <domain>')
266
+ .description('Transfer domény od jiného registrátora')
267
+ .requiredOption('-a, --auth <authInfo>', 'AUTH-ID domény')
268
+ .option('-c, --contact <id>', 'ID kontaktu vlastníka (pro non-CZ domény)')
269
+ .option('-n, --nsset <nsset>', 'Název NSSET')
270
+ .requiredOption('-r, --rules <name>', 'Osoba, která souhlasila s podmínkami')
271
+ .option('--json', 'Výstup jako JSON')
272
+ .action(async (domainName, options) => {
273
+ const spinner = ora('Zahajuji transfer...').start();
274
+ try {
275
+ const client = new WapiClient(getCredentials());
276
+ const response = await client.domainTransfer({
277
+ name: domainName,
278
+ authInfo: options.auth,
279
+ ownerContact: options.contact,
280
+ nsset: options.nsset,
281
+ rules: options.rules,
282
+ });
283
+ spinner.stop();
284
+ formatResponse(response, { format: options.json ? 'json' : undefined });
285
+ } catch (error) {
286
+ spinner.stop();
287
+ printError(error);
288
+ process.exit(1);
289
+ }
290
+ });
291
+
292
+ // Send AUTH-ID
293
+ domain
294
+ .command('send-auth <domain>')
295
+ .description('Odeslání AUTH-ID na e-mail vlastníka')
296
+ .action(async (domainName) => {
297
+ const spinner = ora('Odesílám AUTH-ID...').start();
298
+ try {
299
+ const client = new WapiClient(getCredentials());
300
+ const response = await client.domainSendAuthInfo(domainName);
301
+ spinner.stop();
302
+ formatResponse(response);
303
+ } catch (error) {
304
+ spinner.stop();
305
+ printError(error);
306
+ process.exit(1);
307
+ }
308
+ });
309
+
310
+ // Check TLD period
311
+ domain
312
+ .command('period-check <tld> <years>')
313
+ .description('Kontrola platnosti délky registrace pro TLD')
314
+ .action(async (tld, years) => {
315
+ const spinner = ora('Kontroluji...').start();
316
+ try {
317
+ const client = new WapiClient(getCredentials());
318
+ const response = await client.domainTldPeriodCheck(tld, parseInt(years));
319
+ spinner.stop();
320
+ formatResponse(response);
321
+ } catch (error) {
322
+ spinner.stop();
323
+ printError(error);
324
+ process.exit(1);
325
+ }
326
+ });
327
+
328
+ return domain;
329
+ }
package/src/index.js ADDED
@@ -0,0 +1,240 @@
1
+ #!/usr/bin/env node
2
+
3
+ import { Command } from 'commander';
4
+ import chalk from 'chalk';
5
+ import ora from 'ora';
6
+ import boxen from 'boxen';
7
+
8
+ import { createDomainCommand } from './commands/domain.js';
9
+ import { createDnsCommand } from './commands/dns.js';
10
+ import { createContactCommand } from './commands/contact.js';
11
+ import { createConfigCommand } from './commands/config.js';
12
+ import { WapiClient, isSuccess } from './lib/wapi.js';
13
+ import { getCredentials, hasCredentials } from './lib/config.js';
14
+ import { formatResponse, printError, printSuccess, printInfo, printHeader, printDomainCheck, printSuccessBox, printErrorBox } from './lib/output.js';
15
+ import { formatDateTime } from './lib/locale.js';
16
+ import { printBanner, wedosGradient } from './lib/ui.js';
17
+
18
+ const program = new Command();
19
+
20
+ program
21
+ .name('wedos')
22
+ .description('CLI nástroj pro WEDOS WAPI - registrace domén, správa DNS a další')
23
+ .version('1.0.0')
24
+ .hook('preAction', (thisCommand) => {
25
+ // Show banner for main commands (not subcommands)
26
+ const commandName = thisCommand.args[0];
27
+ if (!commandName || ['--help', '-h', '--version', '-V'].includes(commandName)) {
28
+ return;
29
+ }
30
+ });
31
+
32
+ // Add subcommands
33
+ program.addCommand(createDomainCommand());
34
+ program.addCommand(createDnsCommand());
35
+ program.addCommand(createContactCommand());
36
+ program.addCommand(createConfigCommand());
37
+
38
+ // Ping command
39
+ program
40
+ .command('ping')
41
+ .description('Test připojení k API a ověření autentizace')
42
+ .action(async () => {
43
+ if (!hasCredentials()) {
44
+ printErrorBox('Přihlašovací údaje nejsou nastaveny', 'Spusť: wedos config init');
45
+ process.exit(1);
46
+ }
47
+
48
+ printBanner();
49
+
50
+ const spinner = ora({
51
+ text: 'Testuji připojení k WAPI...',
52
+ spinner: 'dots12',
53
+ color: 'cyan',
54
+ }).start();
55
+
56
+ try {
57
+ const client = new WapiClient(getCredentials());
58
+ const response = await client.ping();
59
+ spinner.stop();
60
+
61
+ if (isSuccess(parseInt(response.code))) {
62
+ const time = formatDateTime(new Date(response.timestamp * 1000));
63
+
64
+ console.log(boxen(
65
+ chalk.green.bold('✓ Připojení úspěšné!\n\n') +
66
+ chalk.gray('Server: ') + chalk.white('api.wedos.com\n') +
67
+ chalk.gray('Čas: ') + chalk.white(time + '\n') +
68
+ chalk.gray('Transakce: ') + chalk.white(response.svTRID || 'N/A'),
69
+ {
70
+ padding: 1,
71
+ borderStyle: 'round',
72
+ borderColor: 'green',
73
+ title: '🌐 WAPI Status',
74
+ titleAlignment: 'center',
75
+ }
76
+ ));
77
+ } else {
78
+ formatResponse(response);
79
+ }
80
+ } catch (error) {
81
+ spinner.stop();
82
+ printError(error);
83
+ process.exit(1);
84
+ }
85
+ });
86
+
87
+ // Poll command (for async operations)
88
+ program
89
+ .command('poll')
90
+ .description('Kontrola čekajících notifikací')
91
+ .option('--ack <msgid>', 'Potvrdit notifikaci podle ID')
92
+ .option('--json', 'Výstup jako JSON')
93
+ .action(async (options) => {
94
+ if (!hasCredentials()) {
95
+ printErrorBox('Přihlašovací údaje nejsou nastaveny', 'Spusť: wedos config init');
96
+ process.exit(1);
97
+ }
98
+
99
+ const spinner = ora({
100
+ text: 'Kontroluji notifikace...',
101
+ spinner: 'dots12',
102
+ color: 'cyan',
103
+ }).start();
104
+
105
+ try {
106
+ const client = new WapiClient(getCredentials());
107
+
108
+ if (options.ack) {
109
+ const response = await client.pollAck(options.ack);
110
+ spinner.stop();
111
+ formatResponse(response, { format: options.json ? 'json' : undefined });
112
+ } else {
113
+ const response = await client.pollReq();
114
+ spinner.stop();
115
+
116
+ if (parseInt(response.code) === 1300) {
117
+ printInfo('Žádné čekající notifikace');
118
+ } else {
119
+ formatResponse(response, { format: options.json ? 'json' : undefined });
120
+ }
121
+ }
122
+ } catch (error) {
123
+ spinner.stop();
124
+ printError(error);
125
+ process.exit(1);
126
+ }
127
+ });
128
+
129
+ // Quick check command (shortcut)
130
+ program
131
+ .command('check <domains...>')
132
+ .description('Rychlá kontrola dostupnosti domény')
133
+ .option('--json', 'Výstup jako JSON')
134
+ .action(async (domains, options) => {
135
+ if (!hasCredentials()) {
136
+ printErrorBox('Přihlašovací údaje nejsou nastaveny', 'Spusť: wedos config init');
137
+ process.exit(1);
138
+ }
139
+
140
+ const spinner = ora({
141
+ text: `Kontroluji ${domains.length} doména(y)...`,
142
+ spinner: 'dots12',
143
+ color: 'cyan',
144
+ }).start();
145
+
146
+ try {
147
+ const client = new WapiClient(getCredentials());
148
+ const response = await client.domainCheck(domains);
149
+ spinner.stop();
150
+
151
+ if (options.json) {
152
+ formatResponse(response, { format: 'json' });
153
+ } else {
154
+ const code = parseInt(response.code);
155
+ if (isSuccess(code) && response.data) {
156
+ printHeader('Dostupnost domén', '🔍');
157
+ console.log('');
158
+
159
+ for (const [domain, info] of Object.entries(response.data)) {
160
+ const available = info.avail === '1' || info.avail === 1;
161
+ printDomainCheck(domain, available, info.reason);
162
+ }
163
+ console.log('');
164
+ } else {
165
+ formatResponse(response);
166
+ }
167
+ }
168
+ } catch (error) {
169
+ spinner.stop();
170
+ printError(error);
171
+ process.exit(1);
172
+ }
173
+ });
174
+
175
+ // Quick register command (shortcut)
176
+ program
177
+ .command('register <domain>')
178
+ .description('Rychlá registrace domény')
179
+ .option('-p, --period <years>', 'Délka registrace v letech', '1')
180
+ .option('-c, --contact <id>', 'ID kontaktu vlastníka')
181
+ .option('-n, --nsset <nsset>', 'Název NSSET')
182
+ .option('-d, --dns <servers>', 'Vlastní DNS servery (oddělené čárkou)')
183
+ .option('-r, --rules <name>', 'Osoba, která souhlasila s podmínkami')
184
+ .option('-i, --interactive', 'Interaktivní režim')
185
+ .action(async (domain, options) => {
186
+ // Delegate to domain register
187
+ const args = ['domain', 'register', domain];
188
+ if (options.period) args.push('-p', options.period);
189
+ if (options.contact) args.push('-c', options.contact);
190
+ if (options.nsset) args.push('-n', options.nsset);
191
+ if (options.dns) args.push('-d', options.dns);
192
+ if (options.rules) args.push('-r', options.rules);
193
+ if (options.interactive) args.push('-i');
194
+
195
+ process.argv = [process.argv[0], process.argv[1], ...args];
196
+ await program.parseAsync(process.argv);
197
+ });
198
+
199
+ // Custom help
200
+ program.configureHelp({
201
+ sortSubcommands: true,
202
+ subcommandTerm: (cmd) => chalk.cyan(cmd.name()) + (cmd.alias() ? chalk.gray(` (${cmd.alias()})`) : ''),
203
+ });
204
+
205
+ // Help with examples
206
+ program.on('--help', () => {
207
+ console.log('');
208
+ console.log(chalk.gray('─'.repeat(55)));
209
+ console.log('');
210
+ console.log(chalk.bold.cyan(' Příklady:'));
211
+ console.log('');
212
+ console.log(chalk.gray(' # Počáteční nastavení'));
213
+ console.log(' $ ' + chalk.white('wedos config init'));
214
+ console.log('');
215
+ console.log(chalk.gray(' # Test připojení'));
216
+ console.log(' $ ' + chalk.white('wedos ping'));
217
+ console.log('');
218
+ console.log(chalk.gray(' # Kontrola dostupnosti domény'));
219
+ console.log(' $ ' + chalk.white('wedos check mojedomena.cz'));
220
+ console.log('');
221
+ console.log(chalk.gray(' # Seznam domén'));
222
+ console.log(' $ ' + chalk.white('wedos domain list'));
223
+ console.log('');
224
+ console.log(chalk.gray(' # Přidání DNS záznamu'));
225
+ console.log(' $ ' + chalk.white('wedos dns quick-add domena.cz -a 1.2.3.4'));
226
+ console.log('');
227
+ console.log(chalk.gray('─'.repeat(55)));
228
+ console.log('');
229
+ console.log(chalk.gray(' 📖 Nápověda k příkazu: ') + chalk.cyan('wedos <příkaz> --help'));
230
+ console.log('');
231
+ });
232
+
233
+ // Show banner before help
234
+ const originalHelp = program.helpInformation.bind(program);
235
+ program.helpInformation = function() {
236
+ printBanner();
237
+ return originalHelp();
238
+ };
239
+
240
+ program.parse();
@@ -0,0 +1,72 @@
1
+ import Conf from 'conf';
2
+ import { homedir } from 'os';
3
+ import { join } from 'path';
4
+ import { existsSync, readFileSync } from 'fs';
5
+
6
+ const config = new Conf({
7
+ projectName: 'wedos-cli',
8
+ schema: {
9
+ username: { type: 'string' },
10
+ password: { type: 'string' },
11
+ testMode: { type: 'boolean', default: false },
12
+ defaultNsset: { type: 'string', default: 'WEDOS' },
13
+ defaultContact: { type: 'string' },
14
+ outputFormat: { type: 'string', enum: ['table', 'json'], default: 'table' },
15
+ }
16
+ });
17
+
18
+ // Also check for .wedosrc in home directory or current directory
19
+ function loadRcFile() {
20
+ const paths = [
21
+ join(process.cwd(), '.wedosrc'),
22
+ join(homedir(), '.wedosrc'),
23
+ ];
24
+
25
+ for (const path of paths) {
26
+ if (existsSync(path)) {
27
+ try {
28
+ const content = readFileSync(path, 'utf-8');
29
+ return JSON.parse(content);
30
+ } catch {
31
+ // Ignore parse errors
32
+ }
33
+ }
34
+ }
35
+ return null;
36
+ }
37
+
38
+ export function getConfig() {
39
+ const rcConfig = loadRcFile();
40
+
41
+ return {
42
+ username: process.env.WEDOS_USERNAME || rcConfig?.username || config.get('username'),
43
+ password: process.env.WEDOS_PASSWORD || rcConfig?.password || config.get('password'),
44
+ testMode: process.env.WEDOS_TEST_MODE === '1' || rcConfig?.testMode || config.get('testMode'),
45
+ defaultNsset: rcConfig?.defaultNsset || config.get('defaultNsset'),
46
+ defaultContact: rcConfig?.defaultContact || config.get('defaultContact'),
47
+ outputFormat: rcConfig?.outputFormat || config.get('outputFormat'),
48
+ };
49
+ }
50
+
51
+ export function setConfig(key, value) {
52
+ config.set(key, value);
53
+ }
54
+
55
+ export function clearConfig() {
56
+ config.clear();
57
+ }
58
+
59
+ export function hasCredentials() {
60
+ const cfg = getConfig();
61
+ return !!(cfg.username && cfg.password);
62
+ }
63
+
64
+ export function getCredentials() {
65
+ const cfg = getConfig();
66
+ if (!cfg.username || !cfg.password) {
67
+ throw new Error('WEDOS credentials not configured. Run: wedos config --username <email> --password <wapi-password>');
68
+ }
69
+ return { username: cfg.username, password: cfg.password, testMode: cfg.testMode };
70
+ }
71
+
72
+ export default config;