genbox 1.0.23 → 1.0.25

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/api.js CHANGED
@@ -7,6 +7,7 @@ exports.AuthenticationError = void 0;
7
7
  exports.handleApiError = handleApiError;
8
8
  exports.isAuthError = isAuthError;
9
9
  exports.fetchApi = fetchApi;
10
+ exports.checkNameAvailability = checkNameAvailability;
10
11
  const chalk_1 = __importDefault(require("chalk"));
11
12
  const config_store_1 = require("./config-store");
12
13
  const API_URL = process.env.GENBOX_API_URL || 'https://api.genbox.dev';
@@ -74,3 +75,9 @@ async function fetchApi(endpoint, options = {}) {
74
75
  }
75
76
  return response.json();
76
77
  }
78
+ /**
79
+ * Check if a genbox name is available in a workspace
80
+ */
81
+ async function checkNameAvailability(name, workspace) {
82
+ return fetchApi(`/genboxes/check-name?name=${encodeURIComponent(name)}&workspace=${encodeURIComponent(workspace)}`);
83
+ }
@@ -50,6 +50,7 @@ const api_1 = require("../api");
50
50
  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
+ const random_name_1 = require("../random-name");
53
54
  /**
54
55
  * Spawn a background process to poll for IP and add SSH config
55
56
  * This runs detached so the main process can exit immediately
@@ -99,9 +100,85 @@ function getPrivateSshKey() {
99
100
  }
100
101
  return undefined;
101
102
  }
103
+ /**
104
+ * Prompt user for environment name
105
+ */
106
+ async function promptForName(skipPrompts) {
107
+ if (skipPrompts) {
108
+ return (0, random_name_1.generateRandomName)();
109
+ }
110
+ const suggestions = (0, random_name_1.generateNameSuggestions)(3);
111
+ const nameChoice = await prompts.select({
112
+ message: 'Environment name:',
113
+ choices: [
114
+ {
115
+ name: `${suggestions[0]} (random)`,
116
+ value: suggestions[0],
117
+ },
118
+ {
119
+ name: `${suggestions[1]} (random)`,
120
+ value: suggestions[1],
121
+ },
122
+ {
123
+ name: `${suggestions[2]} (random)`,
124
+ value: suggestions[2],
125
+ },
126
+ {
127
+ name: 'Enter custom name...',
128
+ value: '__custom__',
129
+ },
130
+ ],
131
+ });
132
+ if (nameChoice === '__custom__') {
133
+ const customName = await prompts.input({
134
+ message: 'Enter environment name:',
135
+ validate: (value) => {
136
+ if (!value.trim())
137
+ return 'Name is required';
138
+ if (!/^[a-z0-9][a-z0-9-]*[a-z0-9]$|^[a-z0-9]$/.test(value.trim())) {
139
+ return 'Name must be lowercase, start/end with letter or number, and contain only letters, numbers, and hyphens';
140
+ }
141
+ return true;
142
+ },
143
+ });
144
+ return customName.trim();
145
+ }
146
+ return nameChoice;
147
+ }
148
+ /**
149
+ * Prompt user to select a profile
150
+ */
151
+ async function promptForProfile(profiles) {
152
+ const profileNames = Object.keys(profiles);
153
+ // Build choices with profile descriptions
154
+ const choices = profileNames.map(name => {
155
+ const profile = profiles[name];
156
+ const apps = profile.apps ? profile.apps.join(', ') : 'default apps';
157
+ const description = profile.description || `Apps: ${apps}`;
158
+ return {
159
+ name: `${name} - ${chalk_1.default.dim(description)}`,
160
+ value: name,
161
+ };
162
+ });
163
+ // Add custom option
164
+ choices.push({
165
+ name: `${chalk_1.default.cyan('Custom')} - ${chalk_1.default.dim('Configure manually')}`,
166
+ value: '__custom__',
167
+ });
168
+ console.log(chalk_1.default.blue('=== Profile Selection ==='));
169
+ console.log('');
170
+ const selected = await prompts.select({
171
+ message: 'Select a profile:',
172
+ choices,
173
+ });
174
+ if (selected === '__custom__') {
175
+ return undefined; // No profile, will use default behavior
176
+ }
177
+ return selected;
178
+ }
102
179
  exports.createCommand = new commander_1.Command('create')
103
180
  .description('Create a new Genbox environment')
104
- .argument('<name>', 'Name of the Genbox')
181
+ .argument('[name]', 'Name of the Genbox (optional - will prompt if not provided)')
105
182
  .option('-p, --profile <profile>', 'Use a predefined profile')
106
183
  .option('-a, --apps <apps>', 'Comma-separated list of apps to include')
107
184
  .option('--add-apps <apps>', 'Add apps to the profile')
@@ -114,7 +191,7 @@ exports.createCommand = new commander_1.Command('create')
114
191
  .option('-f, --from-branch <branch>', 'Source branch to create new branch from (defaults to current/default branch)')
115
192
  .option('-y, --yes', 'Skip interactive prompts')
116
193
  .option('--dry-run', 'Show what would be created without actually creating')
117
- .action(async (name, options) => {
194
+ .action(async (nameArg, options) => {
118
195
  try {
119
196
  // Load configuration
120
197
  const configLoader = new config_loader_1.ConfigLoader();
@@ -122,12 +199,53 @@ exports.createCommand = new commander_1.Command('create')
122
199
  const configVersion = (0, schema_v4_1.getConfigVersion)(loadResult.config);
123
200
  if (!loadResult.config || configVersion === 'unknown') {
124
201
  // Fall back to legacy v1/v2 handling
202
+ const name = nameArg || await promptForName(options.yes);
125
203
  await createLegacy(name, options);
126
204
  return;
127
205
  }
128
206
  // Support both v3 and v4 configs
129
207
  const config = loadResult.config;
130
208
  const profileResolver = new profile_resolver_1.ProfileResolver(configLoader);
209
+ // Interactive name prompt if not provided
210
+ let name = nameArg;
211
+ if (!name && !options.yes) {
212
+ name = await promptForName(false);
213
+ }
214
+ else if (!name && options.yes) {
215
+ // Auto-generate name in non-interactive mode
216
+ name = (0, random_name_1.generateRandomName)();
217
+ console.log(chalk_1.default.dim(` Auto-generated name: ${name}`));
218
+ }
219
+ // Interactive profile selection if profiles exist and no -p option provided
220
+ let selectedProfile = options.profile;
221
+ if (!selectedProfile && !options.yes && config.profiles && Object.keys(config.profiles).length > 0) {
222
+ selectedProfile = await promptForProfile(config.profiles);
223
+ }
224
+ // Check if name is available in workspace, add suffix if taken
225
+ const workspace = config.project?.name || 'default';
226
+ try {
227
+ let { available } = await (0, api_1.checkNameAvailability)(name, workspace);
228
+ if (!available) {
229
+ // Add random suffix and retry
230
+ const originalName = name;
231
+ const suffix = Math.random().toString(36).substring(2, 6);
232
+ name = `${originalName}-${suffix}`;
233
+ // Verify the new name is available
234
+ const recheck = await (0, api_1.checkNameAvailability)(name, workspace);
235
+ if (!recheck.available) {
236
+ // Extremely unlikely, but fall back to timestamp
237
+ name = `${originalName}-${Date.now().toString(36)}`;
238
+ }
239
+ console.log(chalk_1.default.yellow(` Name '${originalName}' is already in use, using '${name}' instead`));
240
+ }
241
+ }
242
+ catch (error) {
243
+ // If check fails (e.g., not logged in), continue - API will catch it on create
244
+ if (error instanceof api_1.AuthenticationError) {
245
+ throw error;
246
+ }
247
+ // Silently continue for other errors - the create API will validate
248
+ }
131
249
  // Validate branch options
132
250
  if (options.newBranch && !options.fromBranch) {
133
251
  console.error(chalk_1.default.red('Error: --new-branch (-n) requires --from-branch (-f) to specify the source branch'));
@@ -145,7 +263,7 @@ exports.createCommand = new commander_1.Command('create')
145
263
  // Build create options
146
264
  const createOptions = {
147
265
  name,
148
- profile: options.profile,
266
+ profile: selectedProfile,
149
267
  apps: options.apps ? options.apps.split(',').map((a) => a.trim()) : undefined,
150
268
  addApps: options.addApps ? options.addApps.split(',').map((a) => a.trim()) : undefined,
151
269
  api: options.api,
@@ -170,7 +288,7 @@ exports.createCommand = new commander_1.Command('create')
170
288
  // Display resolved configuration
171
289
  displayResolvedConfig(resolved);
172
290
  // Ask to save as profile (if not using one already)
173
- if (!options.profile && !options.yes && !options.dryRun) {
291
+ if (!selectedProfile && !options.yes && !options.dryRun) {
174
292
  await profileResolver.askSaveProfile(config, resolved);
175
293
  }
176
294
  // Dry run mode
@@ -302,8 +420,12 @@ function displayResolvedConfig(resolved) {
302
420
  console.log(` • ${infra.name} (${infra.mode})`);
303
421
  }
304
422
  }
305
- console.log('');
306
- console.log(` ${chalk_1.default.bold('Database:')} ${resolved.database.mode}${resolved.database.source ? ` (from ${resolved.database.source})` : ''}`);
423
+ // Only show database info if there are backend apps
424
+ const hasBackendApps = resolved.apps.some(a => a.type === 'backend' || a.name === 'api');
425
+ if (hasBackendApps && resolved.database.mode !== 'none') {
426
+ console.log('');
427
+ console.log(` ${chalk_1.default.bold('Database:')} ${resolved.database.mode}${resolved.database.source ? ` (from ${resolved.database.source})` : ''}`);
428
+ }
307
429
  if (Object.keys(resolved.env).length > 0) {
308
430
  console.log('');
309
431
  console.log(` ${chalk_1.default.bold('Environment:')}`);
@@ -1174,9 +1174,6 @@ async function setupEnvironments(scan, config, isMultiRepo = false, existingEnvV
1174
1174
  }
1175
1175
  }
1176
1176
  if (Object.keys(urls).length > 0) {
1177
- // Add database URLs
1178
- urls['mongodb'] = '${STAGING_MONGODB_URL}';
1179
- urls['redis'] = '${STAGING_REDIS_URL}';
1180
1177
  environments.staging = {
1181
1178
  description: 'Staging environment',
1182
1179
  urls,
@@ -1207,8 +1204,6 @@ async function setupEnvironments(scan, config, isMultiRepo = false, existingEnvV
1207
1204
  description: 'Staging environment',
1208
1205
  urls: {
1209
1206
  api: stagingApiUrl,
1210
- mongodb: '${STAGING_MONGODB_URL}',
1211
- redis: '${STAGING_REDIS_URL}',
1212
1207
  },
1213
1208
  };
1214
1209
  }
@@ -1238,8 +1233,6 @@ async function setupEnvironments(scan, config, isMultiRepo = false, existingEnvV
1238
1233
  description: 'Staging environment',
1239
1234
  urls: {
1240
1235
  api: stagingApiUrl,
1241
- mongodb: '${STAGING_MONGODB_URL}',
1242
- redis: '${STAGING_REDIS_URL}',
1243
1236
  },
1244
1237
  };
1245
1238
  }
@@ -1279,7 +1272,6 @@ async function setupEnvironments(scan, config, isMultiRepo = false, existingEnvV
1279
1272
  }
1280
1273
  }
1281
1274
  if (Object.keys(prodUrls).length > 0) {
1282
- prodUrls['mongodb'] = '${PROD_MONGODB_URL}';
1283
1275
  environments.production = {
1284
1276
  description: 'Production (use with caution)',
1285
1277
  urls: prodUrls,
@@ -1314,7 +1306,6 @@ async function setupEnvironments(scan, config, isMultiRepo = false, existingEnvV
1314
1306
  description: 'Production (use with caution)',
1315
1307
  urls: {
1316
1308
  api: prodApiUrl,
1317
- mongodb: '${PROD_MONGODB_URL}',
1318
1309
  },
1319
1310
  safety: {
1320
1311
  read_only: true,
@@ -279,6 +279,10 @@ class ConfigLoader {
279
279
  }
280
280
  /**
281
281
  * Deep merge objects
282
+ *
283
+ * Special handling:
284
+ * - 'version' field is never overwritten (prevents v4 -> v1 downgrades)
285
+ * - Empty objects don't overwrite non-empty objects
282
286
  */
283
287
  deepMerge(target, source) {
284
288
  for (const key of Object.keys(source)) {
@@ -286,6 +290,21 @@ class ConfigLoader {
286
290
  const targetValue = target[key];
287
291
  if (sourceValue === undefined)
288
292
  continue;
293
+ // Never overwrite version field - workspace config takes precedence
294
+ if (key === 'version' && targetValue !== undefined)
295
+ continue;
296
+ // Don't overwrite non-empty objects with empty objects
297
+ if (typeof sourceValue === 'object' &&
298
+ sourceValue !== null &&
299
+ !Array.isArray(sourceValue) &&
300
+ Object.keys(sourceValue).length === 0 &&
301
+ typeof targetValue === 'object' &&
302
+ targetValue !== null &&
303
+ !Array.isArray(targetValue) &&
304
+ Object.keys(targetValue).length > 0) {
305
+ // Skip - don't let empty {} overwrite populated object
306
+ continue;
307
+ }
289
308
  if (typeof sourceValue === 'object' &&
290
309
  sourceValue !== null &&
291
310
  !Array.isArray(sourceValue) &&
@@ -520,33 +520,37 @@ class ProfileResolver {
520
520
  }
521
521
  }
522
522
  }
523
- // Add database URL
524
- if (database.mode === 'local') {
525
- env['MONGODB_URI'] = `mongodb://localhost:27017/${config.project.name}`;
526
- }
527
- else if (database.url) {
528
- env['MONGODB_URI'] = database.url;
529
- }
530
- // Add infrastructure URLs
531
- for (const infra of infrastructure) {
532
- if (infra.mode === 'local') {
533
- switch (infra.type) {
534
- case 'cache':
535
- env['REDIS_URL'] = `redis://localhost:${infra.port || 6379}`;
536
- break;
537
- case 'queue':
538
- env['RABBITMQ_URL'] = `amqp://localhost:${infra.port || 5672}`;
539
- break;
540
- }
523
+ // Only add database/infrastructure URLs if there are backend apps that need them
524
+ const hasBackendApps = apps.some(a => a.type === 'backend' || a.name === 'api');
525
+ if (hasBackendApps) {
526
+ // Add database URL
527
+ if (database.mode === 'local') {
528
+ env['MONGODB_URI'] = `mongodb://localhost:27017/${config.project.name}`;
541
529
  }
542
- else if (infra.url) {
543
- switch (infra.type) {
544
- case 'cache':
545
- env['REDIS_URL'] = infra.url;
546
- break;
547
- case 'queue':
548
- env['RABBITMQ_URL'] = infra.url;
549
- break;
530
+ else if (database.url) {
531
+ env['MONGODB_URI'] = database.url;
532
+ }
533
+ // Add infrastructure URLs
534
+ for (const infra of infrastructure) {
535
+ if (infra.mode === 'local') {
536
+ switch (infra.type) {
537
+ case 'cache':
538
+ env['REDIS_URL'] = `redis://localhost:${infra.port || 6379}`;
539
+ break;
540
+ case 'queue':
541
+ env['RABBITMQ_URL'] = `amqp://localhost:${infra.port || 5672}`;
542
+ break;
543
+ }
544
+ }
545
+ else if (infra.url) {
546
+ switch (infra.type) {
547
+ case 'cache':
548
+ env['REDIS_URL'] = infra.url;
549
+ break;
550
+ case 'queue':
551
+ env['RABBITMQ_URL'] = infra.url;
552
+ break;
553
+ }
550
554
  }
551
555
  }
552
556
  }
@@ -0,0 +1,124 @@
1
+ "use strict";
2
+ /**
3
+ * Random name generator for Genbox environments
4
+ * Generates memorable names using adjective-noun combinations
5
+ * ~150 adjectives x ~150 nouns = ~22,500 unique combinations
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.TOTAL_COMBINATIONS = void 0;
9
+ exports.generateRandomName = generateRandomName;
10
+ exports.generateNameSuggestions = generateNameSuggestions;
11
+ const adjectives = [
12
+ // Colors & Shades
13
+ 'red', 'blue', 'green', 'gold', 'silver', 'bronze', 'copper', 'amber',
14
+ 'coral', 'ivory', 'onyx', 'jade', 'ruby', 'pearl', 'azure', 'cobalt',
15
+ 'crimson', 'scarlet', 'violet', 'indigo', 'teal', 'cyan', 'magenta',
16
+ 'golden', 'rusty', 'dusty', 'misty', 'frosty', 'snowy', 'icy',
17
+ // Weather & Nature
18
+ 'sunny', 'windy', 'rainy', 'stormy', 'cloudy', 'foggy', 'hazy',
19
+ 'balmy', 'breezy', 'dewy', 'humid', 'arid', 'polar', 'tropic',
20
+ // Temperature & Light
21
+ 'warm', 'cool', 'cold', 'hot', 'bright', 'dim', 'dark', 'light',
22
+ 'glowing', 'shiny', 'glossy', 'matte', 'vivid', 'pale', 'deep',
23
+ // Size & Shape
24
+ 'tiny', 'small', 'big', 'giant', 'vast', 'wide', 'narrow', 'tall',
25
+ 'short', 'long', 'round', 'flat', 'curved', 'steep', 'slim', 'broad',
26
+ // Speed & Movement
27
+ 'swift', 'quick', 'fast', 'rapid', 'slow', 'steady', 'still', 'calm',
28
+ 'wild', 'fierce', 'gentle', 'smooth', 'rough', 'bumpy', 'fluid',
29
+ // Character & Mood
30
+ 'bold', 'brave', 'keen', 'wise', 'smart', 'clever', 'witty', 'sharp',
31
+ 'kind', 'fair', 'proud', 'humble', 'noble', 'royal', 'grand', 'prime',
32
+ 'happy', 'merry', 'jolly', 'lively', 'peppy', 'zesty', 'perky', 'chirpy',
33
+ 'lucky', 'plucky', 'spunky', 'funky', 'groovy', 'snappy', 'zippy',
34
+ // Quality & State
35
+ 'fresh', 'crisp', 'clean', 'pure', 'clear', 'free', 'open', 'safe',
36
+ 'solid', 'stable', 'sturdy', 'strong', 'tough', 'hardy', 'robust',
37
+ 'sleek', 'slick', 'neat', 'tidy', 'trim', 'lean', 'fit', 'agile',
38
+ // Time & Age
39
+ 'new', 'young', 'early', 'late', 'ancient', 'modern', 'future', 'prime',
40
+ 'fresh', 'ripe', 'mature', 'seasoned', 'timely', 'eternal', 'lasting',
41
+ // Sound & Texture
42
+ 'silent', 'quiet', 'loud', 'sonic', 'soft', 'hard', 'dense', 'hollow',
43
+ 'fuzzy', 'fluffy', 'silky', 'velvet', 'smooth', 'grainy', 'sandy',
44
+ // Taste & Smell (abstract)
45
+ 'sweet', 'tangy', 'spicy', 'minty', 'zesty', 'savory', 'rich', 'mellow',
46
+ // Position & Direction
47
+ 'upper', 'lower', 'inner', 'outer', 'central', 'polar', 'lateral',
48
+ 'north', 'south', 'east', 'west', 'coastal', 'alpine', 'lunar', 'solar',
49
+ ];
50
+ const nouns = [
51
+ // Animals - Land
52
+ 'fox', 'wolf', 'bear', 'deer', 'elk', 'moose', 'hare', 'rabbit',
53
+ 'lynx', 'puma', 'tiger', 'lion', 'panther', 'jaguar', 'leopard',
54
+ 'horse', 'zebra', 'buffalo', 'bison', 'ox', 'ram', 'goat', 'sheep',
55
+ 'badger', 'otter', 'beaver', 'marten', 'ferret', 'mink', 'stoat',
56
+ 'squirrel', 'chipmunk', 'hedgehog', 'porcupine', 'armadillo',
57
+ // Animals - Birds
58
+ 'owl', 'hawk', 'eagle', 'falcon', 'raven', 'crow', 'jay', 'finch',
59
+ 'robin', 'sparrow', 'wren', 'lark', 'dove', 'pigeon', 'swan', 'crane',
60
+ 'heron', 'stork', 'pelican', 'albatross', 'condor', 'vulture', 'kite',
61
+ 'osprey', 'harrier', 'kestrel', 'merlin', 'shrike', 'oriole', 'thrush',
62
+ // Animals - Water
63
+ 'seal', 'walrus', 'whale', 'dolphin', 'shark', 'orca', 'salmon', 'trout',
64
+ 'bass', 'pike', 'perch', 'carp', 'tuna', 'marlin', 'barracuda',
65
+ 'squid', 'octopus', 'crab', 'lobster', 'shrimp', 'oyster', 'clam',
66
+ // Trees & Plants
67
+ 'oak', 'pine', 'elm', 'ash', 'birch', 'maple', 'cedar', 'spruce',
68
+ 'willow', 'poplar', 'aspen', 'alder', 'beech', 'cherry', 'walnut',
69
+ 'cypress', 'juniper', 'hemlock', 'sequoia', 'redwood', 'mahogany',
70
+ 'sage', 'fern', 'moss', 'ivy', 'vine', 'reed', 'bamboo', 'palm',
71
+ 'cactus', 'lotus', 'lily', 'rose', 'tulip', 'orchid', 'iris', 'daisy',
72
+ // Landforms & Geography
73
+ 'peak', 'summit', 'ridge', 'cliff', 'bluff', 'mesa', 'butte', 'dune',
74
+ 'vale', 'glen', 'dale', 'dell', 'gorge', 'canyon', 'ravine', 'gulch',
75
+ 'plain', 'prairie', 'meadow', 'field', 'grove', 'forest', 'jungle',
76
+ 'island', 'atoll', 'reef', 'cape', 'bay', 'cove', 'inlet', 'fjord',
77
+ // Water Bodies
78
+ 'river', 'stream', 'creek', 'brook', 'spring', 'falls', 'rapids',
79
+ 'lake', 'pond', 'pool', 'lagoon', 'marsh', 'swamp', 'delta', 'estuary',
80
+ 'ocean', 'sea', 'gulf', 'strait', 'channel', 'tide', 'wave', 'surf',
81
+ // Sky & Space
82
+ 'moon', 'sun', 'star', 'comet', 'meteor', 'nova', 'nebula', 'quasar',
83
+ 'cloud', 'storm', 'rain', 'snow', 'frost', 'hail', 'mist', 'fog',
84
+ 'wind', 'gale', 'breeze', 'gust', 'draft', 'zephyr', 'monsoon',
85
+ 'dawn', 'dusk', 'noon', 'night', 'twilight', 'aurora', 'eclipse',
86
+ // Stones & Minerals
87
+ 'stone', 'rock', 'boulder', 'pebble', 'gravel', 'sand', 'clay', 'chalk',
88
+ 'crystal', 'gem', 'diamond', 'emerald', 'sapphire', 'topaz', 'opal',
89
+ 'granite', 'marble', 'slate', 'quartz', 'flint', 'obsidian', 'basalt',
90
+ // Misc Nature
91
+ 'flame', 'ember', 'spark', 'blaze', 'torch', 'beacon', 'flare',
92
+ 'shadow', 'shade', 'glint', 'gleam', 'shimmer', 'glimmer', 'flash',
93
+ 'echo', 'whisper', 'murmur', 'rustle', 'ripple', 'rumble', 'thunder',
94
+ ];
95
+ function randomElement(array) {
96
+ return array[Math.floor(Math.random() * array.length)];
97
+ }
98
+ /**
99
+ * Generate a random environment name
100
+ * Format: adjective-noun (e.g., "swift-fox", "calm-river")
101
+ */
102
+ function generateRandomName() {
103
+ const adjective = randomElement(adjectives);
104
+ const noun = randomElement(nouns);
105
+ return `${adjective}-${noun}`;
106
+ }
107
+ /**
108
+ * Generate multiple unique random name suggestions
109
+ */
110
+ function generateNameSuggestions(count = 3) {
111
+ const names = new Set();
112
+ // Prevent infinite loop if requesting more than possible combinations
113
+ const maxAttempts = count * 10;
114
+ let attempts = 0;
115
+ while (names.size < count && attempts < maxAttempts) {
116
+ names.add(generateRandomName());
117
+ attempts++;
118
+ }
119
+ return Array.from(names);
120
+ }
121
+ /**
122
+ * Total possible unique combinations
123
+ */
124
+ exports.TOTAL_COMBINATIONS = adjectives.length * nouns.length;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.23",
3
+ "version": "1.0.25",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {