@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.
- package/LICENSE +136 -0
- package/README.md +450 -0
- package/dist/client/api-client.js +1057 -0
- package/dist/client/index.js +21 -0
- package/dist/commands/agent.js +1280 -0
- package/dist/commands/ai.js +608 -0
- package/dist/commands/application.js +885 -0
- package/dist/commands/auth.js +570 -0
- package/dist/commands/base/BaseCommand.js +93 -0
- package/dist/commands/base/CommandHandler.js +7 -0
- package/dist/commands/base/command-wrapper.js +58 -0
- package/dist/commands/base/middleware.js +77 -0
- package/dist/commands/config.js +116 -0
- package/dist/commands/connectivity.js +59 -0
- package/dist/commands/debug.js +98 -0
- package/dist/commands/discover.js +144 -0
- package/dist/commands/examples/migrated-command-example.js +180 -0
- package/dist/commands/gateway.js +494 -0
- package/dist/commands/managedGateway.js +787 -0
- package/dist/commands/utils/config-validator.js +76 -0
- package/dist/commands/utils/gateway-prompt.js +79 -0
- package/dist/commands/utils/input-parser.js +120 -0
- package/dist/commands/utils/output-formatter.js +109 -0
- package/dist/config/app-config.js +99 -0
- package/dist/detection/SystemCapabilityDetector.js +1244 -0
- package/dist/detection/ToolDetector.js +305 -0
- package/dist/detection/WorkloadDetector.js +314 -0
- package/dist/di/bindings.js +99 -0
- package/dist/di/container.js +88 -0
- package/dist/di/types.js +32 -0
- package/dist/index.js +52 -0
- package/dist/interfaces/IDaemonManager.js +3 -0
- package/dist/repositories/config-repository.js +62 -0
- package/dist/repositories/gateway-repository.js +35 -0
- package/dist/scripts/postinstall.js +101 -0
- package/dist/services/AgentStatusManager.js +299 -0
- package/dist/services/ConnectivityTester.js +271 -0
- package/dist/services/DependencyInstaller.js +475 -0
- package/dist/services/LocalAgentManager.js +2216 -0
- package/dist/services/application/ApplicationService.js +299 -0
- package/dist/services/auth/AuthService.js +214 -0
- package/dist/services/aws.js +644 -0
- package/dist/services/daemon/DaemonManagerFactory.js +65 -0
- package/dist/services/daemon/DockerDaemonManager.js +395 -0
- package/dist/services/daemon/LaunchdDaemonManager.js +257 -0
- package/dist/services/daemon/PodmanDaemonManager.js +369 -0
- package/dist/services/daemon/SystemdDaemonManager.js +221 -0
- package/dist/services/daemon/WindowsServiceDaemonManager.js +210 -0
- package/dist/services/daemon/index.js +16 -0
- package/dist/services/edgible.js +3060 -0
- package/dist/services/gateway/GatewayService.js +334 -0
- package/dist/state/config.js +146 -0
- package/dist/types/AgentConfig.js +5 -0
- package/dist/types/AgentStatus.js +5 -0
- package/dist/types/ApiClient.js +5 -0
- package/dist/types/ApiRequests.js +5 -0
- package/dist/types/ApiResponses.js +5 -0
- package/dist/types/Application.js +5 -0
- package/dist/types/CaddyJson.js +5 -0
- package/dist/types/UnifiedAgentStatus.js +56 -0
- package/dist/types/WireGuard.js +5 -0
- package/dist/types/Workload.js +5 -0
- package/dist/types/agent.js +5 -0
- package/dist/types/command-options.js +5 -0
- package/dist/types/connectivity.js +5 -0
- package/dist/types/errors.js +250 -0
- package/dist/types/gateway-types.js +5 -0
- package/dist/types/index.js +48 -0
- package/dist/types/models/ApplicationData.js +5 -0
- package/dist/types/models/CertificateData.js +5 -0
- package/dist/types/models/DeviceData.js +5 -0
- package/dist/types/models/DevicePoolData.js +5 -0
- package/dist/types/models/OrganizationData.js +5 -0
- package/dist/types/models/OrganizationInviteData.js +5 -0
- package/dist/types/models/ProviderConfiguration.js +5 -0
- package/dist/types/models/ResourceData.js +5 -0
- package/dist/types/models/ServiceResourceData.js +5 -0
- package/dist/types/models/UserData.js +5 -0
- package/dist/types/route.js +5 -0
- package/dist/types/validation/schemas.js +218 -0
- package/dist/types/validation.js +5 -0
- package/dist/utils/FileIntegrityManager.js +256 -0
- package/dist/utils/PathMigration.js +219 -0
- package/dist/utils/PathResolver.js +235 -0
- package/dist/utils/PlatformDetector.js +277 -0
- package/dist/utils/console-logger.js +130 -0
- package/dist/utils/docker-compose-parser.js +179 -0
- package/dist/utils/errors.js +130 -0
- package/dist/utils/health-checker.js +155 -0
- package/dist/utils/json-logger.js +72 -0
- package/dist/utils/log-formatter.js +293 -0
- package/dist/utils/logger.js +59 -0
- package/dist/utils/network-utils.js +217 -0
- package/dist/utils/output.js +182 -0
- package/dist/utils/passwordValidation.js +91 -0
- package/dist/utils/progress.js +167 -0
- package/dist/utils/sudo-checker.js +22 -0
- package/dist/utils/urls.js +32 -0
- package/dist/utils/validation.js +31 -0
- package/dist/validation/schemas.js +175 -0
- package/dist/validation/validator.js +67 -0
- 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
|