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/README.md +91 -107
- package/bin/primocode.js +274 -476
- package/lib/act.js +43 -471
- package/lib/api.js +5 -3
- package/lib/config.js +99 -35
- package/lib/desktop.js +142 -0
- package/lib/tools.js +68 -467
- package/lib/ui.js +135 -78
- package/package.json +7 -18
- package/lib/mascot.js +0 -121
- package/lib/memory.js +0 -179
- package/lib/mouse.js +0 -144
- package/lib/platform.js +0 -245
package/lib/config.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
//
|
|
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
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
-
|
|
23
|
-
|
|
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
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
-
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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 = {
|
|
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
|
+
};
|