genbox 1.0.64 → 1.0.66

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.
@@ -0,0 +1,231 @@
1
+ "use strict";
2
+ /**
3
+ * Branch Prompt Utilities
4
+ * Shared functions for interactive branch selection
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ exports.promptForBranchOptionsCreate = promptForBranchOptionsCreate;
44
+ exports.promptForBranchOptionsRebuild = promptForBranchOptionsRebuild;
45
+ const prompts = __importStar(require("@inquirer/prompts"));
46
+ const chalk_1 = __importDefault(require("chalk"));
47
+ /**
48
+ * Prompt for branch options interactively (for create command)
49
+ * Default behavior: create new branch from configured default (or 'main') with branch name = environment name
50
+ */
51
+ async function promptForBranchOptionsCreate(resolved, config, envName) {
52
+ // Get the default/base branch from config (used as source for new branches)
53
+ const baseBranch = config.defaults?.branch || 'main';
54
+ console.log(chalk_1.default.blue('=== Branch Configuration ==='));
55
+ console.log('');
56
+ const branchChoice = await prompts.select({
57
+ message: 'Branch option:',
58
+ choices: [
59
+ {
60
+ name: `${chalk_1.default.green('Create new branch')} '${chalk_1.default.cyan(envName)}' from '${baseBranch}' ${chalk_1.default.dim('(recommended)')}`,
61
+ value: 'new-from-default',
62
+ },
63
+ {
64
+ name: `Create new branch from a different source`,
65
+ value: 'new-custom',
66
+ },
67
+ {
68
+ name: 'Use an existing branch',
69
+ value: 'existing',
70
+ },
71
+ {
72
+ name: `Use '${baseBranch}' directly without creating new branch`,
73
+ value: 'default',
74
+ },
75
+ ],
76
+ default: 'new-from-default',
77
+ });
78
+ if (branchChoice === 'new-from-default') {
79
+ // Create new branch from configured default with name = environment name
80
+ return {
81
+ ...resolved,
82
+ repos: resolved.repos.map(repo => ({
83
+ ...repo,
84
+ branch: envName,
85
+ newBranch: envName,
86
+ sourceBranch: baseBranch,
87
+ })),
88
+ };
89
+ }
90
+ if (branchChoice === 'new-custom') {
91
+ const newBranchName = await prompts.input({
92
+ message: 'New branch name:',
93
+ default: envName,
94
+ validate: (value) => {
95
+ if (!value.trim())
96
+ return 'Branch name is required';
97
+ if (!/^[\w\-./]+$/.test(value))
98
+ return 'Invalid branch name (use letters, numbers, -, _, /, .)';
99
+ return true;
100
+ },
101
+ });
102
+ const sourceBranch = await prompts.input({
103
+ message: 'Create from branch:',
104
+ default: 'main',
105
+ validate: (value) => {
106
+ if (!value.trim())
107
+ return 'Source branch is required';
108
+ return true;
109
+ },
110
+ });
111
+ // Update all repos with new branch info
112
+ return {
113
+ ...resolved,
114
+ repos: resolved.repos.map(repo => ({
115
+ ...repo,
116
+ branch: newBranchName.trim(),
117
+ newBranch: newBranchName.trim(),
118
+ sourceBranch: sourceBranch.trim(),
119
+ })),
120
+ };
121
+ }
122
+ if (branchChoice === 'existing') {
123
+ const branchName = await prompts.input({
124
+ message: 'Enter branch name:',
125
+ default: baseBranch,
126
+ validate: (value) => {
127
+ if (!value.trim())
128
+ return 'Branch name is required';
129
+ return true;
130
+ },
131
+ });
132
+ // Update all repos with the selected branch
133
+ return {
134
+ ...resolved,
135
+ repos: resolved.repos.map(repo => ({
136
+ ...repo,
137
+ branch: branchName.trim(),
138
+ newBranch: undefined,
139
+ sourceBranch: undefined,
140
+ })),
141
+ };
142
+ }
143
+ if (branchChoice === 'default') {
144
+ // Keep resolved repos as-is (no new branch)
145
+ return resolved;
146
+ }
147
+ return resolved;
148
+ }
149
+ /**
150
+ * Prompt for branch options interactively (for rebuild command)
151
+ * Simpler version - just switch branches or create new
152
+ */
153
+ async function promptForBranchOptionsRebuild(resolved, config) {
154
+ // Get the default branch from config or first repo
155
+ const defaultBranch = config.defaults?.branch || resolved.repos[0]?.branch || 'main';
156
+ console.log(chalk_1.default.blue('=== Branch Configuration ==='));
157
+ console.log(chalk_1.default.dim(`Default branch: ${defaultBranch}`));
158
+ console.log('');
159
+ const branchChoice = await prompts.select({
160
+ message: 'Branch option:',
161
+ choices: [
162
+ {
163
+ name: `Use default branch (${defaultBranch})`,
164
+ value: 'default',
165
+ },
166
+ {
167
+ name: 'Use a different existing branch',
168
+ value: 'existing',
169
+ },
170
+ {
171
+ name: 'Create a new branch',
172
+ value: 'new',
173
+ },
174
+ ],
175
+ default: 'default',
176
+ });
177
+ if (branchChoice === 'default') {
178
+ return resolved;
179
+ }
180
+ if (branchChoice === 'existing') {
181
+ const branchName = await prompts.input({
182
+ message: 'Enter branch name:',
183
+ default: defaultBranch,
184
+ validate: (value) => {
185
+ if (!value.trim())
186
+ return 'Branch name is required';
187
+ return true;
188
+ },
189
+ });
190
+ return {
191
+ ...resolved,
192
+ repos: resolved.repos.map(repo => ({
193
+ ...repo,
194
+ branch: branchName.trim(),
195
+ newBranch: undefined,
196
+ sourceBranch: undefined,
197
+ })),
198
+ };
199
+ }
200
+ if (branchChoice === 'new') {
201
+ const newBranchName = await prompts.input({
202
+ message: 'New branch name:',
203
+ validate: (value) => {
204
+ if (!value.trim())
205
+ return 'Branch name is required';
206
+ if (!/^[\w\-./]+$/.test(value))
207
+ return 'Invalid branch name (use letters, numbers, -, _, /, .)';
208
+ return true;
209
+ },
210
+ });
211
+ const sourceBranch = await prompts.input({
212
+ message: 'Create from branch:',
213
+ default: defaultBranch,
214
+ validate: (value) => {
215
+ if (!value.trim())
216
+ return 'Source branch is required';
217
+ return true;
218
+ },
219
+ });
220
+ return {
221
+ ...resolved,
222
+ repos: resolved.repos.map(repo => ({
223
+ ...repo,
224
+ branch: newBranchName.trim(),
225
+ newBranch: newBranchName.trim(),
226
+ sourceBranch: sourceBranch.trim(),
227
+ })),
228
+ };
229
+ }
230
+ return resolved;
231
+ }
@@ -0,0 +1,127 @@
1
+ "use strict";
2
+ /**
3
+ * Environment File Parser Utilities
4
+ * Shared functions for parsing and processing .env.genbox files
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.parseEnvGenboxSections = parseEnvGenboxSections;
8
+ exports.buildServiceUrlMap = buildServiceUrlMap;
9
+ exports.buildAppEnvContent = buildAppEnvContent;
10
+ exports.parseEnvVarsFromSection = parseEnvVarsFromSection;
11
+ /**
12
+ * Parse .env.genbox file into segregated sections
13
+ * Sections are marked with: # === SECTION_NAME ===
14
+ */
15
+ function parseEnvGenboxSections(content) {
16
+ const sections = new Map();
17
+ let currentSection = 'GLOBAL';
18
+ let currentContent = [];
19
+ for (const line of content.split('\n')) {
20
+ const sectionMatch = line.match(/^# === ([^=]+) ===$/);
21
+ if (sectionMatch) {
22
+ // Save previous section
23
+ if (currentContent.length > 0) {
24
+ sections.set(currentSection, currentContent.join('\n').trim());
25
+ }
26
+ currentSection = sectionMatch[1].trim();
27
+ currentContent = [];
28
+ }
29
+ else if (currentSection !== 'END') {
30
+ currentContent.push(line);
31
+ }
32
+ }
33
+ // Save last section
34
+ if (currentContent.length > 0 && currentSection !== 'END') {
35
+ sections.set(currentSection, currentContent.join('\n').trim());
36
+ }
37
+ return sections;
38
+ }
39
+ /**
40
+ * Build a map of service URL variables based on connection type
41
+ * e.g., if connectTo=staging: GATEWAY_URL → STAGING_GATEWAY_URL value
42
+ */
43
+ function buildServiceUrlMap(envVarsFromFile, connectTo) {
44
+ const urlMap = {};
45
+ const prefix = connectTo ? `${connectTo.toUpperCase()}_` : 'LOCAL_';
46
+ // Find all service URL variables (LOCAL_*_URL and STAGING_*_URL patterns)
47
+ const serviceNames = new Set();
48
+ for (const key of Object.keys(envVarsFromFile)) {
49
+ const match = key.match(/^(LOCAL|STAGING|PRODUCTION)_(.+_URL)$/);
50
+ if (match) {
51
+ serviceNames.add(match[2]);
52
+ }
53
+ }
54
+ // Build mapping: VARNAME → value from appropriate prefix
55
+ for (const serviceName of serviceNames) {
56
+ const prefixedKey = `${prefix}${serviceName}`;
57
+ const localKey = `LOCAL_${serviceName}`;
58
+ // Use prefixed value if available, otherwise fall back to local
59
+ const value = envVarsFromFile[prefixedKey] || envVarsFromFile[localKey];
60
+ if (value) {
61
+ urlMap[serviceName] = value;
62
+ }
63
+ }
64
+ // Also handle legacy API_URL for backwards compatibility
65
+ if (!urlMap['API_URL']) {
66
+ const apiUrl = envVarsFromFile[`${prefix}API_URL`] ||
67
+ envVarsFromFile['LOCAL_API_URL'] ||
68
+ envVarsFromFile['STAGING_API_URL'];
69
+ if (apiUrl) {
70
+ urlMap['API_URL'] = apiUrl;
71
+ }
72
+ }
73
+ return urlMap;
74
+ }
75
+ /**
76
+ * Build env content for a specific app by combining GLOBAL + app-specific sections
77
+ */
78
+ function buildAppEnvContent(sections, appName, serviceUrlMap) {
79
+ const parts = [];
80
+ // Always include GLOBAL section
81
+ const globalSection = sections.get('GLOBAL');
82
+ if (globalSection) {
83
+ parts.push(globalSection);
84
+ }
85
+ // Include app-specific section if exists
86
+ const appSection = sections.get(appName);
87
+ if (appSection) {
88
+ parts.push(appSection);
89
+ }
90
+ let envContent = parts.join('\n\n');
91
+ // Expand all ${VARNAME} references using the service URL map
92
+ for (const [varName, value] of Object.entries(serviceUrlMap)) {
93
+ const pattern = new RegExp(`\\$\\{${varName}\\}`, 'g');
94
+ envContent = envContent.replace(pattern, value);
95
+ }
96
+ // Keep only actual env vars (filter out pure comment lines but keep var definitions)
97
+ envContent = envContent
98
+ .split('\n')
99
+ .filter(line => {
100
+ const trimmed = line.trim();
101
+ // Keep empty lines, lines with = (even if commented), and non-comment lines
102
+ return trimmed === '' || trimmed.includes('=') || !trimmed.startsWith('#');
103
+ })
104
+ .join('\n')
105
+ .replace(/\n{3,}/g, '\n\n')
106
+ .trim();
107
+ return envContent;
108
+ }
109
+ /**
110
+ * Parse env vars from a section content string
111
+ */
112
+ function parseEnvVarsFromSection(sectionContent) {
113
+ const envVars = {};
114
+ for (const line of sectionContent.split('\n')) {
115
+ const match = line.match(/^([A-Z_][A-Z0-9_]*)=(.*)$/);
116
+ if (match) {
117
+ let value = match[2].trim();
118
+ // Remove quotes if present
119
+ if ((value.startsWith('"') && value.endsWith('"')) ||
120
+ (value.startsWith("'") && value.endsWith("'"))) {
121
+ value = value.slice(1, -1);
122
+ }
123
+ envVars[match[1]] = value;
124
+ }
125
+ }
126
+ return envVars;
127
+ }
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ /**
3
+ * Git Utilities
4
+ * Shared functions for git configuration and validation
5
+ */
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.validateGitCredentials = validateGitCredentials;
8
+ exports.getGitConfig = getGitConfig;
9
+ const child_process_1 = require("child_process");
10
+ /**
11
+ * Validate git configuration and warn about missing credentials
12
+ */
13
+ function validateGitCredentials(repos, gitToken, privateKey) {
14
+ const warnings = [];
15
+ const errors = [];
16
+ for (const repo of repos) {
17
+ const isHttps = repo.url.startsWith('https://');
18
+ const isSsh = repo.url.startsWith('git@') || repo.url.includes('ssh://');
19
+ const isPrivateRepo = repo.url.includes('github.com') && !repo.url.includes('/public/');
20
+ if (isHttps && !gitToken && isPrivateRepo) {
21
+ warnings.push(`Repository '${repo.name}' uses HTTPS URL but GIT_TOKEN is not set.`);
22
+ warnings.push(` Add GIT_TOKEN=<your-github-token> to .env.genbox for private repos.`);
23
+ }
24
+ if (isSsh && !privateKey) {
25
+ warnings.push(`Repository '${repo.name}' uses SSH URL but no SSH key was injected.`);
26
+ }
27
+ }
28
+ return { warnings, errors };
29
+ }
30
+ /**
31
+ * Get local git config for commits on genbox
32
+ */
33
+ function getGitConfig() {
34
+ let userName;
35
+ let userEmail;
36
+ try {
37
+ userName = (0, child_process_1.execSync)('git config --global user.name', { encoding: 'utf-8' }).trim();
38
+ }
39
+ catch {
40
+ // Git config not set
41
+ }
42
+ try {
43
+ userEmail = (0, child_process_1.execSync)('git config --global user.email', { encoding: 'utf-8' }).trim();
44
+ }
45
+ catch {
46
+ // Git config not set
47
+ }
48
+ return { userName, userEmail };
49
+ }
@@ -0,0 +1,24 @@
1
+ "use strict";
2
+ /**
3
+ * Shared Utilities
4
+ * Re-exports all utility modules for convenient importing
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
18
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
19
+ };
20
+ Object.defineProperty(exports, "__esModule", { value: true });
21
+ __exportStar(require("./ssh-keys"), exports);
22
+ __exportStar(require("./env-parser"), exports);
23
+ __exportStar(require("./git-utils"), exports);
24
+ __exportStar(require("./branch-prompt"), exports);
@@ -0,0 +1,98 @@
1
+ "use strict";
2
+ /**
3
+ * SSH Key Utilities
4
+ * Shared functions for reading SSH keys
5
+ */
6
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
7
+ if (k2 === undefined) k2 = k;
8
+ var desc = Object.getOwnPropertyDescriptor(m, k);
9
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
10
+ desc = { enumerable: true, get: function() { return m[k]; } };
11
+ }
12
+ Object.defineProperty(o, k2, desc);
13
+ }) : (function(o, m, k, k2) {
14
+ if (k2 === undefined) k2 = k;
15
+ o[k2] = m[k];
16
+ }));
17
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
18
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
19
+ }) : function(o, v) {
20
+ o["default"] = v;
21
+ });
22
+ var __importStar = (this && this.__importStar) || (function () {
23
+ var ownKeys = function(o) {
24
+ ownKeys = Object.getOwnPropertyNames || function (o) {
25
+ var ar = [];
26
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
27
+ return ar;
28
+ };
29
+ return ownKeys(o);
30
+ };
31
+ return function (mod) {
32
+ if (mod && mod.__esModule) return mod;
33
+ var result = {};
34
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
35
+ __setModuleDefault(result, mod);
36
+ return result;
37
+ };
38
+ })();
39
+ Object.defineProperty(exports, "__esModule", { value: true });
40
+ exports.getPublicSshKey = getPublicSshKey;
41
+ exports.getPrivateSshKey = getPrivateSshKey;
42
+ exports.getPrivateSshKeyPath = getPrivateSshKeyPath;
43
+ const fs = __importStar(require("fs"));
44
+ const path = __importStar(require("path"));
45
+ const os = __importStar(require("os"));
46
+ /**
47
+ * Get the public SSH key content
48
+ * Tries ed25519 first, then RSA
49
+ */
50
+ function getPublicSshKey() {
51
+ const home = os.homedir();
52
+ const potentialKeys = [
53
+ path.join(home, '.ssh', 'id_ed25519.pub'),
54
+ path.join(home, '.ssh', 'id_rsa.pub'),
55
+ ];
56
+ for (const keyPath of potentialKeys) {
57
+ if (fs.existsSync(keyPath)) {
58
+ const content = fs.readFileSync(keyPath, 'utf-8').trim();
59
+ if (content)
60
+ return content;
61
+ }
62
+ }
63
+ throw new Error('No public SSH key found in ~/.ssh/');
64
+ }
65
+ /**
66
+ * Get the private SSH key content (for injection into remote server)
67
+ * Returns undefined if no key found
68
+ */
69
+ function getPrivateSshKey() {
70
+ const home = os.homedir();
71
+ const potentialKeys = [
72
+ path.join(home, '.ssh', 'id_ed25519'),
73
+ path.join(home, '.ssh', 'id_rsa'),
74
+ ];
75
+ for (const keyPath of potentialKeys) {
76
+ if (fs.existsSync(keyPath)) {
77
+ return fs.readFileSync(keyPath, 'utf-8');
78
+ }
79
+ }
80
+ return undefined;
81
+ }
82
+ /**
83
+ * Get the path to the private SSH key (for local SSH commands)
84
+ * Throws if no key found
85
+ */
86
+ function getPrivateSshKeyPath() {
87
+ const home = os.homedir();
88
+ const potentialKeys = [
89
+ path.join(home, '.ssh', 'id_ed25519'),
90
+ path.join(home, '.ssh', 'id_rsa'),
91
+ ];
92
+ for (const keyPath of potentialKeys) {
93
+ if (fs.existsSync(keyPath)) {
94
+ return keyPath;
95
+ }
96
+ }
97
+ throw new Error('No SSH private key found in ~/.ssh/');
98
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genbox",
3
- "version": "1.0.64",
3
+ "version": "1.0.66",
4
4
  "description": "Genbox CLI - AI-Powered Development Environments",
5
5
  "main": "dist/index.js",
6
6
  "bin": {