genbox 1.0.0
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 +38 -0
- package/dist/commands/balance.js +21 -0
- package/dist/commands/connect.js +96 -0
- package/dist/commands/create.js +219 -0
- package/dist/commands/destroy.js +62 -0
- package/dist/commands/forward.js +166 -0
- package/dist/commands/help.js +404 -0
- package/dist/commands/init.js +488 -0
- package/dist/commands/list.js +36 -0
- package/dist/commands/login.js +56 -0
- package/dist/commands/push.js +191 -0
- package/dist/commands/restore-db.js +273 -0
- package/dist/commands/status.js +294 -0
- package/dist/commands/urls.js +74 -0
- package/dist/config-store.js +65 -0
- package/dist/config.js +162 -0
- package/dist/importer.js +103 -0
- package/dist/index.js +39 -0
- package/dist/scan.js +247 -0
- package/dist/schema.js +2 -0
- package/dist/ssh-config.js +216 -0
- package/package.json +63 -0
package/dist/importer.js
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
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.importLegacyScript = importLegacyScript;
|
|
40
|
+
const fs = __importStar(require("fs"));
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
43
|
+
function importLegacyScript(scriptPath, outputDir) {
|
|
44
|
+
console.log(chalk_1.default.blue(`Importing legacy script: ${scriptPath}`));
|
|
45
|
+
const content = fs.readFileSync(scriptPath, 'utf-8');
|
|
46
|
+
const extractedScripts = [];
|
|
47
|
+
const extractedFiles = [];
|
|
48
|
+
// Regex to find write_files blocks
|
|
49
|
+
// Matches: - path: <path>\n ... content: |\n<content>
|
|
50
|
+
// We'll capture path and content until the next indentation change or list item
|
|
51
|
+
// This is a heuristic parser.
|
|
52
|
+
const fileRegex = /-\s+path: ([\w/.-]+)\s+(?:permissions: '(\d+)'\s+)?(?:owner: [\w:]+\s+)?(?:defer: \w+\s+)?content: \|\n((?: {6,}.*\n)+)/g;
|
|
53
|
+
let match;
|
|
54
|
+
while ((match = fileRegex.exec(content)) !== null) {
|
|
55
|
+
const remotePath = match[1];
|
|
56
|
+
const permissions = match[2];
|
|
57
|
+
const rawBody = match[3];
|
|
58
|
+
const fileName = path.basename(remotePath);
|
|
59
|
+
// De-indent
|
|
60
|
+
// Find base indentation of first line
|
|
61
|
+
const firstLineParams = rawBody.match(/^(\s+)/);
|
|
62
|
+
const indent = firstLineParams ? firstLineParams[1].length : 0;
|
|
63
|
+
const cleanBody = rawBody.split('\n').map(line => {
|
|
64
|
+
if (line.trim() === '')
|
|
65
|
+
return '';
|
|
66
|
+
return line.substring(indent);
|
|
67
|
+
}).join('\n');
|
|
68
|
+
// Determine destination
|
|
69
|
+
let localSubDir = 'files';
|
|
70
|
+
if (fileName.endsWith('.sh')) {
|
|
71
|
+
localSubDir = 'scripts';
|
|
72
|
+
}
|
|
73
|
+
const localDir = path.join(outputDir, localSubDir);
|
|
74
|
+
if (!fs.existsSync(localDir)) {
|
|
75
|
+
fs.mkdirSync(localDir, { recursive: true });
|
|
76
|
+
}
|
|
77
|
+
const localPath = path.join(localDir, fileName);
|
|
78
|
+
fs.writeFileSync(localPath, cleanBody);
|
|
79
|
+
console.log(chalk_1.default.dim(` Extracted ${fileName}`));
|
|
80
|
+
const relativeLocalPath = path.join(localSubDir, fileName);
|
|
81
|
+
if (localSubDir === 'scripts') {
|
|
82
|
+
// We'll add this to scripts if it looks like a setup script
|
|
83
|
+
// For parity, the script runs specific ones.
|
|
84
|
+
// setup-goodpass.sh is main. install-caddy.sh is aux.
|
|
85
|
+
// We'll add all .sh files to 'files' bundle, and user can choose which to RUN in 'scripts' config.
|
|
86
|
+
// Wait, genbox config 'scripts' are executed. 'files' are just placed.
|
|
87
|
+
// The legacy script executed 'install-caddy.sh' then 'setup-goodpass.sh'.
|
|
88
|
+
// We should treat them as files first.
|
|
89
|
+
}
|
|
90
|
+
extractedFiles.push({
|
|
91
|
+
source: relativeLocalPath,
|
|
92
|
+
target: remotePath,
|
|
93
|
+
permissions: permissions || '0644'
|
|
94
|
+
});
|
|
95
|
+
if (fileName === 'setup-goodpass.sh') {
|
|
96
|
+
extractedScripts.push(relativeLocalPath); // Mark for execution
|
|
97
|
+
}
|
|
98
|
+
else if (fileName === 'install-caddy.sh') {
|
|
99
|
+
extractedScripts.push(relativeLocalPath);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return { scripts: extractedScripts, files: extractedFiles };
|
|
103
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
4
|
+
const commander_1 = require("commander");
|
|
5
|
+
const init_1 = require("./commands/init");
|
|
6
|
+
const program = new commander_1.Command();
|
|
7
|
+
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
|
8
|
+
const { version } = require('../package.json');
|
|
9
|
+
program
|
|
10
|
+
.name('genbox')
|
|
11
|
+
.description('Genbox CLI - AI-Powered Development Environments')
|
|
12
|
+
.version(version, '-v, --version', 'Output the current version');
|
|
13
|
+
const create_1 = require("./commands/create");
|
|
14
|
+
const list_1 = require("./commands/list");
|
|
15
|
+
const destroy_1 = require("./commands/destroy");
|
|
16
|
+
const connect_1 = require("./commands/connect");
|
|
17
|
+
const balance_1 = require("./commands/balance");
|
|
18
|
+
const login_1 = require("./commands/login");
|
|
19
|
+
const urls_1 = require("./commands/urls");
|
|
20
|
+
const push_1 = require("./commands/push");
|
|
21
|
+
const status_1 = require("./commands/status");
|
|
22
|
+
const forward_1 = require("./commands/forward");
|
|
23
|
+
const restore_db_1 = require("./commands/restore-db");
|
|
24
|
+
const help_1 = require("./commands/help");
|
|
25
|
+
program
|
|
26
|
+
.addCommand(init_1.initCommand)
|
|
27
|
+
.addCommand(create_1.createCommand)
|
|
28
|
+
.addCommand(list_1.listCommand)
|
|
29
|
+
.addCommand(destroy_1.destroyCommand)
|
|
30
|
+
.addCommand(connect_1.connectCommand)
|
|
31
|
+
.addCommand(balance_1.balanceCommand)
|
|
32
|
+
.addCommand(login_1.loginCommand)
|
|
33
|
+
.addCommand(urls_1.urlsCommand)
|
|
34
|
+
.addCommand(push_1.pushCommand)
|
|
35
|
+
.addCommand(status_1.statusCommand)
|
|
36
|
+
.addCommand(forward_1.forwardCommand)
|
|
37
|
+
.addCommand(restore_db_1.restoreDbCommand)
|
|
38
|
+
.addCommand(help_1.helpCommand);
|
|
39
|
+
program.parse(process.argv);
|
package/dist/scan.js
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.detectSshKeys = detectSshKeys;
|
|
37
|
+
exports.getGitUrlType = getGitUrlType;
|
|
38
|
+
exports.sshToHttps = sshToHttps;
|
|
39
|
+
exports.httpsToSsh = httpsToSsh;
|
|
40
|
+
exports.scanProject = scanProject;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const os = __importStar(require("os"));
|
|
44
|
+
/**
|
|
45
|
+
* Detect available SSH keys in ~/.ssh/
|
|
46
|
+
*/
|
|
47
|
+
function detectSshKeys() {
|
|
48
|
+
const sshDir = path.join(os.homedir(), '.ssh');
|
|
49
|
+
const keys = [];
|
|
50
|
+
if (!fs.existsSync(sshDir)) {
|
|
51
|
+
return keys;
|
|
52
|
+
}
|
|
53
|
+
const files = fs.readdirSync(sshDir);
|
|
54
|
+
// Common private key patterns (not .pub files)
|
|
55
|
+
const keyPatterns = [
|
|
56
|
+
{ pattern: /^id_rsa$/, type: 'rsa' },
|
|
57
|
+
{ pattern: /^id_ed25519$/, type: 'ed25519' },
|
|
58
|
+
{ pattern: /^id_ecdsa$/, type: 'ecdsa' },
|
|
59
|
+
{ pattern: /^.*_rsa$/, type: 'rsa' },
|
|
60
|
+
{ pattern: /^.*_ed25519$/, type: 'ed25519' },
|
|
61
|
+
{ pattern: /^.*_github$/, type: 'other' },
|
|
62
|
+
{ pattern: /^github$/, type: 'other' },
|
|
63
|
+
];
|
|
64
|
+
for (const file of files) {
|
|
65
|
+
// Skip public keys and known non-key files
|
|
66
|
+
if (file.endsWith('.pub') || file === 'known_hosts' || file === 'config' || file === 'authorized_keys') {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const filePath = path.join(sshDir, file);
|
|
70
|
+
// Check if it's a file (not directory)
|
|
71
|
+
try {
|
|
72
|
+
const stat = fs.statSync(filePath);
|
|
73
|
+
if (!stat.isFile())
|
|
74
|
+
continue;
|
|
75
|
+
// Try to read first line to verify it's a private key
|
|
76
|
+
const content = fs.readFileSync(filePath, 'utf8').substring(0, 100);
|
|
77
|
+
if (!content.includes('PRIVATE KEY') && !content.includes('OPENSSH')) {
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
// Determine key type
|
|
81
|
+
let keyType = 'other';
|
|
82
|
+
for (const { pattern, type } of keyPatterns) {
|
|
83
|
+
if (pattern.test(file)) {
|
|
84
|
+
keyType = type;
|
|
85
|
+
break;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
keys.push({
|
|
89
|
+
path: filePath,
|
|
90
|
+
name: file,
|
|
91
|
+
type: keyType,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
catch (e) {
|
|
95
|
+
// Skip files we can't read
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return keys;
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check if a URL is SSH or HTTPS
|
|
102
|
+
*/
|
|
103
|
+
function getGitUrlType(url) {
|
|
104
|
+
if (url.startsWith('git@') || url.startsWith('ssh://')) {
|
|
105
|
+
return 'ssh';
|
|
106
|
+
}
|
|
107
|
+
return 'https';
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Convert SSH URL to HTTPS URL
|
|
111
|
+
*/
|
|
112
|
+
function sshToHttps(sshUrl) {
|
|
113
|
+
// git@github.com:user/repo.git -> https://github.com/user/repo.git
|
|
114
|
+
const match = sshUrl.match(/^git@([^:]+):(.+)$/);
|
|
115
|
+
if (match) {
|
|
116
|
+
return `https://${match[1]}/${match[2]}`;
|
|
117
|
+
}
|
|
118
|
+
return sshUrl;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Convert HTTPS URL to SSH URL
|
|
122
|
+
*/
|
|
123
|
+
function httpsToSsh(httpsUrl) {
|
|
124
|
+
// https://github.com/user/repo.git -> git@github.com:user/repo.git
|
|
125
|
+
const match = httpsUrl.match(/^https?:\/\/([^/]+)\/(.+)$/);
|
|
126
|
+
if (match) {
|
|
127
|
+
return `git@${match[1]}:${match[2]}`;
|
|
128
|
+
}
|
|
129
|
+
return httpsUrl;
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Detect port from various project files
|
|
133
|
+
*/
|
|
134
|
+
function detectPort(root) {
|
|
135
|
+
// 1. Check .env or .env.local for PORT
|
|
136
|
+
const envFiles = ['.env.local', '.env', '.env.development'];
|
|
137
|
+
for (const envFile of envFiles) {
|
|
138
|
+
const envPath = path.join(root, envFile);
|
|
139
|
+
if (fs.existsSync(envPath)) {
|
|
140
|
+
const content = fs.readFileSync(envPath, 'utf8');
|
|
141
|
+
const portMatch = content.match(/^PORT\s*=\s*(\d+)/m);
|
|
142
|
+
if (portMatch) {
|
|
143
|
+
return parseInt(portMatch[1], 10);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// 2. Check package.json scripts for port
|
|
148
|
+
const pkgPath = path.join(root, 'package.json');
|
|
149
|
+
if (fs.existsSync(pkgPath)) {
|
|
150
|
+
try {
|
|
151
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
152
|
+
const scripts = pkg.scripts || {};
|
|
153
|
+
// Check dev/start scripts for --port, -p, or PORT=
|
|
154
|
+
for (const scriptName of ['dev', 'start', 'serve']) {
|
|
155
|
+
const script = scripts[scriptName];
|
|
156
|
+
if (script) {
|
|
157
|
+
// Match patterns like: --port 3000, -p 3000, PORT=3000
|
|
158
|
+
const portMatch = script.match(/(?:--port\s+|(?:^|\s)-p\s+|PORT=)(\d+)/);
|
|
159
|
+
if (portMatch) {
|
|
160
|
+
return parseInt(portMatch[1], 10);
|
|
161
|
+
}
|
|
162
|
+
// Match patterns like: vite --port=5173
|
|
163
|
+
const portEqMatch = script.match(/--port=(\d+)/);
|
|
164
|
+
if (portEqMatch) {
|
|
165
|
+
return parseInt(portEqMatch[1], 10);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
catch (e) {
|
|
171
|
+
// Invalid JSON, skip
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// 3. Check docker-compose for exposed ports
|
|
175
|
+
const dockerComposeFiles = ['docker-compose.yml', 'docker-compose.yaml'];
|
|
176
|
+
for (const dcFile of dockerComposeFiles) {
|
|
177
|
+
const dcPath = path.join(root, dcFile);
|
|
178
|
+
if (fs.existsSync(dcPath)) {
|
|
179
|
+
const content = fs.readFileSync(dcPath, 'utf8');
|
|
180
|
+
// Simple regex to find first ports mapping like "3000:3000" or "- 3000:3000"
|
|
181
|
+
const portMatch = content.match(/ports:\s*\n\s*-\s*["']?(\d+):/m);
|
|
182
|
+
if (portMatch) {
|
|
183
|
+
return parseInt(portMatch[1], 10);
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
// 4. Check vite.config.ts/js for server.port
|
|
188
|
+
const viteConfigs = ['vite.config.ts', 'vite.config.js'];
|
|
189
|
+
for (const viteConfig of viteConfigs) {
|
|
190
|
+
const vitePath = path.join(root, viteConfig);
|
|
191
|
+
if (fs.existsSync(vitePath)) {
|
|
192
|
+
const content = fs.readFileSync(vitePath, 'utf8');
|
|
193
|
+
const portMatch = content.match(/port:\s*(\d+)/);
|
|
194
|
+
if (portMatch) {
|
|
195
|
+
return parseInt(portMatch[1], 10);
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
// 5. Check next.config.js for custom port (rare, but possible)
|
|
200
|
+
// Next.js typically uses 3000 by default, which we'll fall back to
|
|
201
|
+
return undefined;
|
|
202
|
+
}
|
|
203
|
+
function scanProject(root) {
|
|
204
|
+
const info = {
|
|
205
|
+
name: path.basename(root),
|
|
206
|
+
languages: {},
|
|
207
|
+
hasDocker: false,
|
|
208
|
+
foundScripts: [],
|
|
209
|
+
};
|
|
210
|
+
// 1. Detect Languages/Frameworks
|
|
211
|
+
if (fs.existsSync(path.join(root, 'package.json')))
|
|
212
|
+
info.languages.node = true;
|
|
213
|
+
if (fs.existsSync(path.join(root, 'requirements.txt')))
|
|
214
|
+
info.languages.python = true;
|
|
215
|
+
if (fs.existsSync(path.join(root, 'Gemfile')))
|
|
216
|
+
info.languages.ruby = true;
|
|
217
|
+
if (fs.existsSync(path.join(root, 'go.mod')))
|
|
218
|
+
info.languages.go = true;
|
|
219
|
+
// 2. Detect Docker
|
|
220
|
+
// Check for various docker compose names
|
|
221
|
+
const files = fs.readdirSync(root);
|
|
222
|
+
const dockerFiles = files.filter(f => f.startsWith('docker-compose') && (f.endsWith('.yml') || f.endsWith('.yaml')));
|
|
223
|
+
if (dockerFiles.length > 0)
|
|
224
|
+
info.hasDocker = true;
|
|
225
|
+
// 3. Detect Scripts (Standard)
|
|
226
|
+
const scriptsDir = path.join(root, 'scripts');
|
|
227
|
+
if (fs.existsSync(scriptsDir)) {
|
|
228
|
+
const scriptFiles = fs.readdirSync(scriptsDir);
|
|
229
|
+
info.foundScripts = scriptFiles
|
|
230
|
+
.filter(f => f.endsWith('.sh') && f !== 'hetzner-dev-env.sh')
|
|
231
|
+
.map(f => `scripts/${f}`);
|
|
232
|
+
}
|
|
233
|
+
// 4. Detect Git Repo
|
|
234
|
+
try {
|
|
235
|
+
const remote = require('child_process').execSync('git remote get-url origin', { cwd: root, stdio: 'pipe' }).toString().trim();
|
|
236
|
+
if (remote) {
|
|
237
|
+
info.gitRemote = remote;
|
|
238
|
+
info.gitRemoteType = getGitUrlType(remote);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
catch (e) {
|
|
242
|
+
// Not a git repo or no remote
|
|
243
|
+
}
|
|
244
|
+
// 5. Detect Port
|
|
245
|
+
info.detectedPort = detectPort(root);
|
|
246
|
+
return info;
|
|
247
|
+
}
|
package/dist/schema.js
ADDED
|
@@ -0,0 +1,216 @@
|
|
|
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
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.addSshConfigEntry = addSshConfigEntry;
|
|
37
|
+
exports.removeSshConfigEntry = removeSshConfigEntry;
|
|
38
|
+
exports.updateSshConfigEntry = updateSshConfigEntry;
|
|
39
|
+
exports.hasSshConfigEntry = hasSshConfigEntry;
|
|
40
|
+
exports.getSshAlias = getSshAlias;
|
|
41
|
+
const fs = __importStar(require("fs"));
|
|
42
|
+
const path = __importStar(require("path"));
|
|
43
|
+
const os = __importStar(require("os"));
|
|
44
|
+
const SSH_CONFIG_PATH = path.join(os.homedir(), '.ssh', 'config');
|
|
45
|
+
const GENBOX_MARKER_START = '# Genbox Environment:';
|
|
46
|
+
const GENBOX_MARKER_END = '# End Genbox Environment:';
|
|
47
|
+
/**
|
|
48
|
+
* Ensure ~/.ssh directory exists
|
|
49
|
+
*/
|
|
50
|
+
function ensureSshDir() {
|
|
51
|
+
const sshDir = path.join(os.homedir(), '.ssh');
|
|
52
|
+
if (!fs.existsSync(sshDir)) {
|
|
53
|
+
fs.mkdirSync(sshDir, { mode: 0o700 });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* Read current SSH config content
|
|
58
|
+
*/
|
|
59
|
+
function readSshConfig() {
|
|
60
|
+
ensureSshDir();
|
|
61
|
+
if (!fs.existsSync(SSH_CONFIG_PATH)) {
|
|
62
|
+
return '';
|
|
63
|
+
}
|
|
64
|
+
return fs.readFileSync(SSH_CONFIG_PATH, 'utf8');
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Write SSH config content
|
|
68
|
+
*/
|
|
69
|
+
function writeSshConfig(content) {
|
|
70
|
+
ensureSshDir();
|
|
71
|
+
fs.writeFileSync(SSH_CONFIG_PATH, content, { mode: 0o600 });
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Find the best available SSH key
|
|
75
|
+
*/
|
|
76
|
+
function findSshKey() {
|
|
77
|
+
const home = os.homedir();
|
|
78
|
+
const keyPaths = [
|
|
79
|
+
path.join(home, '.ssh', 'id_ed25519'),
|
|
80
|
+
path.join(home, '.ssh', 'id_rsa'),
|
|
81
|
+
];
|
|
82
|
+
for (const keyPath of keyPaths) {
|
|
83
|
+
if (fs.existsSync(keyPath)) {
|
|
84
|
+
return keyPath;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Add an SSH config entry for a Genbox
|
|
91
|
+
*/
|
|
92
|
+
function addSshConfigEntry(entry) {
|
|
93
|
+
try {
|
|
94
|
+
const config = readSshConfig();
|
|
95
|
+
const hostAlias = `genbox-${entry.name}`;
|
|
96
|
+
const marker = `${GENBOX_MARKER_START} ${entry.name}`;
|
|
97
|
+
const endMarker = `${GENBOX_MARKER_END} ${entry.name}`;
|
|
98
|
+
// Check if entry already exists
|
|
99
|
+
if (config.includes(marker)) {
|
|
100
|
+
// Update existing entry
|
|
101
|
+
removeSshConfigEntry(entry.name);
|
|
102
|
+
}
|
|
103
|
+
// Find SSH key
|
|
104
|
+
const keyPath = entry.keyPath || findSshKey();
|
|
105
|
+
// Build config block
|
|
106
|
+
const configBlock = [
|
|
107
|
+
'',
|
|
108
|
+
marker,
|
|
109
|
+
`Host ${hostAlias}`,
|
|
110
|
+
` HostName ${entry.ipAddress}`,
|
|
111
|
+
` User ${entry.user || 'dev'}`,
|
|
112
|
+
' StrictHostKeyChecking no',
|
|
113
|
+
' UserKnownHostsFile /dev/null',
|
|
114
|
+
' LogLevel ERROR',
|
|
115
|
+
];
|
|
116
|
+
if (keyPath) {
|
|
117
|
+
configBlock.push(` IdentityFile ${keyPath}`);
|
|
118
|
+
configBlock.push(' IdentitiesOnly yes');
|
|
119
|
+
}
|
|
120
|
+
configBlock.push(endMarker);
|
|
121
|
+
configBlock.push('');
|
|
122
|
+
// Append to config
|
|
123
|
+
const newConfig = config.trimEnd() + '\n' + configBlock.join('\n');
|
|
124
|
+
writeSshConfig(newConfig);
|
|
125
|
+
return true;
|
|
126
|
+
}
|
|
127
|
+
catch (error) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Remove an SSH config entry for a Genbox
|
|
133
|
+
*/
|
|
134
|
+
function removeSshConfigEntry(name) {
|
|
135
|
+
try {
|
|
136
|
+
const config = readSshConfig();
|
|
137
|
+
const marker = `${GENBOX_MARKER_START} ${name}`;
|
|
138
|
+
const endMarker = `${GENBOX_MARKER_END} ${name}`;
|
|
139
|
+
if (!config.includes(marker)) {
|
|
140
|
+
return true; // Nothing to remove
|
|
141
|
+
}
|
|
142
|
+
// Find and remove the block
|
|
143
|
+
const startIdx = config.indexOf(marker);
|
|
144
|
+
let endIdx = config.indexOf(endMarker);
|
|
145
|
+
if (endIdx === -1) {
|
|
146
|
+
// Malformed entry, try to find next Host line
|
|
147
|
+
endIdx = config.indexOf('\nHost ', startIdx + 1);
|
|
148
|
+
if (endIdx === -1) {
|
|
149
|
+
endIdx = config.length;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
endIdx += endMarker.length;
|
|
154
|
+
}
|
|
155
|
+
// Remove leading newline if present
|
|
156
|
+
let removeStart = startIdx;
|
|
157
|
+
if (removeStart > 0 && config[removeStart - 1] === '\n') {
|
|
158
|
+
removeStart--;
|
|
159
|
+
}
|
|
160
|
+
// Remove trailing newline if present
|
|
161
|
+
let removeEnd = endIdx;
|
|
162
|
+
if (removeEnd < config.length && config[removeEnd] === '\n') {
|
|
163
|
+
removeEnd++;
|
|
164
|
+
}
|
|
165
|
+
const newConfig = config.slice(0, removeStart) + config.slice(removeEnd);
|
|
166
|
+
writeSshConfig(newConfig.trim() + '\n');
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
catch (error) {
|
|
170
|
+
return false;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Update an existing SSH config entry with a new IP
|
|
175
|
+
*/
|
|
176
|
+
function updateSshConfigEntry(name, newIpAddress) {
|
|
177
|
+
const config = readSshConfig();
|
|
178
|
+
const marker = `${GENBOX_MARKER_START} ${name}`;
|
|
179
|
+
if (!config.includes(marker)) {
|
|
180
|
+
return false;
|
|
181
|
+
}
|
|
182
|
+
// Find and update the HostName line
|
|
183
|
+
const lines = config.split('\n');
|
|
184
|
+
let inBlock = false;
|
|
185
|
+
let updated = false;
|
|
186
|
+
for (let i = 0; i < lines.length; i++) {
|
|
187
|
+
if (lines[i].includes(marker)) {
|
|
188
|
+
inBlock = true;
|
|
189
|
+
}
|
|
190
|
+
if (inBlock && lines[i].trim().startsWith('HostName ')) {
|
|
191
|
+
lines[i] = ` HostName ${newIpAddress}`;
|
|
192
|
+
updated = true;
|
|
193
|
+
}
|
|
194
|
+
if (lines[i].includes(`${GENBOX_MARKER_END} ${name}`)) {
|
|
195
|
+
break;
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
if (updated) {
|
|
199
|
+
writeSshConfig(lines.join('\n'));
|
|
200
|
+
}
|
|
201
|
+
return updated;
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Check if an SSH config entry exists for a Genbox
|
|
205
|
+
*/
|
|
206
|
+
function hasSshConfigEntry(name) {
|
|
207
|
+
const config = readSshConfig();
|
|
208
|
+
const marker = `${GENBOX_MARKER_START} ${name}`;
|
|
209
|
+
return config.includes(marker);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Get the SSH alias for a Genbox
|
|
213
|
+
*/
|
|
214
|
+
function getSshAlias(name) {
|
|
215
|
+
return `genbox-${name}`;
|
|
216
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "genbox",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Genbox CLI - AI-Powered Development Environments",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"genbox": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"files": [
|
|
10
|
+
"dist/**/*"
|
|
11
|
+
],
|
|
12
|
+
"scripts": {
|
|
13
|
+
"build": "tsc",
|
|
14
|
+
"start": "node dist/index.js",
|
|
15
|
+
"dev": "ts-node src/index.ts",
|
|
16
|
+
"prepublishOnly": "npm run build",
|
|
17
|
+
"publish:patch": "./scripts/publish.sh patch",
|
|
18
|
+
"publish:minor": "./scripts/publish.sh minor",
|
|
19
|
+
"publish:major": "./scripts/publish.sh major",
|
|
20
|
+
"release": "./scripts/publish.sh patch",
|
|
21
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
22
|
+
},
|
|
23
|
+
"keywords": [
|
|
24
|
+
"genbox",
|
|
25
|
+
"ai",
|
|
26
|
+
"development",
|
|
27
|
+
"environments",
|
|
28
|
+
"cloud",
|
|
29
|
+
"cli",
|
|
30
|
+
"dev-environment",
|
|
31
|
+
"sandbox",
|
|
32
|
+
"hetzner"
|
|
33
|
+
],
|
|
34
|
+
"author": "GoodPass",
|
|
35
|
+
"license": "MIT",
|
|
36
|
+
"repository": {
|
|
37
|
+
"type": "git",
|
|
38
|
+
"url": "https://github.com/goodpass-co/genbox.git",
|
|
39
|
+
"directory": "packages/cli"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://genbox.dev",
|
|
42
|
+
"bugs": {
|
|
43
|
+
"url": "https://github.com/goodpass-co/genbox/issues"
|
|
44
|
+
},
|
|
45
|
+
"engines": {
|
|
46
|
+
"node": ">=18.0.0"
|
|
47
|
+
},
|
|
48
|
+
"devDependencies": {
|
|
49
|
+
"@types/inquirer": "^9.0.9",
|
|
50
|
+
"@types/js-yaml": "^4.0.9",
|
|
51
|
+
"@types/node": "^24.10.1",
|
|
52
|
+
"ts-node": "^10.9.2",
|
|
53
|
+
"typescript": "^5.9.3"
|
|
54
|
+
},
|
|
55
|
+
"dependencies": {
|
|
56
|
+
"chalk": "^5.6.2",
|
|
57
|
+
"commander": "^14.0.2",
|
|
58
|
+
"dotenv": "^17.2.3",
|
|
59
|
+
"inquirer": "^13.0.2",
|
|
60
|
+
"js-yaml": "^4.1.1",
|
|
61
|
+
"ora": "^9.0.0"
|
|
62
|
+
}
|
|
63
|
+
}
|