genbox 1.0.10 → 1.0.11
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/dist/commands/create.js +141 -17
- package/dist/commands/init.js +19 -1
- package/dist/commands/status.js +11 -0
- package/dist/profile-resolver.js +19 -4
- package/package.json +1 -1
package/dist/commands/create.js
CHANGED
|
@@ -48,6 +48,65 @@ const config_loader_1 = require("../config-loader");
|
|
|
48
48
|
const profile_resolver_1 = require("../profile-resolver");
|
|
49
49
|
const api_1 = require("../api");
|
|
50
50
|
const ssh_config_1 = require("../ssh-config");
|
|
51
|
+
const child_process_1 = require("child_process");
|
|
52
|
+
/**
|
|
53
|
+
* Poll for genbox IP address (servers take a few seconds to get an IP assigned)
|
|
54
|
+
*/
|
|
55
|
+
async function waitForIpAddress(genboxId, maxAttempts = 30, delayMs = 2000) {
|
|
56
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
57
|
+
try {
|
|
58
|
+
const genbox = await (0, api_1.fetchApi)(`/genboxes/${genboxId}`);
|
|
59
|
+
if (genbox.ipAddress) {
|
|
60
|
+
return genbox.ipAddress;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
// Ignore errors during polling
|
|
65
|
+
}
|
|
66
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
67
|
+
}
|
|
68
|
+
return null;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Find SSH private key
|
|
72
|
+
*/
|
|
73
|
+
function findSshKeyPath() {
|
|
74
|
+
const home = os.homedir();
|
|
75
|
+
const keyPaths = [
|
|
76
|
+
path.join(home, '.ssh', 'id_ed25519'),
|
|
77
|
+
path.join(home, '.ssh', 'id_rsa'),
|
|
78
|
+
];
|
|
79
|
+
for (const keyPath of keyPaths) {
|
|
80
|
+
if (fs.existsSync(keyPath)) {
|
|
81
|
+
return keyPath;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
return null;
|
|
85
|
+
}
|
|
86
|
+
/**
|
|
87
|
+
* Wait for SSH to be available on the server
|
|
88
|
+
*/
|
|
89
|
+
async function waitForSsh(ipAddress, maxAttempts = 30, delayMs = 5000) {
|
|
90
|
+
const keyPath = findSshKeyPath();
|
|
91
|
+
if (!keyPath)
|
|
92
|
+
return false;
|
|
93
|
+
const sshOpts = `-i ${keyPath} -o IdentitiesOnly=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o LogLevel=ERROR -o ConnectTimeout=5`;
|
|
94
|
+
for (let i = 0; i < maxAttempts; i++) {
|
|
95
|
+
try {
|
|
96
|
+
(0, child_process_1.execSync)(`ssh ${sshOpts} dev@${ipAddress} "echo 'SSH ready'"`, {
|
|
97
|
+
encoding: 'utf8',
|
|
98
|
+
timeout: 10000,
|
|
99
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
100
|
+
});
|
|
101
|
+
return true;
|
|
102
|
+
}
|
|
103
|
+
catch {
|
|
104
|
+
// SSH not ready yet
|
|
105
|
+
}
|
|
106
|
+
await new Promise(resolve => setTimeout(resolve, delayMs));
|
|
107
|
+
}
|
|
108
|
+
return false;
|
|
109
|
+
}
|
|
51
110
|
async function provisionGenbox(payload) {
|
|
52
111
|
return (0, api_1.fetchApi)('/genboxes', {
|
|
53
112
|
method: 'POST',
|
|
@@ -171,17 +230,39 @@ exports.createCommand = new commander_1.Command('create')
|
|
|
171
230
|
const spinner = (0, ora_1.default)(`Creating Genbox '${name}'...`).start();
|
|
172
231
|
try {
|
|
173
232
|
const genbox = await provisionGenbox(payload);
|
|
174
|
-
spinner.succeed(chalk_1.default.green(`Genbox '${name}' created
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
233
|
+
spinner.succeed(chalk_1.default.green(`Genbox '${name}' created!`));
|
|
234
|
+
// Wait for IP if not immediately available
|
|
235
|
+
let ipAddress = genbox.ipAddress;
|
|
236
|
+
if (!ipAddress && genbox._id) {
|
|
237
|
+
spinner.start('Waiting for IP address...');
|
|
238
|
+
ipAddress = await waitForIpAddress(genbox._id);
|
|
239
|
+
if (ipAddress) {
|
|
240
|
+
spinner.succeed(`IP address assigned: ${ipAddress}`);
|
|
241
|
+
genbox.ipAddress = ipAddress;
|
|
242
|
+
}
|
|
243
|
+
else {
|
|
244
|
+
spinner.fail('Timed out waiting for IP. Run `genbox status` to check later.');
|
|
245
|
+
displayGenboxInfo(genbox, resolved);
|
|
246
|
+
return;
|
|
183
247
|
}
|
|
184
248
|
}
|
|
249
|
+
// Wait for SSH to be available
|
|
250
|
+
spinner.start('Waiting for SSH to be ready...');
|
|
251
|
+
const sshReady = await waitForSsh(ipAddress);
|
|
252
|
+
if (sshReady) {
|
|
253
|
+
spinner.succeed(chalk_1.default.green('SSH is ready!'));
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
spinner.warn('SSH not ready yet. Server may still be booting.');
|
|
257
|
+
}
|
|
258
|
+
// Add SSH config
|
|
259
|
+
const sshAdded = (0, ssh_config_1.addSshConfigEntry)({
|
|
260
|
+
name,
|
|
261
|
+
ipAddress,
|
|
262
|
+
});
|
|
263
|
+
if (sshAdded) {
|
|
264
|
+
console.log(chalk_1.default.dim(` SSH config added: ssh ${(0, ssh_config_1.getSshAlias)(name)}`));
|
|
265
|
+
}
|
|
185
266
|
// Display results
|
|
186
267
|
displayGenboxInfo(genbox, resolved);
|
|
187
268
|
}
|
|
@@ -250,6 +331,13 @@ function displayResolvedConfig(resolved) {
|
|
|
250
331
|
}
|
|
251
332
|
console.log('');
|
|
252
333
|
console.log(` ${chalk_1.default.bold('Database:')} ${resolved.database.mode}${resolved.database.source ? ` (from ${resolved.database.source})` : ''}`);
|
|
334
|
+
if (Object.keys(resolved.env).length > 0) {
|
|
335
|
+
console.log('');
|
|
336
|
+
console.log(` ${chalk_1.default.bold('Environment:')}`);
|
|
337
|
+
for (const [key, value] of Object.entries(resolved.env)) {
|
|
338
|
+
console.log(chalk_1.default.dim(` ${key}=${value}`));
|
|
339
|
+
}
|
|
340
|
+
}
|
|
253
341
|
if (resolved.warnings.length > 0) {
|
|
254
342
|
console.log('');
|
|
255
343
|
console.log(chalk_1.default.yellow(' Warnings:'));
|
|
@@ -264,7 +352,7 @@ function displayResolvedConfig(resolved) {
|
|
|
264
352
|
* Build API payload from resolved config
|
|
265
353
|
*/
|
|
266
354
|
function buildPayload(resolved, config, publicKey, privateKey, configLoader) {
|
|
267
|
-
// Load env vars
|
|
355
|
+
// Load env vars from .env.genbox
|
|
268
356
|
const envVars = configLoader.loadEnvVars(process.cwd());
|
|
269
357
|
// Build services map
|
|
270
358
|
const services = {};
|
|
@@ -280,6 +368,22 @@ function buildPayload(resolved, config, publicKey, privateKey, configLoader) {
|
|
|
280
368
|
}
|
|
281
369
|
// Build files bundle
|
|
282
370
|
const files = [];
|
|
371
|
+
// Send .env.genbox content to server for each app
|
|
372
|
+
const envGenboxPath = path.join(process.cwd(), '.env.genbox');
|
|
373
|
+
if (fs.existsSync(envGenboxPath)) {
|
|
374
|
+
const envContent = fs.readFileSync(envGenboxPath, 'utf-8');
|
|
375
|
+
// Add env file for each app (user should have already updated URLs manually)
|
|
376
|
+
for (const app of resolved.apps) {
|
|
377
|
+
const appPath = config.apps[app.name]?.path || app.name;
|
|
378
|
+
const repoPath = resolved.repos.find(r => r.name === app.name)?.path ||
|
|
379
|
+
(resolved.repos[0]?.path ? `${resolved.repos[0].path}/${appPath}` : `/home/dev/${config.project.name}/${appPath}`);
|
|
380
|
+
files.push({
|
|
381
|
+
path: `${repoPath}/.env`,
|
|
382
|
+
content: envContent,
|
|
383
|
+
permissions: '0644',
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
283
387
|
// Add setup script if generated
|
|
284
388
|
const setupScript = generateSetupScript(resolved, config);
|
|
285
389
|
if (setupScript) {
|
|
@@ -465,18 +569,38 @@ async function createLegacy(name, options) {
|
|
|
465
569
|
gitToken: envVars.GIT_TOKEN,
|
|
466
570
|
});
|
|
467
571
|
spinner.succeed(chalk_1.default.green(`Genbox '${name}' created!`));
|
|
468
|
-
if
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
572
|
+
// Wait for IP if not immediately available
|
|
573
|
+
let ipAddress = genbox.ipAddress;
|
|
574
|
+
if (!ipAddress && genbox._id) {
|
|
575
|
+
spinner.start('Waiting for IP address...');
|
|
576
|
+
ipAddress = await waitForIpAddress(genbox._id);
|
|
577
|
+
if (ipAddress) {
|
|
578
|
+
spinner.succeed(`IP address assigned: ${ipAddress}`);
|
|
579
|
+
genbox.ipAddress = ipAddress;
|
|
472
580
|
}
|
|
581
|
+
else {
|
|
582
|
+
spinner.fail('Timed out waiting for IP. Run `genbox status` to check later.');
|
|
583
|
+
return;
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
// Wait for SSH to be available
|
|
587
|
+
spinner.start('Waiting for SSH to be ready...');
|
|
588
|
+
const sshReady = await waitForSsh(ipAddress);
|
|
589
|
+
if (sshReady) {
|
|
590
|
+
spinner.succeed(chalk_1.default.green('SSH is ready!'));
|
|
591
|
+
}
|
|
592
|
+
else {
|
|
593
|
+
spinner.warn('SSH not ready yet. Server may still be booting.');
|
|
594
|
+
}
|
|
595
|
+
// Add SSH config
|
|
596
|
+
const sshAdded = (0, ssh_config_1.addSshConfigEntry)({ name, ipAddress });
|
|
597
|
+
if (sshAdded) {
|
|
598
|
+
console.log(chalk_1.default.dim(` SSH config added: ssh ${(0, ssh_config_1.getSshAlias)(name)}`));
|
|
473
599
|
}
|
|
474
600
|
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
475
601
|
console.log(` ${chalk_1.default.bold('Environment:')} ${name}`);
|
|
476
602
|
console.log(` ${chalk_1.default.bold('Status:')} ${chalk_1.default.yellow(genbox.status)}`);
|
|
477
|
-
|
|
478
|
-
console.log(` ${chalk_1.default.bold('IP:')} ${genbox.ipAddress}`);
|
|
479
|
-
}
|
|
603
|
+
console.log(` ${chalk_1.default.bold('IP:')} ${ipAddress}`);
|
|
480
604
|
console.log(chalk_1.default.dim('───────────────────────────────────────────────'));
|
|
481
605
|
}
|
|
482
606
|
catch (error) {
|
package/dist/commands/init.js
CHANGED
|
@@ -464,11 +464,29 @@ exports.initCommand = new commander_1.Command('init')
|
|
|
464
464
|
console.log(chalk_1.default.dim(` - ${warning}`));
|
|
465
465
|
}
|
|
466
466
|
}
|
|
467
|
+
// Show API URL guidance if environments are configured
|
|
468
|
+
if (v3Config.environments && Object.keys(v3Config.environments).length > 0) {
|
|
469
|
+
console.log('');
|
|
470
|
+
console.log(chalk_1.default.yellow('Important: Update API URLs in .env.genbox for remote environments'));
|
|
471
|
+
console.log('');
|
|
472
|
+
for (const [envName, envConfig] of Object.entries(v3Config.environments)) {
|
|
473
|
+
const apiUrl = envConfig.api?.api ||
|
|
474
|
+
envConfig.api?.url ||
|
|
475
|
+
envConfig.api?.gateway;
|
|
476
|
+
if (apiUrl) {
|
|
477
|
+
console.log(chalk_1.default.dim(` For ${envName} profiles (connect_to: ${envName}):`));
|
|
478
|
+
console.log(chalk_1.default.cyan(` VITE_API_BASE_URL="${apiUrl}"`));
|
|
479
|
+
console.log(chalk_1.default.cyan(` VITE_AUTH_SERVICE_URL="${apiUrl}"`));
|
|
480
|
+
console.log(chalk_1.default.cyan(` NEXT_PUBLIC_API_BASE_URL="${apiUrl}"`));
|
|
481
|
+
console.log('');
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
467
485
|
// Next steps
|
|
468
486
|
console.log('');
|
|
469
487
|
console.log(chalk_1.default.bold('Next steps:'));
|
|
470
488
|
console.log(chalk_1.default.dim(` 1. Review and edit ${CONFIG_FILENAME}`));
|
|
471
|
-
console.log(chalk_1.default.dim(` 2.
|
|
489
|
+
console.log(chalk_1.default.dim(` 2. Update API URLs in ${ENV_FILENAME} for staging/production`));
|
|
472
490
|
console.log(chalk_1.default.dim(` 3. Run 'genbox profiles' to see available profiles`));
|
|
473
491
|
console.log(chalk_1.default.dim(` 4. Run 'genbox create <name> --profile <profile>' to create an environment`));
|
|
474
492
|
}
|
package/dist/commands/status.js
CHANGED
|
@@ -42,6 +42,7 @@ const chalk_1 = __importDefault(require("chalk"));
|
|
|
42
42
|
const api_1 = require("../api");
|
|
43
43
|
const config_1 = require("../config");
|
|
44
44
|
const genbox_selector_1 = require("../genbox-selector");
|
|
45
|
+
const ssh_config_1 = require("../ssh-config");
|
|
45
46
|
const os = __importStar(require("os"));
|
|
46
47
|
const path = __importStar(require("path"));
|
|
47
48
|
const fs = __importStar(require("fs"));
|
|
@@ -101,6 +102,16 @@ exports.statusCommand = new commander_1.Command('status')
|
|
|
101
102
|
return;
|
|
102
103
|
}
|
|
103
104
|
const selectedName = target.name;
|
|
105
|
+
// Auto-add SSH config if missing
|
|
106
|
+
if (!(0, ssh_config_1.hasSshConfigEntry)(selectedName)) {
|
|
107
|
+
const sshAdded = (0, ssh_config_1.addSshConfigEntry)({
|
|
108
|
+
name: selectedName,
|
|
109
|
+
ipAddress: target.ipAddress,
|
|
110
|
+
});
|
|
111
|
+
if (sshAdded) {
|
|
112
|
+
console.log(chalk_1.default.dim(` SSH config added: ssh ${(0, ssh_config_1.getSshAlias)(selectedName)}`));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
104
115
|
// 2. Get SSH key
|
|
105
116
|
let keyPath;
|
|
106
117
|
try {
|
package/dist/profile-resolver.js
CHANGED
|
@@ -113,7 +113,7 @@ class ProfileResolver {
|
|
|
113
113
|
infrastructure,
|
|
114
114
|
database,
|
|
115
115
|
repos: this.resolveRepos(config, apps),
|
|
116
|
-
env: this.resolveEnvVars(config, apps, infrastructure, database),
|
|
116
|
+
env: this.resolveEnvVars(config, apps, infrastructure, database, profile.connect_to),
|
|
117
117
|
hooks: config.hooks || {},
|
|
118
118
|
profile: options.profile,
|
|
119
119
|
warnings,
|
|
@@ -293,7 +293,10 @@ class ProfileResolver {
|
|
|
293
293
|
getUrlForDependency(depName, envConfig) {
|
|
294
294
|
// Check if it's an API dependency
|
|
295
295
|
if (depName === 'api' && envConfig.api) {
|
|
296
|
-
|
|
296
|
+
// Check common fields: url, gateway, api, or first string value
|
|
297
|
+
const apiConfig = envConfig.api;
|
|
298
|
+
return apiConfig.url || apiConfig.gateway || apiConfig.api ||
|
|
299
|
+
Object.values(apiConfig).find(v => typeof v === 'string' && v.startsWith('http'));
|
|
297
300
|
}
|
|
298
301
|
// Check infrastructure
|
|
299
302
|
const infraConfig = envConfig[depName];
|
|
@@ -454,9 +457,21 @@ class ProfileResolver {
|
|
|
454
457
|
/**
|
|
455
458
|
* Resolve environment variables
|
|
456
459
|
*/
|
|
457
|
-
resolveEnvVars(config, apps, infrastructure, database) {
|
|
460
|
+
resolveEnvVars(config, apps, infrastructure, database, connectTo) {
|
|
458
461
|
const env = {};
|
|
459
|
-
//
|
|
462
|
+
// If connect_to is set, get API URL from environment config
|
|
463
|
+
if (connectTo && config.environments?.[connectTo]) {
|
|
464
|
+
const envConfig = config.environments[connectTo];
|
|
465
|
+
const apiUrl = this.getUrlForDependency('api', envConfig);
|
|
466
|
+
if (apiUrl) {
|
|
467
|
+
env['API_URL'] = apiUrl;
|
|
468
|
+
env['VITE_API_URL'] = apiUrl;
|
|
469
|
+
env['VITE_API_BASE_URL'] = apiUrl;
|
|
470
|
+
env['NEXT_PUBLIC_API_URL'] = apiUrl;
|
|
471
|
+
env['NEXT_PUBLIC_API_BASE_URL'] = apiUrl;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
// Add API URL based on app dependency resolution (may override connect_to)
|
|
460
475
|
for (const app of apps) {
|
|
461
476
|
const apiDep = app.dependencies['api'];
|
|
462
477
|
if (apiDep) {
|