primocode 4.3.1 → 7.0.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/lib/config.js CHANGED
@@ -1,4 +1,9 @@
1
- // Configuracao PrimoCode — Cross-Platform (Windows, macOS, Linux, Termux)
1
+ // Carrega config de ~/.primocode/config.json + env vars.
2
+ // Estrutura de pastas:
3
+ // ~/primocode/ <- raiz (projetos + you.md)
4
+ // ~/primocode/primocode/ <- codigo interno do CLI
5
+ // ~/primocode/<projeto>/ <- projetos gerados
6
+ // ~/primocode/<projeto>/memory.md <- memorias do projeto
2
7
 
3
8
  'use strict';
4
9
 
@@ -6,71 +11,130 @@ const fs = require('fs');
6
11
  const path = require('path');
7
12
  const os = require('os');
8
13
 
9
- // Detecta Termux (Android)
10
- function isTermux() {
11
- return process.env.TERMUX_VERSION !== undefined ||
12
- process.env.PREFIX?.includes('termux') ||
13
- process.env.HOME?.includes('termux');
14
- }
15
-
16
- // Diretório base
17
- const HOME = isTermux() ? '/data/data/com.termux/files/home' : os.homedir();
14
+ const HOME = os.homedir();
15
+ const ROOT_DIR = path.join(HOME, 'primocode');
16
+ const INTERNAL_DIR = path.join(ROOT_DIR, 'primocode');
18
17
  const CONFIG_DIR = path.join(HOME, '.primocode');
19
18
  const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
20
19
  const HISTORY_FILE = path.join(CONFIG_DIR, 'history.jsonl');
21
20
 
22
- function ensureDir() {
23
- try { fs.mkdirSync(CONFIG_DIR, { recursive: true }); } catch {}
21
+ const YOU_MD_TEMPLATE = `# You - PrimoCode AI
22
+
23
+ ## Sobre mim
24
+ Sou o Kimi Chat, um assistente de IA desenvolvido pela Moonshot AI.
25
+
26
+ ## Capacidades
27
+ - Ajudo na criacao de sistemas complexos
28
+ - Geracao de codigo de alto nivel
29
+ - Automacao de desktop e navegador
30
+ - Controle via CDP (Chrome DevTools Protocol)
31
+
32
+ ## Projetos
33
+ Este arquivo contem informacoes sobre mim e projetos em geral.
34
+ Cada pasta de projeto tem suas memorias em memory.md.
35
+ `;
36
+
37
+ const MEMORY_MD_TEMPLATE = `# Memorias do Projeto
38
+
39
+ ## Descricao
40
+ {nome_do_projeto}
41
+
42
+ ## Tecnologias
43
+ - Tecnologia: {tecnologia}
44
+
45
+ ## Estrutura de Pastas
46
+ - {estrutura}
47
+
48
+ ## Notas
49
+ - Criado em: {data}
50
+
51
+ ## Historico
52
+ - Primeira interacao: {data}
53
+ `;
54
+
55
+ function ensureDir(dir) {
56
+ try { fs.mkdirSync(dir, { recursive: true }); } catch {}
24
57
  }
25
58
 
26
- // Mapeamento de modos para modelos do servidor
27
- const MODEL_MAP = {
28
- 'Primo Code': 'main',
29
- 'Chat': 'fast',
30
- 'chat': 'fast',
31
- };
59
+ function ensureStructure() {
60
+ ensureDir(ROOT_DIR);
61
+ ensureDir(INTERNAL_DIR);
62
+ ensureDir(CONFIG_DIR);
63
+ const youMdPath = path.join(ROOT_DIR, 'you.md');
64
+ if (!fs.existsSync(youMdPath)) {
65
+ fs.writeFileSync(youMdPath, YOU_MD_TEMPLATE, 'utf-8');
66
+ }
67
+ return { rootDir: ROOT_DIR, internalDir: INTERNAL_DIR };
68
+ }
69
+
70
+ function createProjectDir(projectName) {
71
+ ensureStructure();
72
+ const projectDir = path.join(ROOT_DIR, projectName);
73
+ ensureDir(projectDir);
74
+ const memoryPath = path.join(projectDir, 'memory.md');
75
+ if (!fs.existsSync(memoryPath)) {
76
+ const content = MEMORY_MD_TEMPLATE
77
+ .replace(/{nome_do_projeto}/g, projectName)
78
+ .replace(/{tecnologia}/g, 'a definir')
79
+ .replace(/{estrutura}/g, 'a definir')
80
+ .replace(/{data}/g, new Date().toISOString().split('T')[0]);
81
+ fs.writeFileSync(memoryPath, content, 'utf-8');
82
+ }
83
+ return projectDir;
84
+ }
85
+
86
+ function getProjectDir(projectName) {
87
+ if (!projectName) return ROOT_DIR;
88
+ return path.join(ROOT_DIR, projectName);
89
+ }
32
90
 
33
- // Resolve o modelo correto baseado no modo
34
- function resolveModel(mode) {
35
- return MODEL_MAP[mode] || MODEL_MAP['Primo Code'];
91
+ function listProjects() {
92
+ ensureStructure();
93
+ try {
94
+ return fs.readdirSync(ROOT_DIR)
95
+ .filter(name => {
96
+ const fullPath = path.join(ROOT_DIR, name);
97
+ return fs.statSync(fullPath).isDirectory() && name !== 'primocode';
98
+ });
99
+ } catch { return []; }
36
100
  }
37
101
 
38
102
  function loadConfig() {
39
- ensureDir();
103
+ ensureDir(CONFIG_DIR);
104
+ ensureStructure();
40
105
  let file = {};
41
106
  try {
42
107
  if (fs.existsSync(CONFIG_FILE)) file = JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8'));
43
108
  } catch { file = {}; }
44
-
45
- const mode = process.env.PRIMOCODE_MODE || file.mode || 'Primo Code';
46
- const model = resolveModel(mode);
47
-
48
109
  return {
49
110
  server: process.env.PRIMOCODE_SERVER || file.server || 'https://primocode-server.onrender.com',
50
- model: model,
51
- mode: mode,
111
+ model: process.env.PRIMOCODE_MODEL || file.model || 'main',
52
112
  autoApply: file.autoApply === true,
53
- isTermux: isTermux(),
113
+ currentProject: file.currentProject || null,
114
+ rootDir: ROOT_DIR,
54
115
  _file: file,
55
116
  };
56
117
  }
57
118
 
58
119
  function saveConfig(partial) {
59
- ensureDir();
120
+ ensureDir(CONFIG_DIR);
60
121
  const cur = (() => { try { return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf8')); } catch { return {}; } })();
61
122
  const next = { ...cur, ...partial };
62
- if (partial.mode && MODEL_MAP[partial.mode]) {
63
- next.model = MODEL_MAP[partial.mode];
64
- }
65
123
  fs.writeFileSync(CONFIG_FILE, JSON.stringify(next, null, 2), 'utf8');
66
124
  return next;
67
125
  }
68
126
 
69
127
  function appendHistory(entry) {
70
- ensureDir();
128
+ ensureDir(CONFIG_DIR);
71
129
  try {
72
130
  fs.appendFileSync(HISTORY_FILE, JSON.stringify({ ts: Date.now(), ...entry }) + '\n', 'utf8');
73
131
  } catch {}
74
132
  }
75
133
 
76
- module.exports = { loadConfig, saveConfig, appendHistory, resolveModel, isTermux, CONFIG_DIR, CONFIG_FILE, HISTORY_FILE, MODEL_MAP };
134
+ module.exports = {
135
+ loadConfig, saveConfig, appendHistory,
136
+ CONFIG_DIR, CONFIG_FILE, HISTORY_FILE,
137
+ ROOT_DIR, INTERNAL_DIR,
138
+ ensureStructure, createProjectDir, getProjectDir, listProjects,
139
+ YOU_MD_TEMPLATE, MEMORY_MD_TEMPLATE,
140
+ };
package/lib/desktop.js ADDED
@@ -0,0 +1,142 @@
1
+ /**
2
+ * desktop.js — Controle nativo do mouse/teclado do sistema operacional.
3
+ * Zero dependências npm. Usa ferramentas nativas do SO:
4
+ * Linux: xdotool
5
+ * macOS: cliclick (brew install cliclick) ou osascript
6
+ * Windows: powershell + System.Windows.Forms
7
+ */
8
+
9
+ 'use strict';
10
+
11
+ const { execSync } = require('child_process');
12
+ const os = require('os');
13
+
14
+ const PLATFORM = os.platform();
15
+
16
+ function hasCmd(cmd) {
17
+ try { execSync(`which ${cmd} || where ${cmd}`, { stdio: 'ignore' }); return true; } catch { return false; }
18
+ }
19
+
20
+ // Detectar ferramenta disponível
21
+ const TOOLS = {
22
+ xdotool: PLATFORM === 'linux' && hasCmd('xdotool'),
23
+ cliclick: PLATFORM === 'darwin' && hasCmd('cliclick'),
24
+ nircmd: PLATFORM === 'win32' && (() => { try { execSync('where nircmd', {stdio:'ignore'}); return true; } catch { return false; } })(),
25
+ };
26
+
27
+ function run(cmd) {
28
+ try { execSync(cmd, { stdio: 'ignore', timeout: 10000 }); return { ok: true }; }
29
+ catch (e) { return { ok: false, error: e.message }; }
30
+ }
31
+
32
+ // ── Mouse ─────────────────────────────────────────────
33
+ function click(x, y, button = 'left') {
34
+ if (TOOLS.xdotool) return run(`xdotool mousemove ${x} ${y} click ${button === 'right' ? 3 : 1}`);
35
+ if (TOOLS.cliclick) return run(`cliclick c:${x},${y}`);
36
+ if (PLATFORM === 'win32') {
37
+ const b = button === 'right' ? 'Right' : 'Left';
38
+ const ps = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${x}, ${y}); [System.Windows.Forms.MouseButtons]::${b} -bor 0`;
39
+ return run(`powershell -Command "${ps}"`);
40
+ }
41
+ return { ok: false, error: 'Nenhuma ferramenta de automação encontrada. Instale xdotool (Linux), cliclick (Mac) ou configure o modo browser.' };
42
+ }
43
+
44
+ function move(x, y) {
45
+ if (TOOLS.xdotool) return run(`xdotool mousemove ${x} ${y}`);
46
+ if (TOOLS.cliclick) return run(`cliclick m:${x},${y}`);
47
+ if (PLATFORM === 'win32') {
48
+ const ps = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Cursor]::Position = New-Object System.Drawing.Point(${x}, ${y})`;
49
+ return run(`powershell -Command "${ps}"`);
50
+ }
51
+ return { ok: false, error: 'Automação não disponível' };
52
+ }
53
+
54
+ function drag(fromX, fromY, toX, toY) {
55
+ if (TOOLS.xdotool) {
56
+ return run(`xdotool mousemove ${fromX} ${fromY} mousedown 1 mousemove ${toX} ${toY} mouseup 1`);
57
+ }
58
+ if (TOOLS.cliclick) {
59
+ return run(`cliclick dd:${fromX},${fromY} du:${toX},${toY}`);
60
+ }
61
+ return { ok: false, error: 'Drag não suportado nesta plataforma sem xdotool/cliclick' };
62
+ }
63
+
64
+ function scroll(dy) {
65
+ if (TOOLS.xdotool) return run(`xdotool click --repeat ${Math.abs(Math.round(dy / 50))} ${dy > 0 ? 5 : 4}`);
66
+ if (TOOLS.cliclick) return run(`cliclick scroll:${Math.round(dy / 50)}`);
67
+ return { ok: false, error: 'Scroll não suportado' };
68
+ }
69
+
70
+ // ── Teclado ───────────────────────────────────────────
71
+ function type(text) {
72
+ if (TOOLS.xdotool) return run(`xdotool type --delay 10 '${text.replace(/'/g, "'\''")}'`);
73
+ if (TOOLS.cliclick) {
74
+ const safe = text.replace(/"/g, '\\"');
75
+ return run(`osascript -e 'tell application "System Events" to keystroke "${safe}"'`);
76
+ }
77
+ if (PLATFORM === 'win32') {
78
+ const ps = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('${text.replace(/'/g, "''")}')`;
79
+ return run(`powershell -Command "${ps}"`);
80
+ }
81
+ return { ok: false, error: 'Automação de teclado não disponível' };
82
+ }
83
+
84
+ function key(name) {
85
+ const map = {
86
+ Enter: 'Return', Tab: 'Tab', Escape: 'Escape', Backspace: 'Backspace',
87
+ ArrowUp: 'Up', ArrowDown: 'Down', ArrowLeft: 'Left', ArrowRight: 'Right',
88
+ Space: 'space', Delete: 'Delete',
89
+ };
90
+ const k = map[name] || name;
91
+ if (TOOLS.xdotool) return run(`xdotool key ${k}`);
92
+ if (PLATFORM === 'darwin') return run(`osascript -e 'tell application "System Events" to key code ${k}'`);
93
+ if (PLATFORM === 'win32') {
94
+ const ps = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('{${name.toUpperCase()}}')`;
95
+ return run(`powershell -Command "${ps}"`);
96
+ }
97
+ return { ok: false, error: 'Key não suportado' };
98
+ }
99
+
100
+ // ── Screenshot ────────────────────────────────────────
101
+ function screenshot(saveTo) {
102
+ const filename = saveTo || `desktop-${Date.now()}.png`;
103
+ if (PLATFORM === 'darwin') {
104
+ return run(`screencapture -x ${filename}`) ? { ok: true, path: filename } : { ok: false, error: 'screencapture falhou' };
105
+ }
106
+ if (PLATFORM === 'win32') {
107
+ const ps = `Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.SendKeys]::SendWait('{PRTSC}'); Add-Type -AssemblyName System.Drawing; \$bmp = New-Object System.Drawing.Bitmap([System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Width, [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Height); \$graphics = [System.Drawing.Graphics]::FromImage(\$bmp); \$graphics.CopyFromScreen([System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Location, [System.Drawing.Point]::Empty, \$bmp.Size); \$bmp.Save('${filename}'); \$graphics.Dispose(); \$bmp.Dispose()`;
108
+ return run(`powershell -Command "${ps}"`) ? { ok: true, path: filename } : { ok: false, error: 'Screenshot falhou' };
109
+ }
110
+ return { ok: false, error: 'Screenshot desktop não suportado nesta plataforma. Instale scrot ou gnome-screenshot.' };
111
+ }
112
+
113
+ function getScreenSize() {
114
+ if (PLATFORM === 'win32') {
115
+ try {
116
+ const out = execSync(`powershell -Command "Add-Type -AssemblyName System.Windows.Forms; [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Size.Width.ToString() + ',' + [System.Windows.Forms.Screen]::PrimaryScreen.Bounds.Size.Height.ToString()"`).toString().trim();
117
+ const [w, h] = out.split(',').map(Number);
118
+ return { ok: true, width: w, height: h };
119
+ } catch { }
120
+ }
121
+ if (PLATFORM === 'darwin') {
122
+ try {
123
+ const out = execSync(`system_profiler SPDisplaysDataType | grep Resolution`).toString();
124
+ const m = out.match(/(\d+)\s*x\s*(\d+)/);
125
+ if (m) return { ok: true, width: parseInt(m[1]), height: parseInt(m[2]) };
126
+ } catch { }
127
+ }
128
+ if (TOOLS.xdotool) {
129
+ try {
130
+ const wh = execSync(`xdotool getdisplaygeometry`).toString().trim().split(' ');
131
+ return { ok: true, width: parseInt(wh[0]), height: parseInt(wh[1]) };
132
+ } catch { }
133
+ }
134
+ return { ok: false, error: 'Não foi possível detectar resolução', width: 1920, height: 1080 };
135
+ }
136
+
137
+ module.exports = {
138
+ click, move, drag, scroll,
139
+ type, key,
140
+ screenshot, getScreenSize,
141
+ TOOLS,
142
+ };