primocode 9.8.0-beta.0 → 9.8.0-beta.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 +5 -4
- package/bin/primocode.js +60 -0
- package/lib/api.js +18 -0
- package/lib/local/cerebro.json +2116 -0
- package/lib/local/entrada.js +332 -0
- package/lib/local/motor.js +264 -0
- package/lib/local/provedor.js +151 -0
- package/package.json +2 -2
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* provedor.js — a chave da própria pessoa, e com qual IA ela fala.
|
|
3
|
+
*
|
|
4
|
+
* "Fica batendo no limite. Coloca um comando /provider onde você seleciona
|
|
5
|
+
* se é Gemini, ChatGPT etc., cola a key e ela roda local com a key
|
|
6
|
+
* configurada." (23/09/2026)
|
|
7
|
+
*
|
|
8
|
+
* Com um provedor configurado (/api), o agente fala DIRETO com a API dele, daqui da
|
|
9
|
+
* máquina (lib/local/motor.js). O servidor e a cota compartilhada saem do
|
|
10
|
+
* caminho: o limite passa a ser o da conta da pessoa.
|
|
11
|
+
*
|
|
12
|
+
* ── ONDE A CHAVE MORA ────────────────────────────────────────────────────
|
|
13
|
+
* Em `~/.primocode/provedor.json`, permissão 600. Não no config.json: esse é
|
|
14
|
+
* o arquivo que alguém abre para conferir um ajuste ou cola num chamado.
|
|
15
|
+
*
|
|
16
|
+
* ── O QUE NUNCA APARECE ──────────────────────────────────────────────────
|
|
17
|
+
* A chave inteira — nem ao listar, nem num erro. Mostra-se o começo e o fim,
|
|
18
|
+
* o bastante para reconhecer qual é.
|
|
19
|
+
*
|
|
20
|
+
* Todos falam o formato de chat da OpenAI (/chat/completions, tools, stream):
|
|
21
|
+
* é o que Gemini, Claude, Groq, OpenRouter e DeepSeek aceitam também. Um
|
|
22
|
+
* cliente só, e o provedor é só o endereço, o modelo e a chave.
|
|
23
|
+
*/
|
|
24
|
+
|
|
25
|
+
'use strict';
|
|
26
|
+
|
|
27
|
+
const fs = require('fs');
|
|
28
|
+
const os = require('os');
|
|
29
|
+
const path = require('path');
|
|
30
|
+
|
|
31
|
+
const ARQUIVO = () => path.join(os.homedir(), '.primocode', 'provedor.json');
|
|
32
|
+
|
|
33
|
+
/* A ordem é a do menu. `modelos` é a preferência: ao colar a chave, fica o
|
|
34
|
+
primeiro que a conta da pessoa de fato tem (a lista vem da própria API). */
|
|
35
|
+
const PROVEDORES = {
|
|
36
|
+
gemini: {
|
|
37
|
+
nome: 'Gemini (Google)',
|
|
38
|
+
base: 'https://generativelanguage.googleapis.com/v1beta/openai',
|
|
39
|
+
modelos: ['gemini-2.5-flash', 'gemini-2.5-pro', 'gemini-2.0-flash'],
|
|
40
|
+
onde: 'https://aistudio.google.com/apikey',
|
|
41
|
+
dica: 'tem plano grátis',
|
|
42
|
+
},
|
|
43
|
+
chatgpt: {
|
|
44
|
+
nome: 'ChatGPT (OpenAI)',
|
|
45
|
+
base: 'https://api.openai.com/v1',
|
|
46
|
+
modelos: ['gpt-5-mini', 'gpt-5', 'gpt-4.1-mini', 'gpt-4o-mini'],
|
|
47
|
+
onde: 'https://platform.openai.com/api-keys',
|
|
48
|
+
dica: 'pago por uso',
|
|
49
|
+
},
|
|
50
|
+
claude: {
|
|
51
|
+
nome: 'Claude (Anthropic)',
|
|
52
|
+
base: 'https://api.anthropic.com/v1',
|
|
53
|
+
modelos: ['claude-sonnet-5', 'claude-haiku-4-5-20251001', 'claude-opus-5-5'],
|
|
54
|
+
onde: 'https://console.anthropic.com/settings/keys',
|
|
55
|
+
dica: 'pago por uso',
|
|
56
|
+
},
|
|
57
|
+
groq: {
|
|
58
|
+
nome: 'Groq',
|
|
59
|
+
base: 'https://api.groq.com/openai/v1',
|
|
60
|
+
modelos: ['openai/gpt-oss-120b', 'llama-3.3-70b-versatile', 'openai/gpt-oss-20b'],
|
|
61
|
+
onde: 'https://console.groq.com/keys',
|
|
62
|
+
dica: 'tem plano grátis, muito rápido',
|
|
63
|
+
},
|
|
64
|
+
openrouter: {
|
|
65
|
+
nome: 'OpenRouter',
|
|
66
|
+
base: 'https://openrouter.ai/api/v1',
|
|
67
|
+
modelos: ['openai/gpt-oss-120b', 'google/gemini-2.5-flash', 'anthropic/claude-sonnet-5'],
|
|
68
|
+
onde: 'https://openrouter.ai/keys',
|
|
69
|
+
dica: 'vários modelos numa chave só',
|
|
70
|
+
},
|
|
71
|
+
deepseek: {
|
|
72
|
+
nome: 'DeepSeek',
|
|
73
|
+
base: 'https://api.deepseek.com/v1',
|
|
74
|
+
modelos: ['deepseek-chat'],
|
|
75
|
+
onde: 'https://platform.deepseek.com/api_keys',
|
|
76
|
+
dica: 'barato',
|
|
77
|
+
},
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
/** A chave, irreconhecível para quem olha por cima do ombro. */
|
|
81
|
+
function mascarar(chave) {
|
|
82
|
+
const k = String(chave || '');
|
|
83
|
+
if (k.length <= 10) return '••••';
|
|
84
|
+
return `${k.slice(0, 4)}…${k.slice(-4)}`;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
let cache = null;
|
|
88
|
+
let cacheMtime = 0;
|
|
89
|
+
|
|
90
|
+
/** O provedor configurado, ou null (= segue pelo servidor, como sempre). */
|
|
91
|
+
function atual() {
|
|
92
|
+
if (process.env.PRIMOCODE_SEM_PROVEDOR === '1') return null;
|
|
93
|
+
let st;
|
|
94
|
+
try { st = fs.statSync(ARQUIVO()); } catch { cache = null; return null; }
|
|
95
|
+
if (cache && st.mtimeMs === cacheMtime) return cache;
|
|
96
|
+
try {
|
|
97
|
+
const j = JSON.parse(fs.readFileSync(ARQUIVO(), 'utf8'));
|
|
98
|
+
const p = PROVEDORES[j.provedor];
|
|
99
|
+
cache = p && j.chave ? { id: j.provedor, nome: p.nome, base: j.base || p.base, modelo: j.modelo || p.modelos[0], chave: j.chave } : null;
|
|
100
|
+
} catch { cache = null; }
|
|
101
|
+
cacheMtime = st.mtimeMs;
|
|
102
|
+
return cache;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function salvar({ provedor, chave, modelo }) {
|
|
106
|
+
if (!PROVEDORES[provedor]) throw new Error(`provedor desconhecido: ${provedor}`);
|
|
107
|
+
const arq = ARQUIVO();
|
|
108
|
+
fs.mkdirSync(path.dirname(arq), { recursive: true });
|
|
109
|
+
fs.writeFileSync(arq, JSON.stringify({ provedor, chave: String(chave).trim(), modelo }, null, 2), { mode: 0o600 });
|
|
110
|
+
try { fs.chmodSync(arq, 0o600); } catch { /* Windows */ }
|
|
111
|
+
cache = null;
|
|
112
|
+
return atual();
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
function trocarModelo(modelo) {
|
|
116
|
+
const a = atual();
|
|
117
|
+
if (!a) return null;
|
|
118
|
+
return salvar({ provedor: a.id, chave: a.chave, modelo });
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function desligar() {
|
|
122
|
+
try { fs.unlinkSync(ARQUIVO()); } catch { /* já não havia */ }
|
|
123
|
+
cache = null;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/** A chave colada serve? Pergunta a lista de modelos à própria API. */
|
|
127
|
+
async function conferir(provedor, chave, { buscar = fetch } = {}) {
|
|
128
|
+
const p = PROVEDORES[provedor];
|
|
129
|
+
let r;
|
|
130
|
+
try {
|
|
131
|
+
r = await buscar(`${p.base}/models`, {
|
|
132
|
+
headers: { Authorization: `Bearer ${String(chave).trim()}` },
|
|
133
|
+
signal: AbortSignal.timeout(15000),
|
|
134
|
+
});
|
|
135
|
+
} catch (e) {
|
|
136
|
+
return { ok: false, error: `não consegui falar com ${p.nome}: ${e.message}` };
|
|
137
|
+
}
|
|
138
|
+
if (r.status === 401 || r.status === 403) return { ok: false, error: `${p.nome} recusou a chave (${r.status}). Confira se copiou inteira.` };
|
|
139
|
+
if (!r.ok) return { ok: false, error: `${p.nome} respondeu ${r.status} ao conferir a chave.` };
|
|
140
|
+
let lista = [];
|
|
141
|
+
try {
|
|
142
|
+
const j = await r.json();
|
|
143
|
+
lista = (j.data || j.models || []).map((m) => String(m.id || m.name || '').replace(/^models\//, '')).filter(Boolean);
|
|
144
|
+
} catch { /* sem lista: fica a preferência */ }
|
|
145
|
+
// Nenhum da preferência na lista (ou lista vazia): fica o primeiro dela, e
|
|
146
|
+
// `/api modelo <nome>` troca.
|
|
147
|
+
const modelo = p.modelos.find((m) => lista.includes(m)) || p.modelos[0];
|
|
148
|
+
return { ok: true, modelo, disponiveis: lista };
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
module.exports = { PROVEDORES, ARQUIVO, mascarar, atual, salvar, trocarModelo, desligar, conferir };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "9.8.0-beta.
|
|
3
|
+
"version": "9.8.0-beta.1",
|
|
4
4
|
"description": "PrimoCode — agente de engenharia com IA e cursor próprio. Requer conta Conecta Primo AI (Premium ou Super). Cria arquivos, roda comandos, controla navegador e desktop: abre apps, clica em botões e ícones pelo nome, digita e usa atalhos.",
|
|
5
5
|
"main": "bin/primocode.js",
|
|
6
6
|
"bin": {
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
},
|
|
9
9
|
"scripts": {
|
|
10
10
|
"start": "node bin/primocode.js",
|
|
11
|
-
"test": "node test/tools.test.js && node test/ui.test.js && node test/entrada.test.js && node test/transparencia.test.js && node test/permissao.test.js && node test/compatibilidade.test.js && node test/skills.test.js && node test/estudio.test.js && node test/voz.test.js && node test/voz-conversa.test.js && node test/modo.test.js && node test/pasta.test.js && node test/projeto.test.js && node test/nome-projeto.test.js && node test/conferir.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/seta.test.js && node test/effort.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/autonomia.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/pipeline.test.js && node test/app.test.js && node test/aplicativo.test.js && node test/conta.test.mjs && node test/nuvem.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/janela.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/empurrao.test.js && node test/terminal-novo.test.js && node test/markdown.test.js && node test/coerencia.test.js"
|
|
11
|
+
"test": "node test/tools.test.js && node test/ui.test.js && node test/entrada.test.js && node test/transparencia.test.js && node test/permissao.test.js && node test/compatibilidade.test.js && node test/skills.test.js && node test/estudio.test.js && node test/voz.test.js && node test/voz-conversa.test.js && node test/modo.test.js && node test/pasta.test.js && node test/projeto.test.js && node test/nome-projeto.test.js && node test/api-propria.test.js && node test/conferir.test.js && node test/catalogo.test.js && node test/desktop.test.js && node test/seta.test.js && node test/effort.test.js && node test/regressao.test.js && node test/memoria-conversa.test.js && node test/repeticao.test.js && node test/autonomia.test.js && node test/referencia.test.js && node test/parar.test.js && node test/continuar.test.js && node test/pipeline.test.js && node test/app.test.js && node test/aplicativo.test.js && node test/conta.test.mjs && node test/nuvem.test.js && node test/prazo.test.js && node test/primeira-vez.test.js && node test/janela.test.js && node test/fala.test.js && node test/especialistas.test.js && node test/empurrao.test.js && node test/terminal-novo.test.js && node test/markdown.test.js && node test/coerencia.test.js"
|
|
12
12
|
},
|
|
13
13
|
"engines": {
|
|
14
14
|
"node": ">=18.17.0"
|