kubiy-paas-cli 0.1.0 → 0.1.1

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/fix-link.js CHANGED
@@ -4,47 +4,69 @@ const fs = require('fs');
4
4
  const path = require('path');
5
5
  const os = require('os');
6
6
 
7
- // Ruta donde npm instala los comandos globales
8
- const npmBinPath = path.join(os.homedir(), 'AppData', 'Roaming', 'npm');
9
- const kubiyCmdPath = path.join(npmBinPath, 'kubiy.cmd');
10
- const kubiyPs1Path = path.join(npmBinPath, 'kubiy.ps1');
11
-
12
- // Corregir kubiy.cmd
13
- if (fs.existsSync(kubiyCmdPath)) {
14
- let content = fs.readFileSync(kubiyCmdPath, 'utf8');
15
-
16
- // Verificar si necesita corrección (no tiene 'node' explícito)
17
- if (!content.includes('node "%dp0%')) {
18
- // Reemplazar la línea que ejecuta el .js directamente
19
- content = content.replace(
20
- /"%dp0%\\node_modules\\kubiy-paas-cli\\index\.js"/g,
21
- 'node "%dp0%\\node_modules\\kubiy-paas-cli\\index.js"'
22
- );
23
-
24
- fs.writeFileSync(kubiyCmdPath, content, 'utf8');
25
- console.log('✅ Archivo kubiy.cmd corregido');
7
+ const isWin = process.platform === 'win32';
8
+
9
+ if (isWin) {
10
+ // ==========================
11
+ // LADO WINDOWS (lo que ya tenías)
12
+ // ==========================
13
+
14
+ // Ruta donde npm instala los comandos globales (típico en Windows)
15
+ const npmBinPath = path.join(os.homedir(), 'AppData', 'Roaming', 'npm');
16
+ const kubiyCmdPath = path.join(npmBinPath, 'kubiy.cmd');
17
+ const kubiyPs1Path = path.join(npmBinPath, 'kubiy.ps1');
18
+
19
+ // Corregir kubiy.cmd
20
+ if (fs.existsSync(kubiyCmdPath)) {
21
+ let content = fs.readFileSync(kubiyCmdPath, 'utf8');
22
+
23
+ // Verificar si necesita corrección (no tiene 'node' explícito)
24
+ if (!content.includes('node "%dp0%')) {
25
+ // Reemplazar la línea que ejecuta el .js directamente
26
+ content = content.replace(
27
+ /"%dp0%\\node_modules\\kubiy-paas-cli\\index\.js"/g,
28
+ 'node "%dp0%\\node_modules\\kubiy-paas-cli\\index.js"'
29
+ );
30
+
31
+ fs.writeFileSync(kubiyCmdPath, content, 'utf8');
32
+ console.log('✅ Archivo kubiy.cmd corregido');
33
+ }
26
34
  }
27
- }
28
35
 
29
- // Corregir kubiy.ps1
30
- if (fs.existsSync(kubiyPs1Path)) {
31
- let content = fs.readFileSync(kubiyPs1Path, 'utf8');
32
-
33
- // Verificar si necesita corrección (no tiene 'node' explícito)
34
- if (!content.includes('node "$basedir')) {
35
- // Reemplazar las líneas que ejecutan el .js directamente
36
- content = content.replace(
37
- /& "`$basedir\/node_modules\/kubiy-paas-cli\/index\.js"/g,
38
- '& node "$basedir/node_modules/kubiy-paas-cli/index.js"'
39
- );
40
- // También corregir la versión sin backticks
41
- content = content.replace(
42
- /& "\$basedir\/node_modules\/kubiy-paas-cli\/index\.js"/g,
43
- '& node "$basedir/node_modules/kubiy-paas-cli/index.js"'
44
- );
45
-
46
- fs.writeFileSync(kubiyPs1Path, content, 'utf8');
47
- console.log('✅ Archivo kubiy.ps1 corregido');
36
+ // Corregir kubiy.ps1
37
+ if (fs.existsSync(kubiyPs1Path)) {
38
+ let content = fs.readFileSync(kubiyPs1Path, 'utf8');
39
+
40
+ // Verificar si necesita corrección (no tiene 'node' explícito)
41
+ if (!content.includes('node "$basedir')) {
42
+ // Reemplazar las líneas que ejecutan el .js directamente
43
+ content = content.replace(
44
+ /& "`\$basedir\/node_modules\/kubiy-paas-cli\/index\.js"/g,
45
+ '& node "$basedir/node_modules/kubiy-paas-cli/index.js"'
46
+ );
47
+ // También corregir la versión sin backticks (por si acaso)
48
+ content = content.replace(
49
+ /& "\$basedir\/node_modules\/kubiy-paas-cli\/index\.js"/g,
50
+ '& node "$basedir/node_modules/kubiy-paas-cli/index.js"'
51
+ );
52
+
53
+ fs.writeFileSync(kubiyPs1Path, content, 'utf8');
54
+ console.log('✅ Archivo kubiy.ps1 corregido');
55
+ }
48
56
  }
49
- }
50
57
 
58
+ } else {
59
+ // ==========================
60
+ // LADO UNIX / MAC
61
+ // ==========================
62
+ try {
63
+ const indexPath = path.join(__dirname, 'index.js');
64
+
65
+ if (fs.existsSync(indexPath)) {
66
+ fs.chmodSync(indexPath, 0o755);
67
+ console.log('✅ Unix: permisos ejecutables aplicados a index.js (chmod +x)');
68
+ }
69
+ } catch (e) {
70
+ console.log('⚠ No se pudo aplicar chmod a index.js en Unix:', e.message);
71
+ }
72
+ }
package/index.js CHANGED
@@ -1,77 +1,55 @@
1
- #!/usr/bin/env node
1
+ #!/usr/bin/env node
2
2
 
3
- // CLI minimalista
4
- const fs = require("fs");
5
- const os = require("os");
6
3
  const path = require("path");
4
+ const { logInfo, logError } = require("./src/logger");
7
5
 
8
- const args = process.argv.slice(2); // [command, ...rest]
6
+ const args = process.argv.slice(2);
7
+ const command = args[0];
9
8
 
10
- const CONFIG_DIR = path.join(os.homedir(), ".kubiy");
11
- const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
12
-
13
- function ensureConfigDir() {
14
- if (!fs.existsSync(CONFIG_DIR)) {
15
- fs.mkdirSync(CONFIG_DIR, { recursive: true });
16
- }
17
- }
18
-
19
- function readConfig() {
20
- if (!fs.existsSync(CONFIG_FILE)) return {};
21
- const raw = fs.readFileSync(CONFIG_FILE, "utf8");
22
- return JSON.parse(raw);
23
- }
24
-
25
- function writeConfig(obj) {
26
- ensureConfigDir();
27
- fs.writeFileSync(CONFIG_FILE, JSON.stringify(obj, null, 2), "utf8");
28
- }
29
-
30
- async function cmdLogin() {
31
- // Versión simple: aceptamos token por argumento
32
- // kubiy login <token>
33
- const token = args[1];
34
- if (!token) {
35
- console.error("Uso: kubiy login <cliToken>");
9
+ async function main() {
10
+ try {
11
+ switch (command) {
12
+ case "login":
13
+ await require("./src/commands/login").run(args.slice(1));
14
+ break;
15
+ case "whoami":
16
+ await require("./src/commands/whoami").run(args.slice(1));
17
+ break;
18
+ case "service:get":
19
+ await require("./src/commands/service-get").run(args.slice(1));
20
+ break;
21
+ case "git:connect":
22
+ await require("./src/commands/git-connect").run(args.slice(1));
23
+ break;
24
+ case "deploy":
25
+ await require("./src/commands/deploy").run();
26
+ break;
27
+ case "version":
28
+ case "--version":
29
+ case "-v":
30
+ await require("./src/commands/version").run(args.slice(1));
31
+ break;
32
+ default:
33
+ printHelp();
34
+ process.exit(0);
35
+ }
36
+ } catch (err) {
37
+ logError("Error en CLI:", err.message || err);
36
38
  process.exit(1);
37
39
  }
38
-
39
- const cfg = readConfig();
40
- cfg.cliToken = token.trim();
41
- writeConfig(cfg);
42
-
43
- console.log("✅ CLI token guardado en ~/.kubiy/config.json");
44
- }
45
-
46
- async function cmdWhoami() {
47
- const cfg = readConfig();
48
- if (!cfg.cliToken) {
49
- console.log("No hay token configurado. Ejecuta: kubiy login <cliToken>");
50
- return;
51
- }
52
- console.log("CLI configurado. Token (truncado):", cfg.cliToken.slice(0, 6) + "****");
53
40
  }
54
41
 
55
- async function main() {
56
- const command = args[0];
57
-
58
- switch (command) {
59
- case "login":
60
- await cmdLogin();
61
- break;
62
- case "whoami":
63
- await cmdWhoami();
64
- break;
65
- default:
66
- console.log("Kubiy PaaS CLI");
67
- console.log("Uso:");
68
- console.log(" kubiy login <cliToken>");
69
- console.log(" kubiy whoami");
70
- process.exit(0);
71
- }
42
+ function printHelp() {
43
+ logInfo("Kubiy PaaS CLI");
44
+ console.log("");
45
+ console.log("Uso:");
46
+ console.log(" kubiy login <cliToken>");
47
+ console.log(" kubiy whoami");
48
+ console.log(" kubiy service:get <serviceId>");
49
+ console.log(" kubiy git:connect <serviceId>");
50
+ console.log(" kubiy deploy");
51
+ console.log(" kubiy version");
52
+ console.log("");
72
53
  }
73
54
 
74
- main().catch((err) => {
75
- console.error("Error en CLI:", err);
76
- process.exit(1);
77
- });
55
+ main();
package/package.json CHANGED
@@ -1,15 +1,13 @@
1
1
  {
2
2
  "name": "kubiy-paas-cli",
3
- "version": "0.1.0",
4
- "description": "CLI para desplegar apps en la plataforma PaaS de Kubiy",
3
+ "version": "0.1.1",
4
+ "description": "CLI para la plataforma PaaS de Kubiy",
5
5
  "main": "index.js",
6
6
  "bin": {
7
7
  "kubiy": "index.js"
8
8
  },
9
- "preferGlobal": true,
10
9
  "scripts": {
11
- "start": "node index.js",
12
- "postinstall": "node fix-link.js"
10
+ "start": "node index.js"
13
11
  },
14
12
  "keywords": [
15
13
  "kubiy",
@@ -17,9 +15,10 @@
17
15
  "cli",
18
16
  "deploy"
19
17
  ],
20
- "author": "Elfego Guerra",
18
+ "author": "Kubiy",
21
19
  "license": "MIT",
22
20
  "engines": {
23
21
  "node": ">=12.0.0"
24
22
  }
25
23
  }
24
+
package/src/api.js ADDED
@@ -0,0 +1,79 @@
1
+ const https = require("https");
2
+ const { getCliToken } = require("./config");
3
+ const { logError } = require("./logger");
4
+
5
+ // Ajusta esta URL a tu API real
6
+ const API_BASE = process.env.KUBIY_API_BASE || "https://i5c812hwh2.execute-api.us-east-1.amazonaws.com";
7
+
8
+ function request(method, path, body, opts = {}) {
9
+ return new Promise((resolve, reject) => {
10
+ const token = opts.token || getCliToken();
11
+ const url = new URL(path, API_BASE);
12
+
13
+ const headers = {
14
+ "Accept": "application/json"
15
+ };
16
+
17
+ let payload = null;
18
+ if (body) {
19
+ payload = JSON.stringify(body);
20
+ headers["Content-Type"] = "application/json";
21
+ headers["Content-Length"] = Buffer.byteLength(payload);
22
+ }
23
+
24
+ if (token) {
25
+ headers["Authorization"] = `Bearer ${token}`;
26
+ }
27
+
28
+ const options = {
29
+ method,
30
+ headers
31
+ };
32
+
33
+ const req = https.request(url, options, (res) => {
34
+ let data = "";
35
+
36
+ res.on("data", (chunk) => (data += chunk));
37
+ res.on("end", () => {
38
+ if (!data) {
39
+ return resolve(null);
40
+ }
41
+
42
+ if (res.statusCode >= 200 && res.statusCode < 300) {
43
+ try {
44
+ resolve(JSON.parse(data));
45
+ } catch (e) {
46
+ resolve(data);
47
+ }
48
+ } else {
49
+ reject(
50
+ new Error(
51
+ `API ${res.statusCode}: ${data || res.statusMessage || "Error"}`
52
+ )
53
+ );
54
+ }
55
+ });
56
+ });
57
+
58
+ req.on("error", reject);
59
+
60
+ if (payload) {
61
+ req.write(payload);
62
+ }
63
+ req.end();
64
+ });
65
+ }
66
+
67
+ function apiGet(path, opts) {
68
+ return request("GET", path, null, opts);
69
+ }
70
+
71
+ function apiPost(path, body, opts) {
72
+ return request("POST", path, body, opts);
73
+ }
74
+
75
+ module.exports = {
76
+ apiGet,
77
+ apiPost,
78
+ API_BASE
79
+ };
@@ -0,0 +1,167 @@
1
+ // src/commands/deploy.js
2
+ const { execSync } = require("child_process");
3
+ const fs = require("fs");
4
+ const { logInfo, logSuccess, logError } = require("../logger");
5
+
6
+ function runGit(command, options = {}) {
7
+ const full = `git ${command}`;
8
+ try {
9
+ return execSync(full, {
10
+ stdio: ["pipe", "pipe", "inherit"],
11
+ ...options,
12
+ }).toString().trim();
13
+ } catch (err) {
14
+ throw new Error(`Error al ejecutar: ${full}\n${err.message}`);
15
+ }
16
+ }
17
+
18
+ function isGitRepo() {
19
+ try {
20
+ runGit("rev-parse --is-inside-work-tree");
21
+ return true;
22
+ } catch (_) {
23
+ return false;
24
+ }
25
+ }
26
+
27
+ function ensureGitRepo() {
28
+ if (isGitRepo()) {
29
+ return;
30
+ }
31
+
32
+ logInfo("Este directorio no es un repositorio git. Inicializando...");
33
+ runGit("init");
34
+
35
+ // Intentar crear master como rama inicial
36
+ try {
37
+ runGit("checkout -b master");
38
+ } catch (_) {
39
+ // Si falla, probablemente ya haya HEAD en detached; no pasa nada por ahora.
40
+ }
41
+
42
+ logSuccess("Repositorio git inicializado y rama master creada (si fue posible).");
43
+ }
44
+
45
+ function getCurrentBranch() {
46
+ try {
47
+ return runGit("rev-parse --abbrev-ref HEAD");
48
+ } catch (_) {
49
+ return null;
50
+ }
51
+ }
52
+
53
+ function branchExists(branchName) {
54
+ try {
55
+ const out = runGit(`branch --list ${branchName}`);
56
+ return out !== "";
57
+ } catch (_) {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Normaliza para que la rama activa sea master.
64
+ *
65
+ * Casos:
66
+ * - Si ya estamos en master → OK.
67
+ * - Si existe master → hacemos checkout master.
68
+ * - Si NO existe master → renombramos la rama actual a master.
69
+ */
70
+ function ensureOnMaster() {
71
+ const current = getCurrentBranch();
72
+
73
+ if (!current) {
74
+ throw new Error("No se pudo determinar la rama actual.");
75
+ }
76
+
77
+ if (current === "master") {
78
+ logInfo("Ya estás en la rama master.");
79
+ return;
80
+ }
81
+
82
+ logInfo(`Rama actual: ${current}`);
83
+
84
+ if (branchExists("master")) {
85
+ logInfo("Encontrada rama master. Cambiando a master...");
86
+ runGit("checkout master", { stdio: "inherit" });
87
+ logSuccess("Ahora estás en master.");
88
+ } else {
89
+ logInfo(`No existe rama master. Renombrando rama actual '${current}' a 'master'...`);
90
+ runGit(`branch -m master`, { stdio: "inherit" });
91
+ logSuccess("Rama actual renombrada a master.");
92
+ }
93
+ }
94
+
95
+ function ensureKubiyRemote() {
96
+ try {
97
+ const url = runGit("remote get-url kubiy");
98
+ logInfo(`Remote 'kubiy' encontrado: ${url}`);
99
+ } catch (_) {
100
+ throw new Error(
101
+ "No se encontró el remote 'kubiy'.\n" +
102
+ "Asegúrate de haber ejecutado antes: kubiy git:connect <serviceId>"
103
+ );
104
+ }
105
+ }
106
+
107
+ function hasChangesToCommit() {
108
+ const status = runGit("status --porcelain");
109
+ return status !== "";
110
+ }
111
+
112
+ function createAutoCommit() {
113
+ const msg = `kubiy deploy - ${new Date().toISOString()}`;
114
+ logInfo("Creando commit automático...");
115
+
116
+ runGit("add .", { stdio: "inherit" });
117
+ try {
118
+ runGit(`commit -m "${msg}"`, { stdio: "inherit" });
119
+ logSuccess(`Commit creado: ${msg}`);
120
+ } catch (err) {
121
+ // Si no hay nada que commitear, git commit truena. Lo detectamos.
122
+ if (String(err.message).includes("nothing to commit")) {
123
+ logInfo("No hay cambios nuevos para commitear.");
124
+ } else {
125
+ throw err;
126
+ }
127
+ }
128
+ }
129
+
130
+ function pushToKubiy() {
131
+ logInfo("Haciendo push a 'kubiy' (rama master)...");
132
+ runGit("push kubiy master", { stdio: "inherit" });
133
+ logSuccess("Push a kubiy master completado.");
134
+ }
135
+
136
+ async function run() {
137
+ try {
138
+ // 1) Asegurar repo git
139
+ ensureGitRepo();
140
+
141
+ // 2) Asegurar que estamos en master
142
+ ensureOnMaster();
143
+
144
+ // 3) Asegurar remote kubiy
145
+ ensureKubiyRemote();
146
+
147
+ // 4) Crear commit automático si hay cambios
148
+ if (hasChangesToCommit()) {
149
+ createAutoCommit();
150
+ } else {
151
+ logInfo("No hay cambios locales. Solo se intentará hacer push.");
152
+ }
153
+
154
+ // 5) Push a kubiy master
155
+ pushToKubiy();
156
+
157
+ logSuccess("🚀 Deploy completado. Revisa tu servicio Kubiy.");
158
+ } catch (err) {
159
+ logError("Error en kubiy deploy:");
160
+ logError(err.message);
161
+ process.exit(1);
162
+ }
163
+ }
164
+
165
+ module.exports = {
166
+ run,
167
+ };
@@ -0,0 +1,113 @@
1
+ // src/commands/git-connect.js
2
+ const { getCliToken } = require("../config");
3
+ const { apiGet, apiPost } = require("../api");
4
+ const { ensureSshKey, SSH_PRIV } = require("../ssh");
5
+ const { ensureSshHostConfig } = require("../ssh-config");
6
+ const { addRemote } = require("../git");
7
+ const { logInfo, logSuccess, logError } = require("../logger");
8
+
9
+ const { execSync } = require("child_process");
10
+ const fs = require("fs");
11
+
12
+ function ensureGitRepo() {
13
+ if (fs.existsSync(".git")) return;
14
+
15
+ logInfo("Este directorio no es un repositorio git. Inicializando...");
16
+ execSync("git init", { stdio: "inherit" });
17
+ try {
18
+ execSync("git checkout -b main", { stdio: "inherit" });
19
+ } catch (_) {
20
+ // Si ya existe main/master, no pasa nada
21
+ }
22
+ }
23
+
24
+ function parseRepoUrl(gitRepoUrl) {
25
+ // Esperamos algo como ssh://deploy@1.2.3.4/home/deploy/app.git
26
+ try {
27
+ const url = new URL(gitRepoUrl);
28
+ return {
29
+ host: url.hostname,
30
+ path: url.pathname // /home/deploy/app.git
31
+ };
32
+ } catch (e) {
33
+ // fallback muy básico
34
+ // ssh://deploy@1.2.3.4/home/deploy/app.git
35
+ if (!gitRepoUrl.startsWith("ssh://")) {
36
+ throw new Error(`gitRepoUrl inválido: ${gitRepoUrl}`);
37
+ }
38
+ const withoutScheme = gitRepoUrl.slice("ssh://".length);
39
+ const [userHost, pathPart] = withoutScheme.split("/", 2);
40
+ const host = userHost.split("@").pop();
41
+ return {
42
+ host,
43
+ path: "/" + pathPart
44
+ };
45
+ }
46
+ }
47
+
48
+ async function run(argv) {
49
+ const serviceId = argv[0];
50
+
51
+ if (!serviceId) {
52
+ logError("Uso: kubiy git:connect <serviceId>");
53
+ process.exit(1);
54
+ }
55
+
56
+ const token = getCliToken();
57
+ if (!token) {
58
+ logError("No hay CLI token. Ejecuta: kubiy login <cliToken>");
59
+ process.exit(1);
60
+ }
61
+
62
+ // 1) Asegurar llave SSH local (~/.kubiy/id_ed25519)
63
+ const pubKey = ensureSshKey();
64
+
65
+ // 2) Registrar llave en tu API
66
+ logInfo("Registrando llave SSH en Kubiy...");
67
+ await apiPost("/cli/ssh-keys", { publicKey: pubKey });
68
+ logSuccess("Llave SSH registrada.");
69
+
70
+ // 3) Sincronizar llaves con la EC2 del servicio
71
+ logInfo("Sincronizando llaves SSH con la instancia...");
72
+ try {
73
+ const syncResp = await apiPost(`/cli/services/${serviceId}/sync-ssh`, {});
74
+ logSuccess(
75
+ `Llaves sincronizadas. keysApplied=${syncResp.keysApplied}, ssmStatus=${syncResp.ssmStatus}`
76
+ );
77
+ } catch (e) {
78
+ logError(`No se pudo sincronizar llaves con la instancia: ${e.message}`);
79
+ // seguimos, porque igual podrías tener la llave ya metida de antes
80
+ }
81
+
82
+ // 4) Obtener info del servicio (para gitRepoUrl)
83
+ logInfo("Obteniendo información del servicio...");
84
+ const service = await apiGet(`/services/${serviceId}`);
85
+
86
+ const gitRepoUrl = service.gitRepoUrl;
87
+ if (!gitRepoUrl) {
88
+ logError("El servicio no tiene gitRepoUrl todavía. Asegúrate que esté en RUNNING.");
89
+ process.exit(1);
90
+ }
91
+
92
+ const { host: ec2Host, path: repoPath } = parseRepoUrl(gitRepoUrl);
93
+
94
+ // 5) Configurar ~/.ssh/config con un host lógico único para este servicio
95
+ const hostAlias = `kubiy-${serviceId}`.slice(0, 60); // por si se va muy largo
96
+
97
+ ensureSshHostConfig(hostAlias, ec2Host, SSH_PRIV);
98
+
99
+ // 6) Configurar remote con el alias, no con la IP directa
100
+ ensureGitRepo();
101
+ logInfo("Configurando remote 'kubiy'...");
102
+ const remoteUrl = `ssh://${hostAlias}${repoPath}`;
103
+ addRemote("kubiy", remoteUrl);
104
+
105
+ logSuccess("Remote 'kubiy' configurado.");
106
+
107
+ logSuccess("Listo. Puedes hacer ahora:");
108
+ console.log(" git add .");
109
+ console.log(" git commit -m \"tu mensaje\"");
110
+ console.log(" git push kubiy main # o master");
111
+ }
112
+
113
+ module.exports = { run };
@@ -0,0 +1,16 @@
1
+ const { setCliToken } = require("../config");
2
+ const { logSuccess, logError } = require("../logger");
3
+
4
+ async function run(args) {
5
+ const token = args[0];
6
+
7
+ if (!token) {
8
+ logError("Uso: kubiy login <cliToken>");
9
+ process.exit(1);
10
+ }
11
+
12
+ setCliToken(token.trim());
13
+ logSuccess("CLI token guardado en ~/.kubiy/config.json");
14
+ }
15
+
16
+ module.exports = { run };
@@ -0,0 +1,24 @@
1
+ const { apiGet } = require("../api");
2
+ const { logError, logSuccess } = require("../logger");
3
+ const { getCliToken } = require("../config");
4
+
5
+ async function run(args) {
6
+ const serviceId = args[0];
7
+
8
+ if (!serviceId) {
9
+ logError("Uso: kubiy service:get <serviceId>");
10
+ process.exit(1);
11
+ }
12
+
13
+ const token = getCliToken();
14
+ if (!token) {
15
+ logError("No hay CLI token. Ejecuta: kubiy login <cliToken>");
16
+ process.exit(1);
17
+ }
18
+
19
+ const service = await apiGet(`/services/${serviceId}`);
20
+ logSuccess("Servicio obtenido:");
21
+ console.log(JSON.stringify(service, null, 2));
22
+ }
23
+
24
+ module.exports = { run };
@@ -0,0 +1,17 @@
1
+ const { logInfo } = require("../logger");
2
+ const { API_BASE, apiGet } = require("../api");
3
+ const pkg = require("../../package.json");
4
+
5
+ async function run() {
6
+ logInfo(`Kubiy CLI version: ${pkg.version}`);
7
+ logInfo(`API base: ${API_BASE}`);
8
+
9
+ try {
10
+ const status = await apiGet("/status"); // ajusta si no tienes este endpoint
11
+ console.log("API status:", status);
12
+ } catch (e) {
13
+ console.log("No se pudo consultar /status de la API:", e.message);
14
+ }
15
+ }
16
+
17
+ module.exports = { run };
@@ -0,0 +1,15 @@
1
+ const { getCliToken } = require("../config");
2
+ const { logInfo, logWarn } = require("../logger");
3
+
4
+ async function run() {
5
+ const token = getCliToken();
6
+ if (!token) {
7
+ logWarn("No hay CLI token configurado. Ejecuta: kubiy login <cliToken>");
8
+ return;
9
+ }
10
+
11
+ logInfo("CLI configurado.");
12
+ console.log("Token (truncado):", token.slice(0, 6) + "****");
13
+ }
14
+
15
+ module.exports = { run };
package/src/config.js ADDED
@@ -0,0 +1,47 @@
1
+ const fs = require("fs");
2
+ const os = require("os");
3
+ const path = require("path");
4
+
5
+ const CONFIG_DIR = path.join(os.homedir(), ".kubiy");
6
+ const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
7
+
8
+ function ensureConfigDir() {
9
+ if (!fs.existsSync(CONFIG_DIR)) {
10
+ fs.mkdirSync(CONFIG_DIR, { recursive: true });
11
+ }
12
+ }
13
+
14
+ function readConfig() {
15
+ try {
16
+ if (!fs.existsSync(CONFIG_FILE)) return {};
17
+ const raw = fs.readFileSync(CONFIG_FILE, "utf8");
18
+ return JSON.parse(raw);
19
+ } catch (e) {
20
+ return {};
21
+ }
22
+ }
23
+
24
+ function writeConfig(obj) {
25
+ ensureConfigDir();
26
+ fs.writeFileSync(CONFIG_FILE, JSON.stringify(obj, null, 2), "utf8");
27
+ }
28
+
29
+ function getCliToken() {
30
+ const cfg = readConfig();
31
+ return cfg.cliToken;
32
+ }
33
+
34
+ function setCliToken(token) {
35
+ const cfg = readConfig();
36
+ cfg.cliToken = token;
37
+ writeConfig(cfg);
38
+ }
39
+
40
+ module.exports = {
41
+ CONFIG_DIR,
42
+ CONFIG_FILE,
43
+ readConfig,
44
+ writeConfig,
45
+ getCliToken,
46
+ setCliToken
47
+ };
package/src/git.js ADDED
@@ -0,0 +1,24 @@
1
+ const { execSync } = require("child_process");
2
+ const { logInfo, logSuccess, logWarn } = require("./logger");
3
+
4
+ function run(cmd) {
5
+ execSync(cmd, { stdio: "inherit" });
6
+ }
7
+
8
+ function addRemote(name, url) {
9
+ try {
10
+ // quitar si existe
11
+ execSync(`git remote remove ${name}`, { stdio: "ignore" });
12
+ } catch (_) {
13
+ // no pasa nada si no existe
14
+ }
15
+
16
+ logInfo(`Agregando remote '${name}' -> ${url}`);
17
+ execSync(`git remote add ${name} "${url}"`, { stdio: "inherit" });
18
+ logSuccess(`Remote '${name}' configurado.`);
19
+ }
20
+
21
+ module.exports = {
22
+ run,
23
+ addRemote
24
+ };
package/src/logger.js ADDED
@@ -0,0 +1,31 @@
1
+ const COLORS = {
2
+ reset: "\x1b[0m",
3
+ green: "\x1b[32m",
4
+ yellow: "\x1b[33m",
5
+ red: "\x1b[31m",
6
+ cyan: "\x1b[36m"
7
+ };
8
+
9
+ function logInfo(...args) {
10
+ console.log(COLORS.cyan + "ℹ" + COLORS.reset, ...args);
11
+ }
12
+
13
+ function logSuccess(...args) {
14
+ console.log(COLORS.green + "✔" + COLORS.reset, ...args);
15
+ }
16
+
17
+ function logWarn(...args) {
18
+ console.log(COLORS.yellow + "⚠" + COLORS.reset, ...args);
19
+ }
20
+
21
+ function logError(...args) {
22
+ console.error(COLORS.red + "✖" + COLORS.reset, ...args);
23
+ }
24
+
25
+ module.exports = {
26
+ logInfo,
27
+ logSuccess,
28
+ logWarn,
29
+ logError
30
+ };
31
+
@@ -0,0 +1,57 @@
1
+ // src/ssh-config.js
2
+ const fs = require("fs");
3
+ const os = require("os");
4
+ const path = require("path");
5
+ const { logInfo, logSuccess } = require("./logger");
6
+
7
+ const SSH_DIR = path.join(os.homedir(), ".ssh");
8
+ const SSH_CONFIG = path.join(SSH_DIR, "config");
9
+
10
+ /**
11
+ * Asegura que exista una entrada Host en ~/.ssh/config
12
+ * para usar la llave de Kubiy con ese host.
13
+ *
14
+ * hostAlias: nombre lógico (p. ej. "kubiy-srv-<serviceId>")
15
+ * hostName: IP pública o hostname real de la EC2
16
+ * identityFile: ruta absoluta al id_ed25519 de Kubiy
17
+ */
18
+ function ensureSshHostConfig(hostAlias, hostName, identityFile) {
19
+ if (!fs.existsSync(SSH_DIR)) {
20
+ fs.mkdirSync(SSH_DIR, { recursive: true });
21
+ }
22
+
23
+ let existing = "";
24
+ if (fs.existsSync(SSH_CONFIG)) {
25
+ existing = fs.readFileSync(SSH_CONFIG, "utf8");
26
+ // Si ya hay un bloque para este Host, no duplicamos
27
+ const already = new RegExp(`^Host\\s+${hostAlias}\\b`, "m").test(existing);
28
+ if (already) {
29
+ logInfo(`SSH config ya tiene entrada para Host '${hostAlias}'`);
30
+ return;
31
+ }
32
+ }
33
+
34
+ // Convertir backslashes a slashes para que OpenSSH en Windows no se maree
35
+ const identityPath = identityFile.replace(/\\/g, "/");
36
+
37
+ const block = [
38
+ "",
39
+ `Host ${hostAlias}`,
40
+ ` HostName ${hostName}`,
41
+ ` User deploy`,
42
+ ` IdentityFile ${identityPath}`,
43
+ ` IdentitiesOnly yes`,
44
+ ""
45
+ ].join("\n");
46
+
47
+ const newContent = existing ? `${existing.trimEnd()}\n${block}` : block.trimStart();
48
+
49
+ fs.writeFileSync(SSH_CONFIG, newContent, "utf8");
50
+ logSuccess(`Configurado ~/.ssh/config para Host '${hostAlias}'`);
51
+ }
52
+
53
+ module.exports = {
54
+ ensureSshHostConfig,
55
+ SSH_DIR,
56
+ SSH_CONFIG
57
+ };
package/src/ssh.js ADDED
@@ -0,0 +1,40 @@
1
+ const fs = require("fs");
2
+ const os = require("os");
3
+ const path = require("path");
4
+ const { execSync } = require("child_process");
5
+ const { logInfo, logSuccess, logWarn } = require("./logger");
6
+
7
+ const SSH_DIR = path.join(os.homedir(), ".kubiy");
8
+ const SSH_PRIV = path.join(SSH_DIR, "id_ed25519");
9
+ const SSH_PUB = SSH_PRIV + ".pub";
10
+
11
+ function ensureSshKey() {
12
+ if (!fs.existsSync(SSH_DIR)) {
13
+ fs.mkdirSync(SSH_DIR, { recursive: true });
14
+ }
15
+
16
+ if (!fs.existsSync(SSH_PRIV) || !fs.existsSync(SSH_PUB)) {
17
+ logInfo("No existe llave SSH de Kubiy, generando una nueva...");
18
+
19
+ try {
20
+ execSync(
21
+ `ssh-keygen -t ed25519 -f "${SSH_PRIV}" -N "" -C "kubiy-cli"`,
22
+ { stdio: "inherit" }
23
+ );
24
+ logSuccess("Llave SSH generada en ~/.kubiy/");
25
+ } catch (e) {
26
+ logWarn("Error generando llave SSH:", e.message);
27
+ throw e;
28
+ }
29
+ }
30
+
31
+ const pub = fs.readFileSync(SSH_PUB, "utf8").trim();
32
+ return pub;
33
+ }
34
+
35
+ module.exports = {
36
+ SSH_DIR,
37
+ SSH_PRIV,
38
+ SSH_PUB,
39
+ ensureSshKey
40
+ };