gufi-cli 0.1.0 → 0.1.2
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/CLAUDE.md +1390 -0
- package/dist/commands/companies.d.ts +26 -0
- package/dist/commands/companies.js +513 -0
- package/dist/commands/list.d.ts +4 -0
- package/dist/commands/list.js +42 -0
- package/dist/commands/logs.d.ts +8 -0
- package/dist/commands/logs.js +153 -0
- package/dist/commands/push.d.ts +2 -2
- package/dist/commands/push.js +4 -3
- package/dist/index.d.ts +17 -7
- package/dist/index.js +70 -8
- package/package.json +15 -3
- package/src/commands/login.ts +0 -124
- package/src/commands/pull.ts +0 -113
- package/src/commands/push.ts +0 -85
- package/src/commands/watch.ts +0 -89
- package/src/index.ts +0 -66
- package/src/lib/api.ts +0 -127
- package/src/lib/config.ts +0 -93
- package/src/lib/sync.ts +0 -236
- package/tsconfig.json +0 -17
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gufi logs - Stream console logs from LivePreview in real-time
|
|
3
|
+
*
|
|
4
|
+
* The CLI connects to the Gufi WebSocket server and subscribes to
|
|
5
|
+
* dev-logs for a specific view. When the LivePreview is open in the
|
|
6
|
+
* browser, console logs are forwarded to the CLI.
|
|
7
|
+
*/
|
|
8
|
+
import chalk from "chalk";
|
|
9
|
+
import WebSocket from "ws";
|
|
10
|
+
import { isLoggedIn, getToken, getCurrentView, loadConfig } from "../lib/config.js";
|
|
11
|
+
import { loadViewMeta } from "../lib/sync.js";
|
|
12
|
+
const LOG_COLORS = {
|
|
13
|
+
log: chalk.white,
|
|
14
|
+
info: chalk.blue,
|
|
15
|
+
warn: chalk.yellow,
|
|
16
|
+
error: chalk.red,
|
|
17
|
+
debug: chalk.gray,
|
|
18
|
+
};
|
|
19
|
+
export async function logsCommand(viewDir) {
|
|
20
|
+
if (!isLoggedIn()) {
|
|
21
|
+
console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
// Determine view directory
|
|
25
|
+
const dir = viewDir || getCurrentView()?.localPath || process.cwd();
|
|
26
|
+
const meta = loadViewMeta(dir);
|
|
27
|
+
if (!meta) {
|
|
28
|
+
console.log(chalk.red("\n ✗ No es un directorio de vista Gufi válido."));
|
|
29
|
+
console.log(chalk.gray(" Usa: gufi pull <vista> primero\n"));
|
|
30
|
+
process.exit(1);
|
|
31
|
+
}
|
|
32
|
+
const config = loadConfig();
|
|
33
|
+
const apiUrl = config.apiUrl || "https://gogufi.com";
|
|
34
|
+
// Use the WebSocket server (port 4000 in dev, same domain in prod)
|
|
35
|
+
let wsUrl;
|
|
36
|
+
if (apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1")) {
|
|
37
|
+
// Dev: use local WebSocket server
|
|
38
|
+
wsUrl = "ws://localhost:4000";
|
|
39
|
+
}
|
|
40
|
+
else {
|
|
41
|
+
// Prod: use wss and same domain
|
|
42
|
+
wsUrl = apiUrl.replace(/^https?/, "wss").replace(/:3000$/, ":4000");
|
|
43
|
+
if (!wsUrl.includes(":4000")) {
|
|
44
|
+
wsUrl = wsUrl.replace(/\/?$/, ":4000");
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
const token = getToken();
|
|
48
|
+
console.log(chalk.magenta("\n 🟣 Gufi Logs\n"));
|
|
49
|
+
console.log(chalk.gray(` Vista: ${meta.viewName} (ID: ${meta.viewId})`));
|
|
50
|
+
console.log(chalk.gray(` Conectando a WebSocket...\n`));
|
|
51
|
+
let ws;
|
|
52
|
+
let reconnectAttempts = 0;
|
|
53
|
+
const maxReconnectAttempts = 5;
|
|
54
|
+
const connect = () => {
|
|
55
|
+
// Connect with token in URL (same as frontend)
|
|
56
|
+
ws = new WebSocket(`${wsUrl}?token=${token}`);
|
|
57
|
+
ws.on("open", () => {
|
|
58
|
+
reconnectAttempts = 0;
|
|
59
|
+
console.log(chalk.green(" ✓ Conectado al WebSocket\n"));
|
|
60
|
+
// Subscribe to dev logs for this view
|
|
61
|
+
ws.send(JSON.stringify({
|
|
62
|
+
type: "dev-logs-subscribe",
|
|
63
|
+
viewId: meta.viewId,
|
|
64
|
+
}));
|
|
65
|
+
});
|
|
66
|
+
ws.on("message", (data) => {
|
|
67
|
+
try {
|
|
68
|
+
const msg = JSON.parse(data.toString());
|
|
69
|
+
// Handle subscription confirmation
|
|
70
|
+
if (msg.type === "dev-logs-subscribed") {
|
|
71
|
+
console.log(chalk.green(` ✓ Suscrito a logs de vista ${msg.viewId}\n`));
|
|
72
|
+
console.log(chalk.gray(" 💡 Abre el Developer Center y activa LivePreview"));
|
|
73
|
+
console.log(chalk.gray(" Los logs de la consola aparecerán aquí en tiempo real\n"));
|
|
74
|
+
console.log(chalk.gray(" Ctrl+C para salir\n"));
|
|
75
|
+
console.log(chalk.gray(" ─".repeat(40) + "\n"));
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Handle log messages
|
|
79
|
+
if (msg.type && msg.args) {
|
|
80
|
+
formatAndPrintLog(msg);
|
|
81
|
+
}
|
|
82
|
+
else if (msg.error) {
|
|
83
|
+
console.log(chalk.red(` Error: ${msg.error}`));
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch (e) {
|
|
87
|
+
// Raw message, just print it
|
|
88
|
+
console.log(chalk.gray(` ${data.toString()}`));
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
ws.on("close", () => {
|
|
92
|
+
if (reconnectAttempts < maxReconnectAttempts) {
|
|
93
|
+
reconnectAttempts++;
|
|
94
|
+
console.log(chalk.yellow(` ⚠ Conexión cerrada. Reintentando (${reconnectAttempts}/${maxReconnectAttempts})...`));
|
|
95
|
+
setTimeout(connect, 2000);
|
|
96
|
+
}
|
|
97
|
+
else {
|
|
98
|
+
console.log(chalk.red("\n ✗ No se pudo reconectar. Verifica que el servidor esté corriendo.\n"));
|
|
99
|
+
process.exit(1);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
ws.on("error", (err) => {
|
|
103
|
+
if (err.code === "ECONNREFUSED") {
|
|
104
|
+
console.log(chalk.red(`\n ✗ No se puede conectar a ${wsUrl}`));
|
|
105
|
+
console.log(chalk.gray(" Verifica que el servidor WebSocket esté corriendo (puerto 4000)\n"));
|
|
106
|
+
}
|
|
107
|
+
else {
|
|
108
|
+
console.log(chalk.red(` Error: ${err.message}`));
|
|
109
|
+
}
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
connect();
|
|
113
|
+
// Handle graceful shutdown
|
|
114
|
+
process.on("SIGINT", () => {
|
|
115
|
+
console.log(chalk.gray("\n\n Desconectando...\n"));
|
|
116
|
+
if (ws && ws.readyState === WebSocket.OPEN) {
|
|
117
|
+
ws.send(JSON.stringify({ type: "dev-logs-unsubscribe" }));
|
|
118
|
+
}
|
|
119
|
+
ws?.close();
|
|
120
|
+
process.exit(0);
|
|
121
|
+
});
|
|
122
|
+
// Keep process alive
|
|
123
|
+
await new Promise(() => { });
|
|
124
|
+
}
|
|
125
|
+
function formatAndPrintLog(msg) {
|
|
126
|
+
const colorFn = LOG_COLORS[msg.type] || chalk.white;
|
|
127
|
+
const prefix = getPrefix(msg.type);
|
|
128
|
+
const timestamp = new Date(msg.timestamp).toLocaleTimeString();
|
|
129
|
+
// Format args
|
|
130
|
+
const formatted = msg.args.map(arg => {
|
|
131
|
+
if (typeof arg === "object") {
|
|
132
|
+
try {
|
|
133
|
+
return JSON.stringify(arg, null, 2);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
return String(arg);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
return String(arg);
|
|
140
|
+
}).join(" ");
|
|
141
|
+
console.log(chalk.gray(` ${timestamp}`) +
|
|
142
|
+
` ${prefix} ` +
|
|
143
|
+
colorFn(formatted));
|
|
144
|
+
}
|
|
145
|
+
function getPrefix(type) {
|
|
146
|
+
switch (type) {
|
|
147
|
+
case "error": return chalk.red("✗");
|
|
148
|
+
case "warn": return chalk.yellow("⚠");
|
|
149
|
+
case "info": return chalk.blue("ℹ");
|
|
150
|
+
case "debug": return chalk.gray("🔍");
|
|
151
|
+
default: return chalk.white("›");
|
|
152
|
+
}
|
|
153
|
+
}
|
package/dist/commands/push.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* gufi push - Upload local changes to Gufi
|
|
3
3
|
*/
|
|
4
|
-
export declare function pushCommand(viewDir?: string): Promise<void>;
|
|
5
|
-
export declare function statusCommand(viewDir?: string): Promise<void>;
|
|
4
|
+
export declare function pushCommand(viewDir?: string | object): Promise<void>;
|
|
5
|
+
export declare function statusCommand(viewDir?: string | object): Promise<void>;
|
package/dist/commands/push.js
CHANGED
|
@@ -11,8 +11,8 @@ export async function pushCommand(viewDir) {
|
|
|
11
11
|
process.exit(1);
|
|
12
12
|
}
|
|
13
13
|
console.log(chalk.magenta("\n 🟣 Gufi Push\n"));
|
|
14
|
-
// Determine view directory
|
|
15
|
-
const dir = viewDir || getCurrentView()?.localPath || process.cwd();
|
|
14
|
+
// Determine view directory (Commander passes Command object when no args)
|
|
15
|
+
const dir = (typeof viewDir === 'string' ? viewDir : null) || getCurrentView()?.localPath || process.cwd();
|
|
16
16
|
// Check if it's a valid Gufi view directory
|
|
17
17
|
const meta = loadViewMeta(dir);
|
|
18
18
|
if (!meta) {
|
|
@@ -46,7 +46,8 @@ export async function pushCommand(viewDir) {
|
|
|
46
46
|
}
|
|
47
47
|
}
|
|
48
48
|
export async function statusCommand(viewDir) {
|
|
49
|
-
|
|
49
|
+
// Commander passes Command object when no args
|
|
50
|
+
const dir = (typeof viewDir === 'string' ? viewDir : null) || getCurrentView()?.localPath || process.cwd();
|
|
50
51
|
const meta = loadViewMeta(dir);
|
|
51
52
|
if (!meta) {
|
|
52
53
|
console.log(chalk.red("\n ✗ No es un directorio de vista Gufi válido.\n"));
|
package/dist/index.d.ts
CHANGED
|
@@ -3,12 +3,22 @@
|
|
|
3
3
|
* Gufi Dev CLI - Main Entry Point
|
|
4
4
|
*
|
|
5
5
|
* Commands:
|
|
6
|
-
* gufi login
|
|
7
|
-
* gufi logout
|
|
8
|
-
* gufi whoami
|
|
9
|
-
* gufi pull [view]
|
|
10
|
-
* gufi push
|
|
11
|
-
* gufi watch
|
|
12
|
-
* gufi status
|
|
6
|
+
* gufi login Login to Gufi
|
|
7
|
+
* gufi logout Logout from Gufi
|
|
8
|
+
* gufi whoami Show current user
|
|
9
|
+
* gufi pull [view] Download view files
|
|
10
|
+
* gufi push Upload local changes
|
|
11
|
+
* gufi watch Auto-sync file changes
|
|
12
|
+
* gufi status Show sync status
|
|
13
|
+
* gufi logs [dir] Stream console logs from LivePreview
|
|
14
|
+
*
|
|
15
|
+
* SDK Commands:
|
|
16
|
+
* gufi companies List your companies
|
|
17
|
+
* gufi modules <id> List modules of a company
|
|
18
|
+
* gufi module <id> View/edit module JSON (--edit, --file)
|
|
19
|
+
* gufi module:update Update module from JSON file
|
|
20
|
+
* gufi company:create Create a new company
|
|
21
|
+
* gufi automations List automation scripts
|
|
22
|
+
* gufi automation <name> View/edit automation code (--edit, --file)
|
|
13
23
|
*/
|
|
14
24
|
export {};
|
package/dist/index.js
CHANGED
|
@@ -3,19 +3,32 @@
|
|
|
3
3
|
* Gufi Dev CLI - Main Entry Point
|
|
4
4
|
*
|
|
5
5
|
* Commands:
|
|
6
|
-
* gufi login
|
|
7
|
-
* gufi logout
|
|
8
|
-
* gufi whoami
|
|
9
|
-
* gufi pull [view]
|
|
10
|
-
* gufi push
|
|
11
|
-
* gufi watch
|
|
12
|
-
* gufi status
|
|
6
|
+
* gufi login Login to Gufi
|
|
7
|
+
* gufi logout Logout from Gufi
|
|
8
|
+
* gufi whoami Show current user
|
|
9
|
+
* gufi pull [view] Download view files
|
|
10
|
+
* gufi push Upload local changes
|
|
11
|
+
* gufi watch Auto-sync file changes
|
|
12
|
+
* gufi status Show sync status
|
|
13
|
+
* gufi logs [dir] Stream console logs from LivePreview
|
|
14
|
+
*
|
|
15
|
+
* SDK Commands:
|
|
16
|
+
* gufi companies List your companies
|
|
17
|
+
* gufi modules <id> List modules of a company
|
|
18
|
+
* gufi module <id> View/edit module JSON (--edit, --file)
|
|
19
|
+
* gufi module:update Update module from JSON file
|
|
20
|
+
* gufi company:create Create a new company
|
|
21
|
+
* gufi automations List automation scripts
|
|
22
|
+
* gufi automation <name> View/edit automation code (--edit, --file)
|
|
13
23
|
*/
|
|
14
24
|
import { Command } from "commander";
|
|
15
25
|
import { loginCommand, logoutCommand, whoamiCommand } from "./commands/login.js";
|
|
16
26
|
import { pullCommand } from "./commands/pull.js";
|
|
17
27
|
import { pushCommand, statusCommand } from "./commands/push.js";
|
|
18
28
|
import { watchCommand } from "./commands/watch.js";
|
|
29
|
+
import { listCommand } from "./commands/list.js";
|
|
30
|
+
import { logsCommand } from "./commands/logs.js";
|
|
31
|
+
import { companiesCommand, modulesCommand, moduleCommand, moduleUpdateCommand, companyCreateCommand, automationsCommand, automationCommand, } from "./commands/companies.js";
|
|
19
32
|
const program = new Command();
|
|
20
33
|
program
|
|
21
34
|
.name("gufi")
|
|
@@ -45,11 +58,60 @@ program
|
|
|
45
58
|
.description("Upload local changes to Gufi")
|
|
46
59
|
.action(pushCommand);
|
|
47
60
|
program
|
|
48
|
-
.command("watch")
|
|
61
|
+
.command("watch [dir]")
|
|
49
62
|
.description("Watch for file changes and auto-sync")
|
|
50
63
|
.action(watchCommand);
|
|
51
64
|
program
|
|
52
65
|
.command("status")
|
|
53
66
|
.description("Show sync status")
|
|
54
67
|
.action(statusCommand);
|
|
68
|
+
program
|
|
69
|
+
.command("list")
|
|
70
|
+
.alias("ls")
|
|
71
|
+
.description("List your packages and views")
|
|
72
|
+
.action(listCommand);
|
|
73
|
+
program
|
|
74
|
+
.command("logs [dir]")
|
|
75
|
+
.description("Stream console logs from LivePreview in real-time")
|
|
76
|
+
.action(logsCommand);
|
|
77
|
+
// ════════════════════════════════════════════════════════════════════
|
|
78
|
+
// SDK Commands - Companies, Modules, Automations
|
|
79
|
+
// ════════════════════════════════════════════════════════════════════
|
|
80
|
+
program
|
|
81
|
+
.command("companies")
|
|
82
|
+
.description("List your companies")
|
|
83
|
+
.action(companiesCommand);
|
|
84
|
+
program
|
|
85
|
+
.command("modules <company_id>")
|
|
86
|
+
.description("List modules of a company")
|
|
87
|
+
.action(modulesCommand);
|
|
88
|
+
program
|
|
89
|
+
.command("module <module_id>")
|
|
90
|
+
.description("View or edit a module JSON")
|
|
91
|
+
.option("-e, --edit", "Open in editor to edit")
|
|
92
|
+
.option("-c, --company <id>", "Company ID (if not default)")
|
|
93
|
+
.option("-f, --file <path>", "Save JSON to file")
|
|
94
|
+
.action(moduleCommand);
|
|
95
|
+
program
|
|
96
|
+
.command("module:update <module_id> <json_file>")
|
|
97
|
+
.description("Update module from a JSON file")
|
|
98
|
+
.option("-c, --company <id>", "Company ID (if not default)")
|
|
99
|
+
.option("--dry-run", "Validate without saving")
|
|
100
|
+
.action(moduleUpdateCommand);
|
|
101
|
+
program
|
|
102
|
+
.command("company:create <name>")
|
|
103
|
+
.description("Create a new company")
|
|
104
|
+
.action(companyCreateCommand);
|
|
105
|
+
program
|
|
106
|
+
.command("automations")
|
|
107
|
+
.description("List all automation scripts")
|
|
108
|
+
.option("-c, --company <id>", "Company ID (if not default)")
|
|
109
|
+
.action(automationsCommand);
|
|
110
|
+
program
|
|
111
|
+
.command("automation <name>")
|
|
112
|
+
.description("View or edit an automation by name")
|
|
113
|
+
.option("-e, --edit", "Open in editor to edit")
|
|
114
|
+
.option("-c, --company <id>", "Company ID (if not default)")
|
|
115
|
+
.option("-f, --file <path>", "Save code to file")
|
|
116
|
+
.action(automationCommand);
|
|
55
117
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gufi-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "CLI for developing Gufi Marketplace views locally with Claude Code",
|
|
5
5
|
"bin": {
|
|
6
6
|
"gufi": "./bin/gufi.js"
|
|
7
7
|
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin",
|
|
10
|
+
"dist",
|
|
11
|
+
"CLAUDE.md"
|
|
12
|
+
],
|
|
8
13
|
"repository": {
|
|
9
14
|
"type": "git",
|
|
10
15
|
"url": "https://github.com/juanbp23/gogufi"
|
|
@@ -17,11 +22,13 @@
|
|
|
17
22
|
"start": "node bin/gufi.js"
|
|
18
23
|
},
|
|
19
24
|
"dependencies": {
|
|
25
|
+
"@types/ws": "^8.18.1",
|
|
20
26
|
"chalk": "^5.3.0",
|
|
21
27
|
"chokidar": "^3.5.3",
|
|
22
28
|
"commander": "^12.0.0",
|
|
23
29
|
"node-fetch": "^3.3.2",
|
|
24
|
-
"ora": "^8.0.1"
|
|
30
|
+
"ora": "^8.0.1",
|
|
31
|
+
"ws": "^8.18.3"
|
|
25
32
|
},
|
|
26
33
|
"devDependencies": {
|
|
27
34
|
"@types/node": "^20.10.0",
|
|
@@ -30,7 +37,12 @@
|
|
|
30
37
|
"engines": {
|
|
31
38
|
"node": ">=18"
|
|
32
39
|
},
|
|
33
|
-
"keywords": [
|
|
40
|
+
"keywords": [
|
|
41
|
+
"gufi",
|
|
42
|
+
"developer",
|
|
43
|
+
"cli",
|
|
44
|
+
"marketplace"
|
|
45
|
+
],
|
|
34
46
|
"author": "Gufi",
|
|
35
47
|
"license": "MIT"
|
|
36
48
|
}
|
package/src/commands/login.ts
DELETED
|
@@ -1,124 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* gufi login - Authenticate with Gufi
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import readline from "readline";
|
|
6
|
-
import chalk from "chalk";
|
|
7
|
-
import ora from "ora";
|
|
8
|
-
import { login, validateToken } from "../lib/api.js";
|
|
9
|
-
import { setToken, isLoggedIn, clearToken, loadConfig, setApiUrl } from "../lib/config.js";
|
|
10
|
-
|
|
11
|
-
function prompt(question: string, hidden = false): Promise<string> {
|
|
12
|
-
const rl = readline.createInterface({
|
|
13
|
-
input: process.stdin,
|
|
14
|
-
output: process.stdout,
|
|
15
|
-
});
|
|
16
|
-
|
|
17
|
-
return new Promise((resolve) => {
|
|
18
|
-
if (hidden) {
|
|
19
|
-
process.stdout.write(question);
|
|
20
|
-
let input = "";
|
|
21
|
-
process.stdin.setRawMode?.(true);
|
|
22
|
-
process.stdin.resume();
|
|
23
|
-
process.stdin.on("data", (char) => {
|
|
24
|
-
const c = char.toString();
|
|
25
|
-
if (c === "\n" || c === "\r") {
|
|
26
|
-
process.stdin.setRawMode?.(false);
|
|
27
|
-
process.stdout.write("\n");
|
|
28
|
-
rl.close();
|
|
29
|
-
resolve(input);
|
|
30
|
-
} else if (c === "\u0003") {
|
|
31
|
-
process.exit();
|
|
32
|
-
} else if (c === "\u007F") {
|
|
33
|
-
if (input.length > 0) {
|
|
34
|
-
input = input.slice(0, -1);
|
|
35
|
-
process.stdout.write("\b \b");
|
|
36
|
-
}
|
|
37
|
-
} else {
|
|
38
|
-
input += c;
|
|
39
|
-
process.stdout.write("*");
|
|
40
|
-
}
|
|
41
|
-
});
|
|
42
|
-
} else {
|
|
43
|
-
rl.question(question, (answer) => {
|
|
44
|
-
rl.close();
|
|
45
|
-
resolve(answer);
|
|
46
|
-
});
|
|
47
|
-
}
|
|
48
|
-
});
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export async function loginCommand(options: { api?: string }): Promise<void> {
|
|
52
|
-
console.log(chalk.magenta("\n 🟣 Gufi Developer CLI\n"));
|
|
53
|
-
|
|
54
|
-
// Set custom API URL if provided
|
|
55
|
-
if (options.api) {
|
|
56
|
-
setApiUrl(options.api);
|
|
57
|
-
console.log(chalk.gray(` API: ${options.api}\n`));
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// Check if already logged in
|
|
61
|
-
if (isLoggedIn()) {
|
|
62
|
-
const config = loadConfig();
|
|
63
|
-
const spinner = ora("Verificando sesión...").start();
|
|
64
|
-
const valid = await validateToken();
|
|
65
|
-
|
|
66
|
-
if (valid) {
|
|
67
|
-
spinner.succeed(chalk.green(`Ya estás logueado como ${config.email}`));
|
|
68
|
-
const relogin = await prompt("\n¿Quieres iniciar sesión con otra cuenta? (s/N): ");
|
|
69
|
-
if (relogin.toLowerCase() !== "s") {
|
|
70
|
-
return;
|
|
71
|
-
}
|
|
72
|
-
clearToken();
|
|
73
|
-
} else {
|
|
74
|
-
spinner.warn("Sesión expirada");
|
|
75
|
-
clearToken();
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Get credentials
|
|
80
|
-
const email = await prompt(" Email: ");
|
|
81
|
-
const password = await prompt(" Password: ", true);
|
|
82
|
-
|
|
83
|
-
if (!email || !password) {
|
|
84
|
-
console.log(chalk.red("\n ✗ Email y password son requeridos\n"));
|
|
85
|
-
process.exit(1);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
const spinner = ora("Iniciando sesión...").start();
|
|
89
|
-
|
|
90
|
-
try {
|
|
91
|
-
const { token } = await login(email, password);
|
|
92
|
-
setToken(token, email);
|
|
93
|
-
spinner.succeed(chalk.green(`Sesión iniciada como ${email}`));
|
|
94
|
-
console.log(chalk.gray("\n Ahora puedes usar: gufi pull <vista>\n"));
|
|
95
|
-
} catch (error: any) {
|
|
96
|
-
spinner.fail(chalk.red(error.message || "Error al iniciar sesión"));
|
|
97
|
-
process.exit(1);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
export async function logoutCommand(): Promise<void> {
|
|
102
|
-
clearToken();
|
|
103
|
-
console.log(chalk.green("\n ✓ Sesión cerrada\n"));
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export async function whoamiCommand(): Promise<void> {
|
|
107
|
-
const config = loadConfig();
|
|
108
|
-
|
|
109
|
-
if (!isLoggedIn()) {
|
|
110
|
-
console.log(chalk.yellow("\n No estás logueado. Usa: gufi login\n"));
|
|
111
|
-
return;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
const spinner = ora("Verificando...").start();
|
|
115
|
-
const valid = await validateToken();
|
|
116
|
-
|
|
117
|
-
if (valid) {
|
|
118
|
-
spinner.succeed(chalk.green(`Logueado como ${config.email}`));
|
|
119
|
-
console.log(chalk.gray(` API: ${config.apiUrl}\n`));
|
|
120
|
-
} else {
|
|
121
|
-
spinner.fail(chalk.red("Sesión expirada"));
|
|
122
|
-
console.log(chalk.gray(" Usa: gufi login\n"));
|
|
123
|
-
}
|
|
124
|
-
}
|
package/src/commands/pull.ts
DELETED
|
@@ -1,113 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* gufi pull - Download view files from Gufi
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from "chalk";
|
|
6
|
-
import ora from "ora";
|
|
7
|
-
import { listPackages, listViews, getView } from "../lib/api.js";
|
|
8
|
-
import { pullView } from "../lib/sync.js";
|
|
9
|
-
import { isLoggedIn } from "../lib/config.js";
|
|
10
|
-
|
|
11
|
-
export async function pullCommand(viewIdentifier?: string): Promise<void> {
|
|
12
|
-
if (!isLoggedIn()) {
|
|
13
|
-
console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
console.log(chalk.magenta("\n 🟣 Gufi Pull\n"));
|
|
18
|
-
|
|
19
|
-
let viewId: number;
|
|
20
|
-
let viewName: string;
|
|
21
|
-
let packageId: number;
|
|
22
|
-
|
|
23
|
-
// If view ID provided directly
|
|
24
|
-
if (viewIdentifier && /^\d+$/.test(viewIdentifier)) {
|
|
25
|
-
viewId = parseInt(viewIdentifier);
|
|
26
|
-
const spinner = ora("Obteniendo vista...").start();
|
|
27
|
-
try {
|
|
28
|
-
const view = await getView(viewId);
|
|
29
|
-
viewName = view.name;
|
|
30
|
-
packageId = view.package_id;
|
|
31
|
-
spinner.succeed(`Vista: ${viewName}`);
|
|
32
|
-
} catch (error: any) {
|
|
33
|
-
spinner.fail(chalk.red(`No se encontró la vista ${viewId}`));
|
|
34
|
-
process.exit(1);
|
|
35
|
-
}
|
|
36
|
-
} else {
|
|
37
|
-
// Interactive selection
|
|
38
|
-
const spinner = ora("Cargando packages...").start();
|
|
39
|
-
|
|
40
|
-
try {
|
|
41
|
-
const packages = await listPackages();
|
|
42
|
-
spinner.stop();
|
|
43
|
-
|
|
44
|
-
if (packages.length === 0) {
|
|
45
|
-
console.log(chalk.yellow(" No tienes packages. Crea uno en Developer Center.\n"));
|
|
46
|
-
process.exit(0);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
console.log(chalk.gray(" Tus packages:\n"));
|
|
50
|
-
packages.forEach((pkg, i) => {
|
|
51
|
-
console.log(` ${chalk.cyan(i + 1)}. ${pkg.name}`);
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// For now, use first package (can improve with interactive selection later)
|
|
55
|
-
const pkg = packages[0];
|
|
56
|
-
packageId = pkg.pk_id;
|
|
57
|
-
console.log(chalk.gray(`\n Usando package: ${pkg.name}\n`));
|
|
58
|
-
|
|
59
|
-
const viewsSpinner = ora("Cargando vistas...").start();
|
|
60
|
-
const views = await listViews(packageId);
|
|
61
|
-
viewsSpinner.stop();
|
|
62
|
-
|
|
63
|
-
if (views.length === 0) {
|
|
64
|
-
console.log(chalk.yellow(" No hay vistas en este package.\n"));
|
|
65
|
-
process.exit(0);
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
console.log(chalk.gray(" Vistas disponibles:\n"));
|
|
69
|
-
views.forEach((view, i) => {
|
|
70
|
-
console.log(` ${chalk.cyan(i + 1)}. ${view.name} ${chalk.gray(`(${view.view_type})`)}`);
|
|
71
|
-
});
|
|
72
|
-
|
|
73
|
-
// If viewIdentifier matches a name
|
|
74
|
-
let selectedView = viewIdentifier
|
|
75
|
-
? views.find(v => v.name.toLowerCase().includes(viewIdentifier.toLowerCase()))
|
|
76
|
-
: views[0];
|
|
77
|
-
|
|
78
|
-
if (!selectedView) {
|
|
79
|
-
console.log(chalk.yellow(`\n No se encontró vista "${viewIdentifier}"\n`));
|
|
80
|
-
console.log(chalk.gray(" Uso: gufi pull <nombre-vista> o gufi pull <view-id>\n"));
|
|
81
|
-
process.exit(1);
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
viewId = selectedView.pk_id;
|
|
85
|
-
viewName = selectedView.name;
|
|
86
|
-
|
|
87
|
-
console.log(chalk.gray(`\n Descargando: ${viewName}\n`));
|
|
88
|
-
|
|
89
|
-
} catch (error: any) {
|
|
90
|
-
spinner.fail(chalk.red(error.message));
|
|
91
|
-
process.exit(1);
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// Pull the view
|
|
96
|
-
const pullSpinner = ora("Descargando archivos...").start();
|
|
97
|
-
|
|
98
|
-
try {
|
|
99
|
-
const result = await pullView(viewId, viewName, packageId);
|
|
100
|
-
pullSpinner.succeed(chalk.green(`${result.fileCount} archivos descargados`));
|
|
101
|
-
|
|
102
|
-
console.log(chalk.gray(`\n 📁 ${result.dir}\n`));
|
|
103
|
-
console.log(chalk.gray(" Comandos útiles:"));
|
|
104
|
-
console.log(chalk.cyan(" cd " + result.dir));
|
|
105
|
-
console.log(chalk.cyan(" gufi watch") + chalk.gray(" # Auto-sync cambios"));
|
|
106
|
-
console.log(chalk.cyan(" claude") + chalk.gray(" # Abrir Claude Code"));
|
|
107
|
-
console.log();
|
|
108
|
-
|
|
109
|
-
} catch (error: any) {
|
|
110
|
-
pullSpinner.fail(chalk.red(error.message));
|
|
111
|
-
process.exit(1);
|
|
112
|
-
}
|
|
113
|
-
}
|
package/src/commands/push.ts
DELETED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* gufi push - Upload local changes to Gufi
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from "chalk";
|
|
6
|
-
import ora from "ora";
|
|
7
|
-
import path from "path";
|
|
8
|
-
import { pushView, getChangedFiles, loadViewMeta } from "../lib/sync.js";
|
|
9
|
-
import { isLoggedIn, getCurrentView } from "../lib/config.js";
|
|
10
|
-
|
|
11
|
-
export async function pushCommand(viewDir?: string): Promise<void> {
|
|
12
|
-
if (!isLoggedIn()) {
|
|
13
|
-
console.log(chalk.red("\n ✗ No estás logueado. Usa: gufi login\n"));
|
|
14
|
-
process.exit(1);
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
console.log(chalk.magenta("\n 🟣 Gufi Push\n"));
|
|
18
|
-
|
|
19
|
-
// Determine view directory
|
|
20
|
-
const dir = viewDir || getCurrentView()?.localPath || process.cwd();
|
|
21
|
-
|
|
22
|
-
// Check if it's a valid Gufi view directory
|
|
23
|
-
const meta = loadViewMeta(dir);
|
|
24
|
-
if (!meta) {
|
|
25
|
-
console.log(chalk.red(" ✗ No es un directorio de vista Gufi válido."));
|
|
26
|
-
console.log(chalk.gray(" Usa: gufi pull <vista> primero\n"));
|
|
27
|
-
process.exit(1);
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
console.log(chalk.gray(` Vista: ${meta.viewName}`));
|
|
31
|
-
console.log(chalk.gray(` Directorio: ${dir}\n`));
|
|
32
|
-
|
|
33
|
-
// Check for changes
|
|
34
|
-
const changedFiles = getChangedFiles(dir);
|
|
35
|
-
|
|
36
|
-
if (changedFiles.length === 0) {
|
|
37
|
-
console.log(chalk.green(" ✓ No hay cambios para subir\n"));
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
console.log(chalk.gray(" Archivos modificados:"));
|
|
42
|
-
changedFiles.forEach((file) => {
|
|
43
|
-
console.log(chalk.yellow(` • ${file}`));
|
|
44
|
-
});
|
|
45
|
-
console.log();
|
|
46
|
-
|
|
47
|
-
// Push changes
|
|
48
|
-
const spinner = ora("Subiendo cambios...").start();
|
|
49
|
-
|
|
50
|
-
try {
|
|
51
|
-
const result = await pushView(dir);
|
|
52
|
-
spinner.succeed(chalk.green(`${result.pushed} archivo(s) subido(s)`));
|
|
53
|
-
console.log(chalk.gray("\n El preview en Gufi se actualizará automáticamente.\n"));
|
|
54
|
-
} catch (error: any) {
|
|
55
|
-
spinner.fail(chalk.red(error.message));
|
|
56
|
-
process.exit(1);
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
export async function statusCommand(viewDir?: string): Promise<void> {
|
|
61
|
-
const dir = viewDir || getCurrentView()?.localPath || process.cwd();
|
|
62
|
-
|
|
63
|
-
const meta = loadViewMeta(dir);
|
|
64
|
-
if (!meta) {
|
|
65
|
-
console.log(chalk.red("\n ✗ No es un directorio de vista Gufi válido.\n"));
|
|
66
|
-
process.exit(1);
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
console.log(chalk.magenta("\n 🟣 Gufi Status\n"));
|
|
70
|
-
console.log(chalk.gray(` Vista: ${meta.viewName}`));
|
|
71
|
-
console.log(chalk.gray(` View ID: ${meta.viewId}`));
|
|
72
|
-
console.log(chalk.gray(` Último sync: ${meta.lastSync}\n`));
|
|
73
|
-
|
|
74
|
-
const changedFiles = getChangedFiles(dir);
|
|
75
|
-
|
|
76
|
-
if (changedFiles.length === 0) {
|
|
77
|
-
console.log(chalk.green(" ✓ Todo sincronizado\n"));
|
|
78
|
-
} else {
|
|
79
|
-
console.log(chalk.yellow(" Archivos modificados:"));
|
|
80
|
-
changedFiles.forEach((file) => {
|
|
81
|
-
console.log(chalk.yellow(` • ${file}`));
|
|
82
|
-
});
|
|
83
|
-
console.log(chalk.gray("\n Usa: gufi push para subir cambios\n"));
|
|
84
|
-
}
|
|
85
|
-
}
|