genbox 1.0.41 → 1.0.43

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.
@@ -51,6 +51,20 @@ const ssh_config_1 = require("../ssh-config");
51
51
  const schema_v4_1 = require("../schema-v4");
52
52
  const child_process_1 = require("child_process");
53
53
  const random_name_1 = require("../random-name");
54
+ // Credits consumed per hour for each size (matches API billing.config.ts)
55
+ const CREDITS_PER_HOUR = {
56
+ cx22: 1,
57
+ cx32: 2,
58
+ cx42: 4,
59
+ cx52: 8,
60
+ small: 1,
61
+ medium: 2,
62
+ large: 4,
63
+ xl: 8,
64
+ };
65
+ function getCreditsPerHour(size) {
66
+ return CREDITS_PER_HOUR[size.toLowerCase()] || 2; // Default to medium
67
+ }
54
68
  /**
55
69
  * Spawn a background process to poll for IP and add SSH config
56
70
  * This runs detached so the main process can exit immediately
@@ -296,11 +310,41 @@ exports.createCommand = new commander_1.Command('create')
296
310
  if (!selectedProfile && !options.yes && !options.dryRun) {
297
311
  await profileResolver.askSaveProfile(config, resolved);
298
312
  }
313
+ // Calculate credits that will be consumed
314
+ const creditsPerHour = getCreditsPerHour(resolved.size);
315
+ // Fetch user's current credit balance
316
+ let userCredits = 0;
317
+ let addonCredits = 0;
318
+ try {
319
+ const user = await (0, api_1.fetchApi)('/users/me');
320
+ userCredits = user.credits || 0;
321
+ addonCredits = user.addonCredits || 0;
322
+ }
323
+ catch {
324
+ // Continue without balance info if fetch fails
325
+ }
326
+ const totalCredits = userCredits + addonCredits;
327
+ const estimatedHours = totalCredits > 0 ? Math.floor(totalCredits / creditsPerHour) : 0;
328
+ // Display billing info
329
+ console.log(chalk_1.default.blue('=== Billing ==='));
330
+ console.log(` Credits to start: ${chalk_1.default.yellow(creditsPerHour)} (${creditsPerHour} credit${creditsPerHour > 1 ? 's' : ''}/hr for ${resolved.size})`);
331
+ if (totalCredits > 0) {
332
+ console.log(` Your balance: ${chalk_1.default.green(totalCredits)} credits${addonCredits > 0 ? chalk_1.default.dim(` (${userCredits} regular + ${addonCredits} addon)`) : ''}`);
333
+ console.log(` After creation: ${chalk_1.default.cyan(totalCredits - creditsPerHour)} credits`);
334
+ console.log(` Estimated runtime: ${chalk_1.default.cyan('~' + estimatedHours + ' hours')}`);
335
+ }
336
+ console.log('');
299
337
  // Dry run mode
300
338
  if (options.dryRun) {
301
339
  console.log(chalk_1.default.yellow('\nDry run mode - no genbox created'));
302
340
  return;
303
341
  }
342
+ // Check if user has enough credits
343
+ if (totalCredits < creditsPerHour) {
344
+ console.log(chalk_1.default.red(`Insufficient credits. Need ${creditsPerHour}, have ${totalCredits}.`));
345
+ console.log(chalk_1.default.dim('Run `genbox balance` to check your balance or purchase more credits.'));
346
+ return;
347
+ }
304
348
  // Confirm creation
305
349
  if (!options.yes) {
306
350
  const confirm = await prompts.confirm({
@@ -771,6 +815,8 @@ function buildPayload(resolved, config, publicKey, privateKey, configLoader) {
771
815
  framework: a.framework,
772
816
  runner: a.runner,
773
817
  docker: a.docker,
818
+ healthcheck: a.healthcheck,
819
+ dependsOn: a.dependsOn,
774
820
  commands: a.commands,
775
821
  })),
776
822
  infrastructure: resolved.infrastructure.map(i => ({
@@ -583,8 +583,15 @@ exports.initCommand = new commander_1.Command('init')
583
583
  console.log(` ... and ${scan.apps.length - 5} more`);
584
584
  }
585
585
  }
586
- if (scan.compose) {
587
- console.log(` ${chalk_1.default.dim('Docker:')} ${scan.compose.applications.length} services`);
586
+ // Show Docker app count (apps with runner: 'docker')
587
+ const dockerApps = scan.apps.filter(a => a.runner === 'docker');
588
+ if (dockerApps.length > 0) {
589
+ console.log(` ${chalk_1.default.dim('Docker:')} ${dockerApps.length} app(s) with docker runner`);
590
+ }
591
+ // Show infrastructure services from docker-compose (databases, caches, etc.)
592
+ if (scan.compose && scan.compose.databases.length + scan.compose.caches.length + scan.compose.queues.length > 0) {
593
+ const infraCount = scan.compose.databases.length + scan.compose.caches.length + scan.compose.queues.length;
594
+ console.log(` ${chalk_1.default.dim('Infra:')} ${infraCount} infrastructure service(s)`);
588
595
  }
589
596
  if (scan.git) {
590
597
  console.log(` ${chalk_1.default.dim('Git:')} ${scan.git.remote} (${scan.git.type})`);
@@ -1886,24 +1893,28 @@ function convertDetectedToScan(detected) {
1886
1893
  lockfile: r.lockfile,
1887
1894
  }));
1888
1895
  // Convert infrastructure to compose analysis
1896
+ // Note: docker_services is deprecated - apps with runner: 'docker' are now tracked per-app
1889
1897
  let compose = null;
1890
1898
  const hasInfra = detected.infrastructure && detected.infrastructure.length > 0;
1891
- const hasDockerServices = detected.docker_services && detected.docker_services.length > 0;
1892
- if (hasInfra || hasDockerServices) {
1899
+ // Get docker apps from apps with runner: 'docker' (new approach)
1900
+ const dockerApps = Object.entries(detected.apps || {})
1901
+ .filter(([, app]) => app.runner === 'docker')
1902
+ .map(([name, app]) => ({
1903
+ name,
1904
+ image: app.docker?.service || name,
1905
+ build: app.docker?.build_context ? {
1906
+ context: app.docker.build_context,
1907
+ dockerfile: app.docker.dockerfile,
1908
+ } : undefined,
1909
+ ports: app.port ? [{ host: app.port, container: app.port }] : [],
1910
+ environment: {},
1911
+ dependsOn: app.depends_on || [],
1912
+ volumes: [],
1913
+ }));
1914
+ if (hasInfra || dockerApps.length > 0) {
1893
1915
  compose = {
1894
1916
  files: ['docker-compose.yml'],
1895
- applications: (detected.docker_services || []).map(svc => ({
1896
- name: svc.name,
1897
- image: svc.image,
1898
- build: svc.build_context ? {
1899
- context: svc.build_context,
1900
- dockerfile: svc.dockerfile,
1901
- } : undefined,
1902
- ports: svc.port ? [{ host: svc.port, container: svc.port }] : [],
1903
- environment: {},
1904
- dependsOn: svc.depends_on || [],
1905
- volumes: [],
1906
- })),
1917
+ applications: dockerApps,
1907
1918
  databases: (detected.infrastructure || [])
1908
1919
  .filter(i => i.type === 'database')
1909
1920
  .map(i => ({
@@ -514,11 +514,17 @@ async function editAppConfig(name, app, rootDir) {
514
514
  build_context: dockerContext,
515
515
  dockerfile: dockerfile || app.docker?.dockerfile,
516
516
  };
517
- // Auto-set port from docker-compose
517
+ // Auto-set values from docker-compose
518
518
  if (composeInfo?.port) {
519
519
  result.port = composeInfo.port;
520
520
  result.port_source = 'docker-compose.yml';
521
521
  }
522
+ if (composeInfo?.healthcheck) {
523
+ result.healthcheck = composeInfo.healthcheck;
524
+ }
525
+ if (composeInfo?.dependsOn?.length) {
526
+ result.depends_on = composeInfo.dependsOn;
527
+ }
522
528
  }
523
529
  // Edit port (only if not a library and not already set by docker-compose)
524
530
  if (newRunner !== 'none' && result.type !== 'library') {
@@ -242,6 +242,8 @@ class ProfileResolver {
242
242
  const v4Config = appConfig;
243
243
  const runner = v4Config.runner;
244
244
  const docker = v4Config.docker;
245
+ const healthcheck = v4Config.healthcheck;
246
+ const dependsOn = v4Config.depends_on;
245
247
  return {
246
248
  name: appName,
247
249
  path: appConfig.path,
@@ -253,6 +255,8 @@ class ProfileResolver {
253
255
  env: appConfig.env,
254
256
  runner,
255
257
  docker,
258
+ healthcheck,
259
+ dependsOn,
256
260
  dependencies,
257
261
  };
258
262
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.41",
3
+ "version": "1.0.43",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {