@edgible-team/cli 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. package/LICENSE +136 -0
  2. package/README.md +450 -0
  3. package/dist/client/api-client.js +1057 -0
  4. package/dist/client/index.js +21 -0
  5. package/dist/commands/agent.js +1280 -0
  6. package/dist/commands/ai.js +608 -0
  7. package/dist/commands/application.js +885 -0
  8. package/dist/commands/auth.js +570 -0
  9. package/dist/commands/base/BaseCommand.js +93 -0
  10. package/dist/commands/base/CommandHandler.js +7 -0
  11. package/dist/commands/base/command-wrapper.js +58 -0
  12. package/dist/commands/base/middleware.js +77 -0
  13. package/dist/commands/config.js +116 -0
  14. package/dist/commands/connectivity.js +59 -0
  15. package/dist/commands/debug.js +98 -0
  16. package/dist/commands/discover.js +144 -0
  17. package/dist/commands/examples/migrated-command-example.js +180 -0
  18. package/dist/commands/gateway.js +494 -0
  19. package/dist/commands/managedGateway.js +787 -0
  20. package/dist/commands/utils/config-validator.js +76 -0
  21. package/dist/commands/utils/gateway-prompt.js +79 -0
  22. package/dist/commands/utils/input-parser.js +120 -0
  23. package/dist/commands/utils/output-formatter.js +109 -0
  24. package/dist/config/app-config.js +99 -0
  25. package/dist/detection/SystemCapabilityDetector.js +1244 -0
  26. package/dist/detection/ToolDetector.js +305 -0
  27. package/dist/detection/WorkloadDetector.js +314 -0
  28. package/dist/di/bindings.js +99 -0
  29. package/dist/di/container.js +88 -0
  30. package/dist/di/types.js +32 -0
  31. package/dist/index.js +52 -0
  32. package/dist/interfaces/IDaemonManager.js +3 -0
  33. package/dist/repositories/config-repository.js +62 -0
  34. package/dist/repositories/gateway-repository.js +35 -0
  35. package/dist/scripts/postinstall.js +101 -0
  36. package/dist/services/AgentStatusManager.js +299 -0
  37. package/dist/services/ConnectivityTester.js +271 -0
  38. package/dist/services/DependencyInstaller.js +475 -0
  39. package/dist/services/LocalAgentManager.js +2216 -0
  40. package/dist/services/application/ApplicationService.js +299 -0
  41. package/dist/services/auth/AuthService.js +214 -0
  42. package/dist/services/aws.js +644 -0
  43. package/dist/services/daemon/DaemonManagerFactory.js +65 -0
  44. package/dist/services/daemon/DockerDaemonManager.js +395 -0
  45. package/dist/services/daemon/LaunchdDaemonManager.js +257 -0
  46. package/dist/services/daemon/PodmanDaemonManager.js +369 -0
  47. package/dist/services/daemon/SystemdDaemonManager.js +221 -0
  48. package/dist/services/daemon/WindowsServiceDaemonManager.js +210 -0
  49. package/dist/services/daemon/index.js +16 -0
  50. package/dist/services/edgible.js +3060 -0
  51. package/dist/services/gateway/GatewayService.js +334 -0
  52. package/dist/state/config.js +146 -0
  53. package/dist/types/AgentConfig.js +5 -0
  54. package/dist/types/AgentStatus.js +5 -0
  55. package/dist/types/ApiClient.js +5 -0
  56. package/dist/types/ApiRequests.js +5 -0
  57. package/dist/types/ApiResponses.js +5 -0
  58. package/dist/types/Application.js +5 -0
  59. package/dist/types/CaddyJson.js +5 -0
  60. package/dist/types/UnifiedAgentStatus.js +56 -0
  61. package/dist/types/WireGuard.js +5 -0
  62. package/dist/types/Workload.js +5 -0
  63. package/dist/types/agent.js +5 -0
  64. package/dist/types/command-options.js +5 -0
  65. package/dist/types/connectivity.js +5 -0
  66. package/dist/types/errors.js +250 -0
  67. package/dist/types/gateway-types.js +5 -0
  68. package/dist/types/index.js +48 -0
  69. package/dist/types/models/ApplicationData.js +5 -0
  70. package/dist/types/models/CertificateData.js +5 -0
  71. package/dist/types/models/DeviceData.js +5 -0
  72. package/dist/types/models/DevicePoolData.js +5 -0
  73. package/dist/types/models/OrganizationData.js +5 -0
  74. package/dist/types/models/OrganizationInviteData.js +5 -0
  75. package/dist/types/models/ProviderConfiguration.js +5 -0
  76. package/dist/types/models/ResourceData.js +5 -0
  77. package/dist/types/models/ServiceResourceData.js +5 -0
  78. package/dist/types/models/UserData.js +5 -0
  79. package/dist/types/route.js +5 -0
  80. package/dist/types/validation/schemas.js +218 -0
  81. package/dist/types/validation.js +5 -0
  82. package/dist/utils/FileIntegrityManager.js +256 -0
  83. package/dist/utils/PathMigration.js +219 -0
  84. package/dist/utils/PathResolver.js +235 -0
  85. package/dist/utils/PlatformDetector.js +277 -0
  86. package/dist/utils/console-logger.js +130 -0
  87. package/dist/utils/docker-compose-parser.js +179 -0
  88. package/dist/utils/errors.js +130 -0
  89. package/dist/utils/health-checker.js +155 -0
  90. package/dist/utils/json-logger.js +72 -0
  91. package/dist/utils/log-formatter.js +293 -0
  92. package/dist/utils/logger.js +59 -0
  93. package/dist/utils/network-utils.js +217 -0
  94. package/dist/utils/output.js +182 -0
  95. package/dist/utils/passwordValidation.js +91 -0
  96. package/dist/utils/progress.js +167 -0
  97. package/dist/utils/sudo-checker.js +22 -0
  98. package/dist/utils/urls.js +32 -0
  99. package/dist/utils/validation.js +31 -0
  100. package/dist/validation/schemas.js +175 -0
  101. package/dist/validation/validator.js +67 -0
  102. package/package.json +83 -0
@@ -0,0 +1,570 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.setupAuthCommands = setupAuthCommands;
40
+ const inquirer_1 = __importDefault(require("inquirer"));
41
+ const chalk_1 = __importDefault(require("chalk"));
42
+ const os = __importStar(require("os"));
43
+ const validation_1 = require("../utils/validation");
44
+ const passwordValidation_1 = require("../utils/passwordValidation");
45
+ const ToolDetector_1 = require("../detection/ToolDetector");
46
+ const command_wrapper_1 = require("./base/command-wrapper");
47
+ const container_1 = require("../di/container");
48
+ const types_1 = require("../di/types");
49
+ function setupAuthCommands(program) {
50
+ const authCommand = program
51
+ .command('auth')
52
+ .description('Manage authentication');
53
+ authCommand
54
+ .command('login')
55
+ .description('Login with user account or create device login')
56
+ .addHelpText('after', `
57
+ Examples:
58
+ $ edgible auth login
59
+ $ edgible auth login --device --device-name mydevice --email user@example.com
60
+ $ edgible auth login --device --device-name mydevice --email user@example.com --password "SecurePass123!"
61
+ `)
62
+ .option('--device', 'Login as a device')
63
+ .option('--device-name <name>', 'Device name (required for device login)')
64
+ .option('--device-type <type>', 'Device type: serving or gateway (default: serving)')
65
+ .option('--email <email>', 'Email address for invite (required for device login)')
66
+ .option('--password <password>', 'Device password (if not provided, will be generated)')
67
+ .action((0, command_wrapper_1.wrapCommand)(async (options) => {
68
+ const container = (0, container_1.getContainer)();
69
+ const logger = container.get(types_1.TYPES.Logger);
70
+ const configRepository = container.get(types_1.TYPES.ConfigRepository);
71
+ const authService = container.get(types_1.TYPES.AuthService);
72
+ const isFirstRun = configRepository.isFirstRun();
73
+ const savedEmail = configRepository.getEmail();
74
+ if (options.device) {
75
+ await handleDeviceLogin(configRepository, authService, savedEmail, isFirstRun, options, logger);
76
+ return;
77
+ }
78
+ // Interactive mode (existing logic)
79
+ if (isFirstRun) {
80
+ console.log(chalk_1.default.blue.bold('\nšŸŽ‰ Welcome to Edgible CLI!'));
81
+ console.log(chalk_1.default.gray('This is your first time using the Edgible CLI.'));
82
+ console.log(chalk_1.default.gray('You can login in two ways:\n'));
83
+ console.log(chalk_1.default.cyan('1. User Login: Use your existing account email and password'));
84
+ console.log(chalk_1.default.cyan('2. Device Login: Create a device without an account (invite email will be sent)\n'));
85
+ // Detect available tools on first run
86
+ console.log(chalk_1.default.gray('Detecting available infrastructure tools...'));
87
+ const detectedTools = await ToolDetector_1.ToolDetector.detectTools();
88
+ ToolDetector_1.ToolDetector.displayDetectedTools(detectedTools);
89
+ // Store tool detection results in config
90
+ configRepository.updateConfig({
91
+ detectedTools: detectedTools.filter((t) => t.available).map((t) => t.name),
92
+ toolDetectionDate: new Date().toISOString(),
93
+ });
94
+ }
95
+ else {
96
+ console.log(chalk_1.default.blue('Welcome back to Edgible CLI!'));
97
+ }
98
+ // Choose login method
99
+ const loginMethod = await inquirer_1.default.prompt([
100
+ {
101
+ type: 'list',
102
+ name: 'method',
103
+ message: 'How would you like to login?',
104
+ choices: [
105
+ { name: 'User Login (I have an account)', value: 'user' },
106
+ { name: 'Device Login (Create device without account)', value: 'device' },
107
+ ],
108
+ },
109
+ ]);
110
+ if (loginMethod.method === 'user') {
111
+ await handleUserLogin(configRepository, authService, savedEmail, isFirstRun, logger);
112
+ }
113
+ else {
114
+ await handleDeviceLogin(configRepository, authService, savedEmail, isFirstRun, {}, logger);
115
+ }
116
+ }, {
117
+ configRepository: (0, container_1.getContainer)().get(types_1.TYPES.ConfigRepository),
118
+ }));
119
+ authCommand
120
+ .command('logout')
121
+ .description('Log out of the Edgible service')
122
+ .action((0, command_wrapper_1.wrapCommand)(async () => {
123
+ const container = (0, container_1.getContainer)();
124
+ const logger = container.get(types_1.TYPES.Logger);
125
+ const configRepository = container.get(types_1.TYPES.ConfigRepository);
126
+ configRepository.clearConfig();
127
+ logger.info('User logged out');
128
+ console.log(chalk_1.default.green('āœ“ You have been logged out.'));
129
+ }, {
130
+ configRepository: (0, container_1.getContainer)().get(types_1.TYPES.ConfigRepository),
131
+ }));
132
+ authCommand
133
+ .command('select-org')
134
+ .description('Select active organization from your available organizations')
135
+ .action((0, command_wrapper_1.wrapCommand)(async () => {
136
+ const container = (0, container_1.getContainer)();
137
+ const logger = container.get(types_1.TYPES.Logger);
138
+ const configRepository = container.get(types_1.TYPES.ConfigRepository);
139
+ const authService = container.get(types_1.TYPES.AuthService);
140
+ const config = configRepository.getConfig();
141
+ // Check if user is logged in
142
+ if (!config.email) {
143
+ console.log(chalk_1.default.red('āœ— You must be logged in to select an organization.'));
144
+ console.log(chalk_1.default.gray('Run "edgible auth login" to login first.'));
145
+ return;
146
+ }
147
+ console.log(chalk_1.default.blue('\nšŸ¢ Select Organization\n'));
148
+ console.log(chalk_1.default.gray('Fetching your organizations...\n'));
149
+ try {
150
+ // Get user organizations
151
+ const userOrgs = await authService.getUserOrganizations(config.email);
152
+ // Filter for standard (non-billing) organizations only
153
+ const standardOrgs = userOrgs.organizations.filter((org) => org.organizationType !== 'billing');
154
+ if (standardOrgs.length === 0) {
155
+ console.log(chalk_1.default.yellow('⚠ No organizations found for your account.'));
156
+ logger.warn('No standard organizations found', { email: config.email });
157
+ return;
158
+ }
159
+ if (standardOrgs.length === 1) {
160
+ console.log(chalk_1.default.yellow('You only have access to one organization:'));
161
+ console.log(chalk_1.default.cyan(` ${standardOrgs[0].name} (${standardOrgs[0].organizationId})`));
162
+ console.log(chalk_1.default.gray('\nThis organization is already active.'));
163
+ return;
164
+ }
165
+ // Show current organization
166
+ if (config.organizationId) {
167
+ const currentOrg = standardOrgs.find(org => org.organizationId === config.organizationId);
168
+ if (currentOrg) {
169
+ console.log(chalk_1.default.gray(`Current organization: ${currentOrg.name}\n`));
170
+ }
171
+ }
172
+ // Create choices for inquirer
173
+ const orgChoices = standardOrgs.map((org) => {
174
+ const isCurrent = org.organizationId === config.organizationId;
175
+ return {
176
+ name: `${org.name}${isCurrent ? chalk_1.default.gray(' (current)') : ''}${org.description ? chalk_1.default.gray(` - ${org.description}`) : ''}`,
177
+ value: org.organizationId,
178
+ short: org.name
179
+ };
180
+ });
181
+ // Prompt for organization selection
182
+ const { selectedOrgId } = await inquirer_1.default.prompt([
183
+ {
184
+ type: 'list',
185
+ name: 'selectedOrgId',
186
+ message: 'Select organization:',
187
+ choices: orgChoices,
188
+ default: config.organizationId
189
+ }
190
+ ]);
191
+ // Check if same as current
192
+ if (selectedOrgId === config.organizationId) {
193
+ console.log(chalk_1.default.gray('\nNo change - this organization is already active.'));
194
+ return;
195
+ }
196
+ // Update config with selected organization
197
+ configRepository.updateConfig({ organizationId: selectedOrgId });
198
+ const selectedOrg = standardOrgs.find(org => org.organizationId === selectedOrgId);
199
+ logger.info('Organization changed', {
200
+ from: config.organizationId,
201
+ to: selectedOrgId,
202
+ organizationName: selectedOrg?.name
203
+ });
204
+ console.log(chalk_1.default.green(`\nāœ“ Active organization changed to: ${selectedOrg?.name}`));
205
+ console.log(chalk_1.default.gray(`Organization ID: ${selectedOrgId}`));
206
+ }
207
+ catch (error) {
208
+ logger.error('Error fetching organizations', error);
209
+ const errorMessage = error instanceof Error ? error.message : 'An unexpected error occurred';
210
+ console.error(chalk_1.default.red('\nāœ— Failed to fetch organizations:'), errorMessage);
211
+ }
212
+ }, {
213
+ configRepository: (0, container_1.getContainer)().get(types_1.TYPES.ConfigRepository),
214
+ }));
215
+ }
216
+ async function handleUserLogin(configRepository, authService, savedEmail, isFirstRun, logger) {
217
+ // Check if we have a saved email
218
+ let email;
219
+ if (savedEmail && !isFirstRun) {
220
+ console.log(chalk_1.default.gray(`Using saved email: ${savedEmail}`));
221
+ const useSaved = await inquirer_1.default.prompt([
222
+ {
223
+ type: 'confirm',
224
+ name: 'useSaved',
225
+ message: 'Would you like to use this email address?',
226
+ default: true
227
+ }
228
+ ]);
229
+ if (useSaved.useSaved) {
230
+ email = savedEmail;
231
+ }
232
+ else {
233
+ const answers = await inquirer_1.default.prompt([
234
+ {
235
+ type: 'input',
236
+ name: 'email',
237
+ message: 'Enter your email address:',
238
+ validate: (input) => {
239
+ if (!input.trim()) {
240
+ return 'Email address is required';
241
+ }
242
+ if (!(0, validation_1.validateEmail)(input)) {
243
+ return 'Please enter a valid email address';
244
+ }
245
+ return true;
246
+ }
247
+ }
248
+ ]);
249
+ email = answers.email.trim().toLowerCase();
250
+ }
251
+ }
252
+ else {
253
+ console.log(chalk_1.default.gray('Please enter your email address to get started.'));
254
+ if (isFirstRun) {
255
+ console.log(chalk_1.default.yellow('šŸ’” Don\'t worry if you don\'t have an account yet - you can create one later!'));
256
+ }
257
+ console.log('');
258
+ const answers = await inquirer_1.default.prompt([
259
+ {
260
+ type: 'input',
261
+ name: 'email',
262
+ message: 'Enter your email address:',
263
+ validate: (input) => {
264
+ if (!input.trim()) {
265
+ return 'Email address is required';
266
+ }
267
+ if (!(0, validation_1.validateEmail)(input)) {
268
+ return 'Please enter a valid email address';
269
+ }
270
+ return true;
271
+ }
272
+ }
273
+ ]);
274
+ email = answers.email.trim().toLowerCase();
275
+ }
276
+ console.log(chalk_1.default.green(`\nāœ“ Email received: ${email}`));
277
+ console.log(chalk_1.default.gray('Checking account status...\n'));
278
+ // Save email to config
279
+ configRepository.setEmail(email);
280
+ // Check if account exists
281
+ logger.debug('Checking account existence', { email });
282
+ const accountExists = await authService.checkAccount(email);
283
+ if (accountExists) {
284
+ // Prompt for password
285
+ const passwordAnswer = await inquirer_1.default.prompt([
286
+ {
287
+ type: 'password',
288
+ name: 'password',
289
+ message: 'Enter your password:',
290
+ validate: (input) => {
291
+ if (!input.trim()) {
292
+ return 'Password is required';
293
+ }
294
+ return true;
295
+ }
296
+ }
297
+ ]);
298
+ try {
299
+ logger.info('Attempting user login', { email });
300
+ const loginResponse = await authService.loginUser(email, passwordAnswer.password);
301
+ console.log(chalk_1.default.green('āœ“ Login successful!'));
302
+ console.log(chalk_1.default.gray(`Welcome back, ${email}`));
303
+ // Get user organizations and store the first one (standard organizations only)
304
+ try {
305
+ const userOrgs = await authService.getUserOrganizations(email);
306
+ // Filter for standard (non-billing) organizations only
307
+ const standardOrgs = userOrgs.organizations.filter((org) => org.organizationType !== 'billing');
308
+ if (standardOrgs.length > 0) {
309
+ configRepository.updateConfig({ organizationId: standardOrgs[0].organizationId });
310
+ logger.info('Organization selected', { organizationId: standardOrgs[0].organizationId });
311
+ console.log(chalk_1.default.gray(`Using organization: ${standardOrgs[0].name}`));
312
+ }
313
+ else {
314
+ logger.warn('No standard organizations found for user', { email });
315
+ console.log(chalk_1.default.yellow('⚠ No standard organizations found for this user'));
316
+ }
317
+ }
318
+ catch (orgError) {
319
+ logger.warn('Could not fetch organizations', orgError);
320
+ console.log(chalk_1.default.yellow('⚠ Could not fetch organizations, but login was successful'));
321
+ }
322
+ configRepository.setAccountStatus(true);
323
+ configRepository.setFirstRunComplete();
324
+ console.log(chalk_1.default.gray('Use "edgible --help" to see available commands.'));
325
+ }
326
+ catch (error) {
327
+ logger.error('Login failed', error);
328
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
329
+ console.log(chalk_1.default.red('āœ— Login failed:'), errorMessage);
330
+ console.log(chalk_1.default.gray('Please check your email and password.'));
331
+ }
332
+ }
333
+ else {
334
+ logger.debug('No account found for email', { email });
335
+ console.log(chalk_1.default.yellow('⚠ No account found for this email.'));
336
+ configRepository.setAccountStatus(false);
337
+ if (isFirstRun) {
338
+ console.log(chalk_1.default.blue('\nšŸ”§ No worries! You can create an account in a few ways:'));
339
+ console.log(chalk_1.default.gray('1. Use the "edgible signup" command'));
340
+ console.log(chalk_1.default.gray('2. Visit https://edgible.com/signup in your browser'));
341
+ console.log(chalk_1.default.gray('3. Use device login to create a device without an account'));
342
+ console.log(chalk_1.default.gray('4. Come back later and run "edgible login" again\n'));
343
+ }
344
+ else {
345
+ console.log(chalk_1.default.gray('You can create an account by visiting the Edgible website or using the signup command.'));
346
+ }
347
+ const createAccount = await inquirer_1.default.prompt([
348
+ {
349
+ type: 'confirm',
350
+ name: 'create',
351
+ message: 'Would you like to create an account now?',
352
+ default: true,
353
+ },
354
+ ]);
355
+ if (createAccount.create) {
356
+ console.log(chalk_1.default.blue('\nRedirecting to account creation...'));
357
+ console.log(chalk_1.default.gray('Please visit https://edgible.com/signup to create your account.'));
358
+ console.log(chalk_1.default.gray(`We\'ll remember your email: ${email}`));
359
+ }
360
+ configRepository.setFirstRunComplete();
361
+ }
362
+ }
363
+ async function handleDeviceLogin(configRepository, authService, savedEmail, isFirstRun, options, logger) {
364
+ if (options.deviceName && options.email) {
365
+ await handleDeviceLoginNonInteractive(configRepository, authService, options.deviceName, (options.deviceType || 'serving'), options.email, options.password, logger);
366
+ return;
367
+ }
368
+ console.log(chalk_1.default.blue('\nšŸ”§ Device Login Setup'));
369
+ console.log(chalk_1.default.gray('This will create a device without requiring a user account.'));
370
+ console.log(chalk_1.default.gray('An invite email will be sent to you automatically.\n'));
371
+ // Get device name
372
+ const deviceNameAnswer = await inquirer_1.default.prompt([
373
+ {
374
+ type: 'input',
375
+ name: 'deviceName',
376
+ message: 'Enter a name for this device:',
377
+ default: `${os.hostname()}-${os.platform()}`,
378
+ validate: (input) => {
379
+ if (!input.trim()) {
380
+ return 'Device name is required';
381
+ }
382
+ return true;
383
+ }
384
+ }
385
+ ]);
386
+ // Get device type
387
+ const deviceTypeAnswer = await inquirer_1.default.prompt([
388
+ {
389
+ type: 'list',
390
+ name: 'deviceType',
391
+ message: 'Select device type:',
392
+ choices: [
393
+ { name: 'Serving Device - Host applications and services', value: 'serving' },
394
+ { name: 'Gateway Device - Route traffic and manage firewall', value: 'gateway' }
395
+ ],
396
+ default: 'serving'
397
+ }
398
+ ]);
399
+ // API URL is now managed through AppConfig
400
+ // Get optional user email for invite
401
+ let userEmail;
402
+ if (savedEmail) {
403
+ const useSavedEmail = await inquirer_1.default.prompt([
404
+ {
405
+ type: 'confirm',
406
+ name: 'useSaved',
407
+ message: `Use saved email (${savedEmail}) for invite?`,
408
+ default: true
409
+ }
410
+ ]);
411
+ if (useSavedEmail.useSaved) {
412
+ userEmail = savedEmail;
413
+ }
414
+ }
415
+ if (!userEmail) {
416
+ const emailAnswer = await inquirer_1.default.prompt([
417
+ {
418
+ type: 'input',
419
+ name: 'email',
420
+ message: 'Enter your email address (for invite):',
421
+ validate: (input) => {
422
+ if (!input.trim()) {
423
+ return 'Email address is required for invite';
424
+ }
425
+ if (!(0, validation_1.validateEmail)(input)) {
426
+ return 'Please enter a valid email address';
427
+ }
428
+ return true;
429
+ }
430
+ }
431
+ ]);
432
+ userEmail = emailAnswer.email.trim().toLowerCase();
433
+ }
434
+ // Generate or get password
435
+ const passwordChoice = await inquirer_1.default.prompt([
436
+ {
437
+ type: 'list',
438
+ name: 'choice',
439
+ message: 'Choose password option:',
440
+ choices: [
441
+ { name: 'Generate secure password automatically', value: 'generate' },
442
+ { name: 'Enter custom password', value: 'custom' }
443
+ ]
444
+ }
445
+ ]);
446
+ let password;
447
+ if (passwordChoice.choice === 'generate') {
448
+ password = (0, passwordValidation_1.generateSecurePassword)();
449
+ console.log(chalk_1.default.green(`\nāœ“ Generated secure password: ${password}`));
450
+ console.log(chalk_1.default.yellow('⚠ Please save this password securely! You will need it to login.'));
451
+ }
452
+ else {
453
+ const passwordAnswer = await inquirer_1.default.prompt([
454
+ {
455
+ type: 'password',
456
+ name: 'password',
457
+ message: 'Enter password (must meet security requirements):',
458
+ validate: (input) => {
459
+ if (!input.trim()) {
460
+ return 'Password is required';
461
+ }
462
+ const validation = (0, passwordValidation_1.validateCognitoPassword)(input);
463
+ if (!validation.isValid) {
464
+ return `Password requirements not met: ${validation.errors.join(', ')}`;
465
+ }
466
+ return true;
467
+ }
468
+ }
469
+ ]);
470
+ password = passwordAnswer.password;
471
+ }
472
+ console.log(chalk_1.default.gray('\nCreating device and organization...'));
473
+ try {
474
+ logger.info('Creating device with organization', { deviceName: deviceNameAnswer.deviceName });
475
+ const response = await authService.createDeviceWithOrganization(deviceNameAnswer.deviceName, password, userEmail);
476
+ console.log(chalk_1.default.green('\nāœ“ Device created successfully!'));
477
+ console.log(chalk_1.default.blue('\nšŸ“‹ Device Details:'));
478
+ console.log(chalk_1.default.white(` Device ID: ${response.device.id}`));
479
+ console.log(chalk_1.default.white(` Device Name: ${response.device.name}`));
480
+ console.log(chalk_1.default.white(` Organization: ${response.organization.name}`));
481
+ console.log(chalk_1.default.white(` Password: ${password}`));
482
+ if (userEmail) {
483
+ console.log(chalk_1.default.white(` Invite Email: ${userEmail}`));
484
+ console.log(chalk_1.default.yellow('\nšŸ“§ An invite email has been sent to your email address.'));
485
+ }
486
+ console.log(chalk_1.default.blue('\nšŸ” Login Credentials:'));
487
+ console.log(chalk_1.default.cyan(` Device ID: ${response.device.id}`));
488
+ console.log(chalk_1.default.cyan(` Password: ${password}`));
489
+ console.log(chalk_1.default.gray('\nUse these credentials to login with "edgible device-login" in the future.'));
490
+ // Save device info to config
491
+ logger.info('Device created successfully', { deviceId: response.device.id });
492
+ configRepository.updateConfig({
493
+ deviceId: response.device.id,
494
+ deviceName: response.device.name,
495
+ devicePassword: password,
496
+ deviceType: deviceTypeAnswer.deviceType,
497
+ organizationId: response.organization.organizationId,
498
+ userEmail: userEmail,
499
+ });
500
+ configRepository.setFirstRunComplete();
501
+ }
502
+ catch (error) {
503
+ logger.error('Device creation failed', error);
504
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
505
+ console.log(chalk_1.default.red('āœ— Device creation failed:'), errorMessage);
506
+ console.log(chalk_1.default.gray('Please try again or contact support.'));
507
+ }
508
+ }
509
+ async function handleDeviceLoginNonInteractive(configRepository, authService, deviceName, deviceType, userEmail, password, logger) {
510
+ console.log(chalk_1.default.gray('Creating device without requiring a user account.'));
511
+ console.log(chalk_1.default.gray('An invite email will be sent automatically.\n'));
512
+ // Generate password if not provided
513
+ let devicePassword;
514
+ if (password) {
515
+ // Validate provided password
516
+ const validation = (0, passwordValidation_1.validateCognitoPassword)(password);
517
+ if (!validation.isValid) {
518
+ logger.error('Password requirements not met', { errors: validation.errors });
519
+ throw new Error(`Password requirements not met: ${validation.errors.join(', ')}`);
520
+ }
521
+ devicePassword = password;
522
+ console.log(chalk_1.default.green(`āœ“ Using provided password`));
523
+ }
524
+ else {
525
+ devicePassword = (0, passwordValidation_1.generateSecurePassword)();
526
+ console.log(chalk_1.default.green(`āœ“ Generated secure password: ${devicePassword}`));
527
+ }
528
+ console.log(chalk_1.default.gray('\nCreating device and organization...'));
529
+ try {
530
+ logger.info('Creating device with organization (non-interactive)', { deviceName });
531
+ const response = await authService.createDeviceWithOrganization(deviceName, devicePassword, userEmail);
532
+ console.log(chalk_1.default.green('\nāœ“ Device created successfully!'));
533
+ console.log(chalk_1.default.blue('\nšŸ“‹ Device Details:'));
534
+ console.log(chalk_1.default.white(` Device ID: ${response.device.id}`));
535
+ console.log(chalk_1.default.white(` Device Name: ${response.device.name}`));
536
+ console.log(chalk_1.default.white(` Organization: ${response.organization.name}`));
537
+ console.log(chalk_1.default.white(` Password: ${devicePassword}`));
538
+ console.log(chalk_1.default.white(` Invite Email: ${userEmail}`));
539
+ console.log(chalk_1.default.yellow('\nšŸ“§ An invite email has been sent to your email address.'));
540
+ console.log(chalk_1.default.blue('\nšŸ” Login Credentials:'));
541
+ console.log(chalk_1.default.cyan(` Device ID: ${response.device.id}`));
542
+ console.log(chalk_1.default.cyan(` Password: ${devicePassword}`));
543
+ // Save device info to config
544
+ logger.info('Device created successfully (non-interactive)', { deviceId: response.device.id });
545
+ configRepository.updateConfig({
546
+ deviceId: response.device.id,
547
+ deviceName: response.device.name,
548
+ devicePassword: devicePassword,
549
+ deviceType: deviceType,
550
+ organizationId: response.organization.organizationId,
551
+ userEmail: userEmail,
552
+ });
553
+ configRepository.setFirstRunComplete();
554
+ // Output credentials in a format that can be easily parsed by scripts
555
+ const container = (0, container_1.getContainer)();
556
+ const appConfig = container.get(types_1.TYPES.AppConfig);
557
+ console.log(chalk_1.default.gray('\n--- CREDENTIALS FOR AUTOMATION ---'));
558
+ console.log(`DEVICE_ID=${response.device.id}`);
559
+ console.log(`DEVICE_PASSWORD=${devicePassword}`);
560
+ console.log(`API_BASE_URL=${appConfig.apiBaseUrl}`);
561
+ console.log('--- END CREDENTIALS ---');
562
+ }
563
+ catch (error) {
564
+ logger.error('Device creation failed (non-interactive)', error);
565
+ const errorMessage = error instanceof Error ? error.message : 'Unknown error';
566
+ console.log(chalk_1.default.red('āœ— Device creation failed:'), errorMessage);
567
+ throw error;
568
+ }
569
+ }
570
+ //# sourceMappingURL=auth.js.map
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ /**
3
+ * Base command class
4
+ * Provides consistent command execution with middleware support
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.BaseCommand = void 0;
8
+ const errors_1 = require("../../utils/errors");
9
+ /**
10
+ * Base command implementation with error handling and middleware
11
+ */
12
+ class BaseCommand {
13
+ constructor(options) {
14
+ this.middlewares = [];
15
+ this.logger = options.logger;
16
+ this.configRepository = options.configRepository;
17
+ this.requireAuth = options.requireAuth ?? false;
18
+ this.requireOrganization = options.requireOrganization ?? false;
19
+ }
20
+ /**
21
+ * Add middleware to the command
22
+ */
23
+ use(middleware) {
24
+ this.middlewares.push(middleware);
25
+ }
26
+ /**
27
+ * Execute the command with middleware chain
28
+ */
29
+ async execute(context) {
30
+ try {
31
+ // Run middleware chain
32
+ await this.runMiddleware(context, 0);
33
+ // Execute the actual command
34
+ await this.doExecute(context);
35
+ }
36
+ catch (error) {
37
+ errors_1.ErrorHandler.handleError(error);
38
+ }
39
+ }
40
+ /**
41
+ * Run middleware chain
42
+ */
43
+ async runMiddleware(context, index) {
44
+ if (index >= this.middlewares.length) {
45
+ return;
46
+ }
47
+ const middleware = this.middlewares[index];
48
+ await middleware.process(context, async () => {
49
+ await this.runMiddleware(context, index + 1);
50
+ });
51
+ }
52
+ /**
53
+ * Override this method in subclasses to implement command logic
54
+ */
55
+ async doExecute(context) {
56
+ throw new Error('doExecute must be implemented by subclass');
57
+ }
58
+ /**
59
+ * Check if user is authenticated
60
+ */
61
+ ensureAuthenticated() {
62
+ if (!this.requireAuth) {
63
+ return;
64
+ }
65
+ const config = this.configRepository.getConfig();
66
+ const hasTokens = !!(config.accessToken || config.idToken);
67
+ const hasDeviceCreds = !!(config.deviceId && config.devicePassword);
68
+ if (!hasTokens && !hasDeviceCreds) {
69
+ throw new Error('Not authenticated. Please run "edgible login" first.');
70
+ }
71
+ }
72
+ /**
73
+ * Check if organization ID is available
74
+ */
75
+ ensureOrganization() {
76
+ if (!this.requireOrganization) {
77
+ return;
78
+ }
79
+ const config = this.configRepository.getConfig();
80
+ if (!config.organizationId) {
81
+ throw new Error('No organization found. Please login first.');
82
+ }
83
+ }
84
+ /**
85
+ * Validate and ensure authentication and organization
86
+ */
87
+ validateContext() {
88
+ this.ensureAuthenticated();
89
+ this.ensureOrganization();
90
+ }
91
+ }
92
+ exports.BaseCommand = BaseCommand;
93
+ //# sourceMappingURL=BaseCommand.js.map
@@ -0,0 +1,7 @@
1
+ "use strict";
2
+ /**
3
+ * Command handler interface
4
+ * Defines the contract for command execution
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ //# sourceMappingURL=CommandHandler.js.map