@vibedhost/cli 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/index.js +913 -0
- package/dist/utils.js +336 -0
- package/package.json +33 -0
- package/src/index.ts +1028 -0
- package/src/utils.ts +365 -0
- package/tsconfig.json +15 -0
package/dist/utils.js
ADDED
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MAX_PAYLOAD_BYTES = exports.PROJECT_CONFIG_FILE = exports.PROJECT_CONFIG_DIR = exports.GLOBAL_CONFIG_FILE = exports.GLOBAL_CONFIG_DIR = exports.API_BASE_URL = void 0;
|
|
7
|
+
exports.computeBufferSha256 = computeBufferSha256;
|
|
8
|
+
exports.readGlobalConfig = readGlobalConfig;
|
|
9
|
+
exports.writeGlobalConfig = writeGlobalConfig;
|
|
10
|
+
exports.clearGlobalConfig = clearGlobalConfig;
|
|
11
|
+
exports.readProjectConfig = readProjectConfig;
|
|
12
|
+
exports.writeProjectConfig = writeProjectConfig;
|
|
13
|
+
exports.openBrowser = openBrowser;
|
|
14
|
+
exports.scanDirectory = scanDirectory;
|
|
15
|
+
exports.createZipBuffer = createZipBuffer;
|
|
16
|
+
exports.askQuestion = askQuestion;
|
|
17
|
+
exports.inferAppName = inferAppName;
|
|
18
|
+
const fs_1 = __importDefault(require("fs"));
|
|
19
|
+
const path_1 = __importDefault(require("path"));
|
|
20
|
+
const os_1 = __importDefault(require("os"));
|
|
21
|
+
const zlib_1 = __importDefault(require("zlib"));
|
|
22
|
+
const child_process_1 = require("child_process");
|
|
23
|
+
exports.API_BASE_URL = process.env.VIBED_API_URL || "https://www.vibedhost.com";
|
|
24
|
+
exports.GLOBAL_CONFIG_DIR = path_1.default.join(os_1.default.homedir(), ".vibed");
|
|
25
|
+
exports.GLOBAL_CONFIG_FILE = path_1.default.join(exports.GLOBAL_CONFIG_DIR, "config.json");
|
|
26
|
+
exports.PROJECT_CONFIG_DIR = ".vibed";
|
|
27
|
+
exports.PROJECT_CONFIG_FILE = path_1.default.join(".vibed", "config.json");
|
|
28
|
+
exports.MAX_PAYLOAD_BYTES = 50 * 1024 * 1024; // 50MB
|
|
29
|
+
const DEFAULT_IGNORED_DIRS = [
|
|
30
|
+
"node_modules",
|
|
31
|
+
".git",
|
|
32
|
+
".next",
|
|
33
|
+
"dist",
|
|
34
|
+
"build",
|
|
35
|
+
".output",
|
|
36
|
+
".venv",
|
|
37
|
+
"venv",
|
|
38
|
+
"env",
|
|
39
|
+
"__pycache__",
|
|
40
|
+
".turbo",
|
|
41
|
+
".cache",
|
|
42
|
+
".local",
|
|
43
|
+
".vibed",
|
|
44
|
+
".aws",
|
|
45
|
+
".ssh"
|
|
46
|
+
];
|
|
47
|
+
const DEFAULT_IGNORED_EXACT_FILES = [
|
|
48
|
+
".ds_store",
|
|
49
|
+
"thumbs.db",
|
|
50
|
+
".npmrc",
|
|
51
|
+
".git-credentials",
|
|
52
|
+
"credentials.json",
|
|
53
|
+
"service-account.json",
|
|
54
|
+
"id_rsa",
|
|
55
|
+
"id_ed25519"
|
|
56
|
+
];
|
|
57
|
+
const IGNORED_EXTENSIONS_REGEX = /\.(mp4|mov|mkv|avi|iso|tar\.gz|zip|7z|rar|dmg|exe|dll|so|pem|key|pfx|p12|kdbx)$/i;
|
|
58
|
+
const crypto_1 = __importDefault(require("crypto"));
|
|
59
|
+
function computeBufferSha256(buf) {
|
|
60
|
+
return crypto_1.default.createHash("sha256").update(buf).digest("hex");
|
|
61
|
+
}
|
|
62
|
+
function readGlobalConfig() {
|
|
63
|
+
try {
|
|
64
|
+
if (!fs_1.default.existsSync(exports.GLOBAL_CONFIG_FILE))
|
|
65
|
+
return null;
|
|
66
|
+
const raw = fs_1.default.readFileSync(exports.GLOBAL_CONFIG_FILE, "utf-8");
|
|
67
|
+
return JSON.parse(raw);
|
|
68
|
+
}
|
|
69
|
+
catch {
|
|
70
|
+
return null;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
function writeGlobalConfig(config) {
|
|
74
|
+
try {
|
|
75
|
+
if (!fs_1.default.existsSync(exports.GLOBAL_CONFIG_DIR)) {
|
|
76
|
+
fs_1.default.mkdirSync(exports.GLOBAL_CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
77
|
+
}
|
|
78
|
+
fs_1.default.writeFileSync(exports.GLOBAL_CONFIG_FILE, JSON.stringify(config, null, 2), { mode: 0o600 });
|
|
79
|
+
}
|
|
80
|
+
catch (err) {
|
|
81
|
+
console.error(`Failed to write local configuration: ${err.message}`);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
function clearGlobalConfig() {
|
|
85
|
+
try {
|
|
86
|
+
if (fs_1.default.existsSync(exports.GLOBAL_CONFIG_FILE)) {
|
|
87
|
+
fs_1.default.unlinkSync(exports.GLOBAL_CONFIG_FILE);
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
catch { }
|
|
91
|
+
}
|
|
92
|
+
function readProjectConfig(targetDir = process.cwd()) {
|
|
93
|
+
try {
|
|
94
|
+
const configPath = path_1.default.join(targetDir, exports.PROJECT_CONFIG_FILE);
|
|
95
|
+
if (!fs_1.default.existsSync(configPath))
|
|
96
|
+
return null;
|
|
97
|
+
const raw = fs_1.default.readFileSync(configPath, "utf-8");
|
|
98
|
+
return JSON.parse(raw);
|
|
99
|
+
}
|
|
100
|
+
catch {
|
|
101
|
+
return null;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
function writeProjectConfig(config, targetDir = process.cwd()) {
|
|
105
|
+
try {
|
|
106
|
+
const dirPath = path_1.default.join(targetDir, exports.PROJECT_CONFIG_DIR);
|
|
107
|
+
if (!fs_1.default.existsSync(dirPath)) {
|
|
108
|
+
fs_1.default.mkdirSync(dirPath, { recursive: true });
|
|
109
|
+
}
|
|
110
|
+
const configPath = path_1.default.join(targetDir, exports.PROJECT_CONFIG_FILE);
|
|
111
|
+
fs_1.default.writeFileSync(configPath, JSON.stringify(config, null, 2));
|
|
112
|
+
}
|
|
113
|
+
catch { }
|
|
114
|
+
}
|
|
115
|
+
function openBrowser(url) {
|
|
116
|
+
try {
|
|
117
|
+
const parsed = new URL(url);
|
|
118
|
+
if (parsed.protocol !== "https:" && parsed.protocol !== "http:") {
|
|
119
|
+
return;
|
|
120
|
+
}
|
|
121
|
+
// Reject shell control characters
|
|
122
|
+
if (/[\r\n"';`$&|<>]/.test(url)) {
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const platform = process.platform;
|
|
130
|
+
let command = "";
|
|
131
|
+
if (platform === "darwin") {
|
|
132
|
+
command = `open "${url}"`;
|
|
133
|
+
}
|
|
134
|
+
else if (platform === "win32") {
|
|
135
|
+
command = `start "" "${url}"`;
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
command = `xdg-open "${url}"`;
|
|
139
|
+
}
|
|
140
|
+
(0, child_process_1.exec)(command, () => { });
|
|
141
|
+
}
|
|
142
|
+
function scanDirectory(rootDir, currentDir = rootDir, collected = [], bloatFound = {}) {
|
|
143
|
+
const items = fs_1.default.readdirSync(currentDir, { withFileTypes: true });
|
|
144
|
+
let realRootDir = rootDir;
|
|
145
|
+
try {
|
|
146
|
+
realRootDir = fs_1.default.realpathSync(rootDir);
|
|
147
|
+
}
|
|
148
|
+
catch { }
|
|
149
|
+
for (const item of items) {
|
|
150
|
+
const itemName = item.name;
|
|
151
|
+
const fullPath = path_1.default.join(currentDir, itemName);
|
|
152
|
+
const relPath = path_1.default.relative(rootDir, fullPath).replace(/\\/g, "/");
|
|
153
|
+
// Symlink Boundary Guard: Ensure symlinks do not escape project root
|
|
154
|
+
try {
|
|
155
|
+
const lstat = fs_1.default.lstatSync(fullPath);
|
|
156
|
+
if (lstat.isSymbolicLink()) {
|
|
157
|
+
const realPath = fs_1.default.realpathSync(fullPath);
|
|
158
|
+
if (!realPath.startsWith(realRootDir)) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
catch {
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
if (item.isDirectory()) {
|
|
167
|
+
if (DEFAULT_IGNORED_DIRS.includes(itemName) || itemName.startsWith(".")) {
|
|
168
|
+
// Track directory size for diagnostic reporting
|
|
169
|
+
try {
|
|
170
|
+
const dirStat = getDirectorySize(fullPath);
|
|
171
|
+
bloatFound[itemName] = (bloatFound[itemName] || 0) + dirStat;
|
|
172
|
+
}
|
|
173
|
+
catch { }
|
|
174
|
+
continue;
|
|
175
|
+
}
|
|
176
|
+
scanDirectory(rootDir, fullPath, collected, bloatFound);
|
|
177
|
+
}
|
|
178
|
+
else if (item.isFile()) {
|
|
179
|
+
const lowerName = itemName.toLowerCase();
|
|
180
|
+
if (DEFAULT_IGNORED_EXACT_FILES.includes(lowerName) ||
|
|
181
|
+
lowerName.startsWith(".env") ||
|
|
182
|
+
IGNORED_EXTENSIONS_REGEX.test(lowerName)) {
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
try {
|
|
186
|
+
const stat = fs_1.default.statSync(fullPath);
|
|
187
|
+
collected.push({
|
|
188
|
+
relativePath: relPath,
|
|
189
|
+
absolutePath: fullPath,
|
|
190
|
+
size: stat.size
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
catch { }
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return { files: collected, bloatReport: bloatFound };
|
|
197
|
+
}
|
|
198
|
+
function getDirectorySize(dirPath) {
|
|
199
|
+
let total = 0;
|
|
200
|
+
try {
|
|
201
|
+
const items = fs_1.default.readdirSync(dirPath, { withFileTypes: true });
|
|
202
|
+
for (const item of items) {
|
|
203
|
+
const p = path_1.default.join(dirPath, item.name);
|
|
204
|
+
if (item.isDirectory()) {
|
|
205
|
+
total += getDirectorySize(p);
|
|
206
|
+
}
|
|
207
|
+
else if (item.isFile()) {
|
|
208
|
+
total += fs_1.default.statSync(p).size;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch { }
|
|
213
|
+
return total;
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Pure zero-dependency in-memory PKZip generator.
|
|
217
|
+
*/
|
|
218
|
+
function createZipBuffer(files) {
|
|
219
|
+
const localHeaders = [];
|
|
220
|
+
const centralRecords = [];
|
|
221
|
+
let offset = 0;
|
|
222
|
+
for (const file of files) {
|
|
223
|
+
const fileContent = fs_1.default.readFileSync(file.absolutePath);
|
|
224
|
+
const uncompressedSize = fileContent.length;
|
|
225
|
+
const crc = computeCrc32(fileContent);
|
|
226
|
+
const compressedContent = zlib_1.default.deflateRawSync(fileContent);
|
|
227
|
+
const compressedSize = compressedContent.length;
|
|
228
|
+
const pathBuffer = Buffer.from(file.relativePath, "utf8");
|
|
229
|
+
const pathLength = pathBuffer.length;
|
|
230
|
+
// 1. Local File Header (30 Bytes)
|
|
231
|
+
const localHeader = Buffer.alloc(30 + pathLength);
|
|
232
|
+
localHeader.writeUInt32LE(0x04034b50, 0); // Signature
|
|
233
|
+
localHeader.writeUInt16LE(20, 4); // Version needed
|
|
234
|
+
localHeader.writeUInt16LE(0, 6); // Flags
|
|
235
|
+
localHeader.writeUInt16LE(8, 8); // Compression (Deflate)
|
|
236
|
+
localHeader.writeUInt16LE(0, 10); // Mod time
|
|
237
|
+
localHeader.writeUInt16LE(0, 12); // Mod date
|
|
238
|
+
localHeader.writeUInt32LE(crc, 14); // CRC-32
|
|
239
|
+
localHeader.writeUInt32LE(compressedSize, 18);
|
|
240
|
+
localHeader.writeUInt32LE(uncompressedSize, 22);
|
|
241
|
+
localHeader.writeUInt16LE(pathLength, 26);
|
|
242
|
+
localHeader.writeUInt16LE(0, 28); // Extra field length
|
|
243
|
+
pathBuffer.copy(localHeader, 30);
|
|
244
|
+
localHeaders.push(localHeader, compressedContent);
|
|
245
|
+
// 2. Central Directory Record (46 Bytes)
|
|
246
|
+
const centralRecord = Buffer.alloc(46 + pathLength);
|
|
247
|
+
centralRecord.writeUInt32LE(0x02014b50, 0); // Signature
|
|
248
|
+
centralRecord.writeUInt16LE(20, 4); // Version made by
|
|
249
|
+
centralRecord.writeUInt16LE(20, 6); // Version needed
|
|
250
|
+
centralRecord.writeUInt16LE(0, 8); // Flags
|
|
251
|
+
centralRecord.writeUInt16LE(8, 10); // Compression
|
|
252
|
+
centralRecord.writeUInt16LE(0, 12); // Mod time
|
|
253
|
+
centralRecord.writeUInt16LE(0, 14); // Mod date
|
|
254
|
+
centralRecord.writeUInt32LE(crc, 16); // CRC-32
|
|
255
|
+
centralRecord.writeUInt32LE(compressedSize, 20);
|
|
256
|
+
centralRecord.writeUInt32LE(uncompressedSize, 24);
|
|
257
|
+
centralRecord.writeUInt16LE(pathLength, 28);
|
|
258
|
+
centralRecord.writeUInt16LE(0, 30); // Extra field length
|
|
259
|
+
centralRecord.writeUInt16LE(0, 32); // Comment length
|
|
260
|
+
centralRecord.writeUInt16LE(0, 34); // Disk start
|
|
261
|
+
centralRecord.writeUInt16LE(0, 36); // Internal attr
|
|
262
|
+
centralRecord.writeUInt32LE(0, 38); // External attr
|
|
263
|
+
centralRecord.writeUInt32LE(offset, 42); // Relative offset of local header
|
|
264
|
+
pathBuffer.copy(centralRecord, 46);
|
|
265
|
+
centralRecords.push(centralRecord);
|
|
266
|
+
offset += localHeader.length + compressedContent.length;
|
|
267
|
+
}
|
|
268
|
+
const centralDirBuffer = Buffer.concat(centralRecords);
|
|
269
|
+
const centralDirSize = centralDirBuffer.length;
|
|
270
|
+
const centralDirOffset = offset;
|
|
271
|
+
const totalEntries = files.length;
|
|
272
|
+
// 3. End of Central Directory Record (22 Bytes)
|
|
273
|
+
const eocd = Buffer.alloc(22);
|
|
274
|
+
eocd.writeUInt32LE(0x06054b50, 0); // Signature
|
|
275
|
+
eocd.writeUInt16LE(0, 4); // Disk number
|
|
276
|
+
eocd.writeUInt16LE(0, 6); // Disk with CD
|
|
277
|
+
eocd.writeUInt16LE(totalEntries, 8);
|
|
278
|
+
eocd.writeUInt16LE(totalEntries, 10);
|
|
279
|
+
eocd.writeUInt32LE(centralDirSize, 12);
|
|
280
|
+
eocd.writeUInt32LE(centralDirOffset, 16);
|
|
281
|
+
eocd.writeUInt16LE(0, 20); // Comment length
|
|
282
|
+
return Buffer.concat([...localHeaders, centralDirBuffer, eocd]);
|
|
283
|
+
}
|
|
284
|
+
const CRC_TABLE = new Uint32Array(256);
|
|
285
|
+
for (let i = 0; i < 256; i++) {
|
|
286
|
+
let c = i;
|
|
287
|
+
for (let j = 0; j < 8; j++) {
|
|
288
|
+
c = c & 1 ? 0xedb88320 ^ (c >>> 1) : c >>> 1;
|
|
289
|
+
}
|
|
290
|
+
CRC_TABLE[i] = c;
|
|
291
|
+
}
|
|
292
|
+
function computeCrc32(buf) {
|
|
293
|
+
let crc = 0xffffffff;
|
|
294
|
+
for (let i = 0; i < buf.length; i++) {
|
|
295
|
+
crc = CRC_TABLE[(crc ^ buf[i]) & 0xff] ^ (crc >>> 8);
|
|
296
|
+
}
|
|
297
|
+
return (crc ^ 0xffffffff) >>> 0;
|
|
298
|
+
}
|
|
299
|
+
const readline_1 = __importDefault(require("readline"));
|
|
300
|
+
function askQuestion(query, defaultValue = "") {
|
|
301
|
+
return new Promise((resolve) => {
|
|
302
|
+
const rl = readline_1.default.createInterface({
|
|
303
|
+
input: process.stdin,
|
|
304
|
+
output: process.stdout,
|
|
305
|
+
});
|
|
306
|
+
const promptText = defaultValue ? `${query} [${defaultValue}]: ` : `${query}: `;
|
|
307
|
+
rl.question(promptText, (answer) => {
|
|
308
|
+
rl.close();
|
|
309
|
+
const trimmed = answer.trim();
|
|
310
|
+
resolve(trimmed || defaultValue);
|
|
311
|
+
});
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
function inferAppName(targetDir) {
|
|
315
|
+
try {
|
|
316
|
+
const pkgPath = path_1.default.join(targetDir, "package.json");
|
|
317
|
+
if (fs_1.default.existsSync(pkgPath)) {
|
|
318
|
+
const pkg = JSON.parse(fs_1.default.readFileSync(pkgPath, "utf-8"));
|
|
319
|
+
if (pkg.name) {
|
|
320
|
+
const clean = String(pkg.name)
|
|
321
|
+
.replace(/^@[^/]+\//, "")
|
|
322
|
+
.toLowerCase()
|
|
323
|
+
.replace(/[^a-z0-9-]/g, "-")
|
|
324
|
+
.replace(/(^-|-$)+/g, "");
|
|
325
|
+
if (clean)
|
|
326
|
+
return clean;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
catch { }
|
|
331
|
+
return path_1.default
|
|
332
|
+
.basename(targetDir)
|
|
333
|
+
.toLowerCase()
|
|
334
|
+
.replace(/[^a-z0-9-]/g, "-")
|
|
335
|
+
.replace(/(^-|-$)+/g, "");
|
|
336
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@vibedhost/cli",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "VibedHost Cloud Engine CLI — Deploy applications to dedicated cloud workspaces directly from your terminal.",
|
|
5
|
+
"main": "./dist/index.js",
|
|
6
|
+
"bin": {
|
|
7
|
+
"vibed": "./dist/index.js",
|
|
8
|
+
"vibedhost": "./dist/index.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"prepublishOnly": "npm run build"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"vibedhost",
|
|
16
|
+
"paas",
|
|
17
|
+
"deploy",
|
|
18
|
+
"cloud",
|
|
19
|
+
"vps",
|
|
20
|
+
"nextjs",
|
|
21
|
+
"docker",
|
|
22
|
+
"cli"
|
|
23
|
+
],
|
|
24
|
+
"author": "VibedHost Cloud Engine",
|
|
25
|
+
"license": "MIT",
|
|
26
|
+
"engines": {
|
|
27
|
+
"node": ">=18.0.0"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"@types/node": "^20.14.0",
|
|
31
|
+
"typescript": "^5.4.5"
|
|
32
|
+
}
|
|
33
|
+
}
|