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
package/src/commands/watch.ts
DELETED
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* gufi watch - Auto-sync file changes to Gufi
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import chalk from "chalk";
|
|
6
|
-
import chokidar from "chokidar";
|
|
7
|
-
import path from "path";
|
|
8
|
-
import { pushSingleFile, loadViewMeta, getViewDir } from "../lib/sync.js";
|
|
9
|
-
import { isLoggedIn, getCurrentView } from "../lib/config.js";
|
|
10
|
-
|
|
11
|
-
export async function watchCommand(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
|
-
// Determine view directory
|
|
18
|
-
const dir = viewDir || getCurrentView()?.localPath || process.cwd();
|
|
19
|
-
|
|
20
|
-
const meta = loadViewMeta(dir);
|
|
21
|
-
if (!meta) {
|
|
22
|
-
console.log(chalk.red("\n ✗ No es un directorio de vista Gufi válido."));
|
|
23
|
-
console.log(chalk.gray(" Usa: gufi pull <vista> primero\n"));
|
|
24
|
-
process.exit(1);
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
console.log(chalk.magenta("\n 🟣 Gufi Watch\n"));
|
|
28
|
-
console.log(chalk.gray(` Vista: ${meta.viewName}`));
|
|
29
|
-
console.log(chalk.gray(` Directorio: ${dir}\n`));
|
|
30
|
-
console.log(chalk.green(" ✓ Observando cambios...\n"));
|
|
31
|
-
console.log(chalk.gray(" Ctrl+C para salir\n"));
|
|
32
|
-
|
|
33
|
-
// Setup file watcher
|
|
34
|
-
const watcher = chokidar.watch(dir, {
|
|
35
|
-
ignored: [
|
|
36
|
-
/(^|[\/\\])\../, // Hidden files
|
|
37
|
-
"**/node_modules/**",
|
|
38
|
-
"**/.gufi-view.json",
|
|
39
|
-
],
|
|
40
|
-
persistent: true,
|
|
41
|
-
ignoreInitial: true,
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
// Debounce map to prevent rapid duplicate syncs
|
|
45
|
-
const debounceMap = new Map<string, NodeJS.Timeout>();
|
|
46
|
-
|
|
47
|
-
const syncFile = async (filePath: string) => {
|
|
48
|
-
const relativePath = path.relative(dir, filePath);
|
|
49
|
-
|
|
50
|
-
// Clear existing debounce
|
|
51
|
-
const existing = debounceMap.get(relativePath);
|
|
52
|
-
if (existing) {
|
|
53
|
-
clearTimeout(existing);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Set new debounce
|
|
57
|
-
debounceMap.set(
|
|
58
|
-
relativePath,
|
|
59
|
-
setTimeout(async () => {
|
|
60
|
-
debounceMap.delete(relativePath);
|
|
61
|
-
|
|
62
|
-
try {
|
|
63
|
-
process.stdout.write(chalk.yellow(` ↑ ${relativePath}...`));
|
|
64
|
-
await pushSingleFile(dir, relativePath);
|
|
65
|
-
process.stdout.write(chalk.green(" ✓\n"));
|
|
66
|
-
} catch (error: any) {
|
|
67
|
-
process.stdout.write(chalk.red(` ✗ ${error.message}\n`));
|
|
68
|
-
}
|
|
69
|
-
}, 300)
|
|
70
|
-
);
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
watcher
|
|
74
|
-
.on("change", syncFile)
|
|
75
|
-
.on("add", syncFile)
|
|
76
|
-
.on("error", (error) => {
|
|
77
|
-
console.log(chalk.red(` Error: ${error.message}`));
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// Handle graceful shutdown
|
|
81
|
-
process.on("SIGINT", () => {
|
|
82
|
-
console.log(chalk.gray("\n\n Deteniendo watch...\n"));
|
|
83
|
-
watcher.close();
|
|
84
|
-
process.exit(0);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
// Keep process alive
|
|
88
|
-
await new Promise(() => {});
|
|
89
|
-
}
|
package/src/index.ts
DELETED
|
@@ -1,66 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* Gufi Dev CLI - Main Entry Point
|
|
4
|
-
*
|
|
5
|
-
* Commands:
|
|
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
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { Command } from "commander";
|
|
16
|
-
import { loginCommand, logoutCommand, whoamiCommand } from "./commands/login.js";
|
|
17
|
-
import { pullCommand } from "./commands/pull.js";
|
|
18
|
-
import { pushCommand, statusCommand } from "./commands/push.js";
|
|
19
|
-
import { watchCommand } from "./commands/watch.js";
|
|
20
|
-
|
|
21
|
-
const program = new Command();
|
|
22
|
-
|
|
23
|
-
program
|
|
24
|
-
.name("gufi")
|
|
25
|
-
.description("Gufi Developer CLI - Develop Gufi views locally")
|
|
26
|
-
.version("0.1.0");
|
|
27
|
-
|
|
28
|
-
// Auth commands
|
|
29
|
-
program
|
|
30
|
-
.command("login")
|
|
31
|
-
.description("Login to Gufi")
|
|
32
|
-
.option("--api <url>", "Custom API URL")
|
|
33
|
-
.action(loginCommand);
|
|
34
|
-
|
|
35
|
-
program
|
|
36
|
-
.command("logout")
|
|
37
|
-
.description("Logout from Gufi")
|
|
38
|
-
.action(logoutCommand);
|
|
39
|
-
|
|
40
|
-
program
|
|
41
|
-
.command("whoami")
|
|
42
|
-
.description("Show current logged in user")
|
|
43
|
-
.action(whoamiCommand);
|
|
44
|
-
|
|
45
|
-
// Sync commands
|
|
46
|
-
program
|
|
47
|
-
.command("pull [view]")
|
|
48
|
-
.description("Download view files from Gufi")
|
|
49
|
-
.action(pullCommand);
|
|
50
|
-
|
|
51
|
-
program
|
|
52
|
-
.command("push")
|
|
53
|
-
.description("Upload local changes to Gufi")
|
|
54
|
-
.action(pushCommand);
|
|
55
|
-
|
|
56
|
-
program
|
|
57
|
-
.command("watch")
|
|
58
|
-
.description("Watch for file changes and auto-sync")
|
|
59
|
-
.action(watchCommand);
|
|
60
|
-
|
|
61
|
-
program
|
|
62
|
-
.command("status")
|
|
63
|
-
.description("Show sync status")
|
|
64
|
-
.action(statusCommand);
|
|
65
|
-
|
|
66
|
-
program.parse();
|
package/src/lib/api.ts
DELETED
|
@@ -1,127 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gufi Dev CLI - API Client
|
|
3
|
-
* Communicates with Gufi Marketplace API
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import { getToken, getApiUrl } from "./config.js";
|
|
7
|
-
|
|
8
|
-
export interface ViewFile {
|
|
9
|
-
id?: number;
|
|
10
|
-
file_path: string;
|
|
11
|
-
content: string;
|
|
12
|
-
language: string;
|
|
13
|
-
is_entry_point?: boolean;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export interface View {
|
|
17
|
-
pk_id: number;
|
|
18
|
-
name: string;
|
|
19
|
-
description?: string;
|
|
20
|
-
view_type: string;
|
|
21
|
-
package_id: number;
|
|
22
|
-
config?: Record<string, any>;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export interface Package {
|
|
26
|
-
pk_id: number;
|
|
27
|
-
name: string;
|
|
28
|
-
description?: string;
|
|
29
|
-
company_id: number;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
class ApiError extends Error {
|
|
33
|
-
constructor(public status: number, message: string) {
|
|
34
|
-
super(message);
|
|
35
|
-
this.name = "ApiError";
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
async function request<T>(
|
|
40
|
-
endpoint: string,
|
|
41
|
-
options: RequestInit = {}
|
|
42
|
-
): Promise<T> {
|
|
43
|
-
const token = getToken();
|
|
44
|
-
if (!token) {
|
|
45
|
-
throw new Error("No estás logueado. Ejecuta: gufi login");
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const url = `${getApiUrl()}${endpoint}`;
|
|
49
|
-
const response = await fetch(url, {
|
|
50
|
-
...options,
|
|
51
|
-
headers: {
|
|
52
|
-
"Content-Type": "application/json",
|
|
53
|
-
Authorization: `Bearer ${token}`,
|
|
54
|
-
...options.headers,
|
|
55
|
-
},
|
|
56
|
-
});
|
|
57
|
-
|
|
58
|
-
if (!response.ok) {
|
|
59
|
-
const text = await response.text();
|
|
60
|
-
throw new ApiError(response.status, `API Error ${response.status}: ${text}`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return response.json();
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// ============ Auth ============
|
|
67
|
-
|
|
68
|
-
export async function login(email: string, password: string): Promise<{ token: string }> {
|
|
69
|
-
const url = `${getApiUrl()}/api/auth/login`;
|
|
70
|
-
const response = await fetch(url, {
|
|
71
|
-
method: "POST",
|
|
72
|
-
headers: { "Content-Type": "application/json" },
|
|
73
|
-
body: JSON.stringify({ email, password }),
|
|
74
|
-
});
|
|
75
|
-
|
|
76
|
-
if (!response.ok) {
|
|
77
|
-
throw new ApiError(response.status, "Credenciales inválidas");
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
const data = await response.json();
|
|
81
|
-
return { token: data.accessToken };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
// ============ Packages ============
|
|
85
|
-
|
|
86
|
-
export async function listPackages(): Promise<Package[]> {
|
|
87
|
-
return request<Package[]>("/api/marketplace/developer/packages");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// ============ Views ============
|
|
91
|
-
|
|
92
|
-
export async function listViews(packageId: number): Promise<View[]> {
|
|
93
|
-
return request<View[]>(`/api/marketplace/packages/${packageId}/views`);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
export async function getView(viewId: number): Promise<View> {
|
|
97
|
-
return request<View>(`/api/marketplace/views/${viewId}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
export async function getViewFiles(viewId: number): Promise<ViewFile[]> {
|
|
101
|
-
return request<ViewFile[]>(`/api/marketplace/views/${viewId}/files`);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
export async function saveViewFiles(viewId: number, files: ViewFile[]): Promise<void> {
|
|
105
|
-
await request(`/api/marketplace/views/${viewId}/files`, {
|
|
106
|
-
method: "PUT",
|
|
107
|
-
body: JSON.stringify({ files }),
|
|
108
|
-
});
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
export async function saveViewFile(viewId: number, file: ViewFile): Promise<void> {
|
|
112
|
-
await request(`/api/marketplace/views/${viewId}/files`, {
|
|
113
|
-
method: "PUT",
|
|
114
|
-
body: JSON.stringify({ files: [file] }),
|
|
115
|
-
});
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
// ============ Validation ============
|
|
119
|
-
|
|
120
|
-
export async function validateToken(): Promise<boolean> {
|
|
121
|
-
try {
|
|
122
|
-
await request("/api/auth/me");
|
|
123
|
-
return true;
|
|
124
|
-
} catch {
|
|
125
|
-
return false;
|
|
126
|
-
}
|
|
127
|
-
}
|
package/src/lib/config.ts
DELETED
|
@@ -1,93 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gufi Dev CLI - Config Management
|
|
3
|
-
* Stores credentials and settings in ~/.gufi/config.json
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
import path from "path";
|
|
8
|
-
import os from "os";
|
|
9
|
-
|
|
10
|
-
export interface GufiConfig {
|
|
11
|
-
apiUrl: string;
|
|
12
|
-
token?: string;
|
|
13
|
-
email?: string;
|
|
14
|
-
currentView?: {
|
|
15
|
-
id: number;
|
|
16
|
-
name: string;
|
|
17
|
-
packageId: number;
|
|
18
|
-
localPath: string;
|
|
19
|
-
};
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
const CONFIG_DIR = path.join(os.homedir(), ".gufi");
|
|
23
|
-
const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
|
|
24
|
-
|
|
25
|
-
const DEFAULT_CONFIG: GufiConfig = {
|
|
26
|
-
apiUrl: "https://gogufi.com",
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
export function ensureConfigDir(): void {
|
|
30
|
-
if (!fs.existsSync(CONFIG_DIR)) {
|
|
31
|
-
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
32
|
-
}
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function loadConfig(): GufiConfig {
|
|
36
|
-
ensureConfigDir();
|
|
37
|
-
if (!fs.existsSync(CONFIG_FILE)) {
|
|
38
|
-
return { ...DEFAULT_CONFIG };
|
|
39
|
-
}
|
|
40
|
-
try {
|
|
41
|
-
const data = fs.readFileSync(CONFIG_FILE, "utf-8");
|
|
42
|
-
return { ...DEFAULT_CONFIG, ...JSON.parse(data) };
|
|
43
|
-
} catch {
|
|
44
|
-
return { ...DEFAULT_CONFIG };
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function saveConfig(config: GufiConfig): void {
|
|
49
|
-
ensureConfigDir();
|
|
50
|
-
fs.writeFileSync(CONFIG_FILE, JSON.stringify(config, null, 2));
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function getToken(): string | undefined {
|
|
54
|
-
return loadConfig().token;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export function setToken(token: string, email: string): void {
|
|
58
|
-
const config = loadConfig();
|
|
59
|
-
config.token = token;
|
|
60
|
-
config.email = email;
|
|
61
|
-
saveConfig(config);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
export function clearToken(): void {
|
|
65
|
-
const config = loadConfig();
|
|
66
|
-
delete config.token;
|
|
67
|
-
delete config.email;
|
|
68
|
-
saveConfig(config);
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function isLoggedIn(): boolean {
|
|
72
|
-
return !!getToken();
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function setCurrentView(view: GufiConfig["currentView"]): void {
|
|
76
|
-
const config = loadConfig();
|
|
77
|
-
config.currentView = view;
|
|
78
|
-
saveConfig(config);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
export function getCurrentView(): GufiConfig["currentView"] | undefined {
|
|
82
|
-
return loadConfig().currentView;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function getApiUrl(): string {
|
|
86
|
-
return loadConfig().apiUrl;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export function setApiUrl(url: string): void {
|
|
90
|
-
const config = loadConfig();
|
|
91
|
-
config.apiUrl = url;
|
|
92
|
-
saveConfig(config);
|
|
93
|
-
}
|
package/src/lib/sync.ts
DELETED
|
@@ -1,236 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Gufi Dev CLI - Sync Library
|
|
3
|
-
* Handles bidirectional file synchronization
|
|
4
|
-
*/
|
|
5
|
-
|
|
6
|
-
import fs from "fs";
|
|
7
|
-
import path from "path";
|
|
8
|
-
import os from "os";
|
|
9
|
-
import { getViewFiles, saveViewFile, type ViewFile } from "./api.js";
|
|
10
|
-
import { setCurrentView, getCurrentView } from "./config.js";
|
|
11
|
-
|
|
12
|
-
const GUFI_DEV_DIR = path.join(os.homedir(), "gufi-dev");
|
|
13
|
-
const META_FILE = ".gufi-view.json";
|
|
14
|
-
|
|
15
|
-
export interface ViewMeta {
|
|
16
|
-
viewId: number;
|
|
17
|
-
viewName: string;
|
|
18
|
-
packageId: number;
|
|
19
|
-
lastSync: string;
|
|
20
|
-
files: Record<string, { hash: string; mtime: number }>;
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function ensureDir(dir: string): void {
|
|
24
|
-
if (!fs.existsSync(dir)) {
|
|
25
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
26
|
-
}
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function hashContent(content: string): string {
|
|
30
|
-
// Simple hash for change detection
|
|
31
|
-
let hash = 0;
|
|
32
|
-
for (let i = 0; i < content.length; i++) {
|
|
33
|
-
const char = content.charCodeAt(i);
|
|
34
|
-
hash = ((hash << 5) - hash) + char;
|
|
35
|
-
hash = hash & hash;
|
|
36
|
-
}
|
|
37
|
-
return hash.toString(16);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function getLanguage(filePath: string): string {
|
|
41
|
-
const ext = path.extname(filePath).toLowerCase();
|
|
42
|
-
const langMap: Record<string, string> = {
|
|
43
|
-
".ts": "typescript",
|
|
44
|
-
".tsx": "typescript",
|
|
45
|
-
".js": "javascript",
|
|
46
|
-
".jsx": "javascript",
|
|
47
|
-
".css": "css",
|
|
48
|
-
".json": "json",
|
|
49
|
-
".md": "markdown",
|
|
50
|
-
};
|
|
51
|
-
return langMap[ext] || "text";
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function getViewDir(viewName: string): string {
|
|
55
|
-
return path.join(GUFI_DEV_DIR, viewName.toLowerCase().replace(/\s+/g, "-"));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
export function loadViewMeta(viewDir: string): ViewMeta | null {
|
|
59
|
-
const metaPath = path.join(viewDir, META_FILE);
|
|
60
|
-
if (!fs.existsSync(metaPath)) return null;
|
|
61
|
-
try {
|
|
62
|
-
return JSON.parse(fs.readFileSync(metaPath, "utf-8"));
|
|
63
|
-
} catch {
|
|
64
|
-
return null;
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
function saveViewMeta(viewDir: string, meta: ViewMeta): void {
|
|
69
|
-
const metaPath = path.join(viewDir, META_FILE);
|
|
70
|
-
fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
/**
|
|
74
|
-
* Pull view files from Gufi to local directory
|
|
75
|
-
*/
|
|
76
|
-
export async function pullView(
|
|
77
|
-
viewId: number,
|
|
78
|
-
viewName: string,
|
|
79
|
-
packageId: number
|
|
80
|
-
): Promise<{ dir: string; fileCount: number }> {
|
|
81
|
-
const viewDir = getViewDir(viewName);
|
|
82
|
-
ensureDir(viewDir);
|
|
83
|
-
|
|
84
|
-
const files = await getViewFiles(viewId);
|
|
85
|
-
const fileMeta: ViewMeta["files"] = {};
|
|
86
|
-
|
|
87
|
-
for (const file of files) {
|
|
88
|
-
const filePath = path.join(viewDir, file.file_path.replace(/^\//, ""));
|
|
89
|
-
ensureDir(path.dirname(filePath));
|
|
90
|
-
fs.writeFileSync(filePath, file.content);
|
|
91
|
-
|
|
92
|
-
fileMeta[file.file_path] = {
|
|
93
|
-
hash: hashContent(file.content),
|
|
94
|
-
mtime: Date.now(),
|
|
95
|
-
};
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
const meta: ViewMeta = {
|
|
99
|
-
viewId,
|
|
100
|
-
viewName,
|
|
101
|
-
packageId,
|
|
102
|
-
lastSync: new Date().toISOString(),
|
|
103
|
-
files: fileMeta,
|
|
104
|
-
};
|
|
105
|
-
saveViewMeta(viewDir, meta);
|
|
106
|
-
|
|
107
|
-
// Update current view in config
|
|
108
|
-
setCurrentView({ id: viewId, name: viewName, packageId, localPath: viewDir });
|
|
109
|
-
|
|
110
|
-
return { dir: viewDir, fileCount: files.length };
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
/**
|
|
114
|
-
* Push local files to Gufi
|
|
115
|
-
*/
|
|
116
|
-
export async function pushView(viewDir?: string): Promise<{ pushed: number }> {
|
|
117
|
-
const dir = viewDir || getCurrentView()?.localPath;
|
|
118
|
-
if (!dir) {
|
|
119
|
-
throw new Error("No hay vista activa. Usa: gufi pull <vista>");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
const meta = loadViewMeta(dir);
|
|
123
|
-
if (!meta) {
|
|
124
|
-
throw new Error("No se encontró metadata de vista. ¿Hiciste pull primero?");
|
|
125
|
-
}
|
|
126
|
-
|
|
127
|
-
const localFiles = getLocalFiles(dir);
|
|
128
|
-
let pushed = 0;
|
|
129
|
-
|
|
130
|
-
for (const file of localFiles) {
|
|
131
|
-
const content = fs.readFileSync(path.join(dir, file), "utf-8");
|
|
132
|
-
const hash = hashContent(content);
|
|
133
|
-
const filePath = "/" + file;
|
|
134
|
-
|
|
135
|
-
// Check if file changed
|
|
136
|
-
const oldMeta = meta.files[filePath];
|
|
137
|
-
if (oldMeta && oldMeta.hash === hash) {
|
|
138
|
-
continue; // No changes
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
// Push to Gufi
|
|
142
|
-
await saveViewFile(meta.viewId, {
|
|
143
|
-
file_path: filePath,
|
|
144
|
-
content,
|
|
145
|
-
language: getLanguage(file),
|
|
146
|
-
is_entry_point: file === "index.tsx",
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
// Update meta
|
|
150
|
-
meta.files[filePath] = { hash, mtime: Date.now() };
|
|
151
|
-
pushed++;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
meta.lastSync = new Date().toISOString();
|
|
155
|
-
saveViewMeta(dir, meta);
|
|
156
|
-
|
|
157
|
-
return { pushed };
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Get list of local files (excluding meta and hidden)
|
|
162
|
-
*/
|
|
163
|
-
function getLocalFiles(dir: string, prefix = ""): string[] {
|
|
164
|
-
const files: string[] = [];
|
|
165
|
-
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
166
|
-
|
|
167
|
-
for (const entry of entries) {
|
|
168
|
-
if (entry.name.startsWith(".")) continue; // Skip hidden files
|
|
169
|
-
|
|
170
|
-
const fullPath = path.join(prefix, entry.name);
|
|
171
|
-
|
|
172
|
-
if (entry.isDirectory()) {
|
|
173
|
-
files.push(...getLocalFiles(path.join(dir, entry.name), fullPath));
|
|
174
|
-
} else {
|
|
175
|
-
files.push(fullPath);
|
|
176
|
-
}
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
return files;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
|
-
/**
|
|
183
|
-
* Check for local changes that need pushing
|
|
184
|
-
*/
|
|
185
|
-
export function getChangedFiles(viewDir: string): string[] {
|
|
186
|
-
const meta = loadViewMeta(viewDir);
|
|
187
|
-
if (!meta) return [];
|
|
188
|
-
|
|
189
|
-
const changed: string[] = [];
|
|
190
|
-
const localFiles = getLocalFiles(viewDir);
|
|
191
|
-
|
|
192
|
-
for (const file of localFiles) {
|
|
193
|
-
const content = fs.readFileSync(path.join(viewDir, file), "utf-8");
|
|
194
|
-
const hash = hashContent(content);
|
|
195
|
-
const filePath = "/" + file;
|
|
196
|
-
|
|
197
|
-
const oldMeta = meta.files[filePath];
|
|
198
|
-
if (!oldMeta || oldMeta.hash !== hash) {
|
|
199
|
-
changed.push(file);
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
return changed;
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Push a single file
|
|
208
|
-
*/
|
|
209
|
-
export async function pushSingleFile(
|
|
210
|
-
viewDir: string,
|
|
211
|
-
relativePath: string
|
|
212
|
-
): Promise<void> {
|
|
213
|
-
const meta = loadViewMeta(viewDir);
|
|
214
|
-
if (!meta) {
|
|
215
|
-
throw new Error("No se encontró metadata de vista");
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
const fullPath = path.join(viewDir, relativePath);
|
|
219
|
-
const content = fs.readFileSync(fullPath, "utf-8");
|
|
220
|
-
const filePath = "/" + relativePath.replace(/\\/g, "/");
|
|
221
|
-
|
|
222
|
-
await saveViewFile(meta.viewId, {
|
|
223
|
-
file_path: filePath,
|
|
224
|
-
content,
|
|
225
|
-
language: getLanguage(relativePath),
|
|
226
|
-
is_entry_point: relativePath === "index.tsx",
|
|
227
|
-
});
|
|
228
|
-
|
|
229
|
-
// Update meta
|
|
230
|
-
meta.files[filePath] = {
|
|
231
|
-
hash: hashContent(content),
|
|
232
|
-
mtime: Date.now(),
|
|
233
|
-
};
|
|
234
|
-
meta.lastSync = new Date().toISOString();
|
|
235
|
-
saveViewMeta(viewDir, meta);
|
|
236
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "NodeNext",
|
|
5
|
-
"moduleResolution": "NodeNext",
|
|
6
|
-
"outDir": "./dist",
|
|
7
|
-
"rootDir": "./src",
|
|
8
|
-
"strict": true,
|
|
9
|
-
"esModuleInterop": true,
|
|
10
|
-
"skipLibCheck": true,
|
|
11
|
-
"forceConsistentCasingInFileNames": true,
|
|
12
|
-
"declaration": true,
|
|
13
|
-
"resolveJsonModule": true
|
|
14
|
-
},
|
|
15
|
-
"include": ["src/**/*"],
|
|
16
|
-
"exclude": ["node_modules", "dist"]
|
|
17
|
-
}
|