primocode 7.12.0 → 7.12.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/lib/act.js +5 -2
- package/lib/desktop.js +10 -2
- package/lib/streaming.js +65 -56
- package/package.json +1 -1
package/lib/act.js
CHANGED
|
@@ -15,7 +15,10 @@ const tools = require('./tools');
|
|
|
15
15
|
const api = require('./api');
|
|
16
16
|
const cfg = require('./config');
|
|
17
17
|
const catalogo = require('./catalogo');
|
|
18
|
-
|
|
18
|
+
// "painel", não "streaming": dentro do runAct existe um `let streaming` (a
|
|
19
|
+
// flag de texto chegando em stream) que SOMBREARIA este módulo — e o step()
|
|
20
|
+
// viraria TypeError engolido pelo catch. Foi um bug real, pego em teste.
|
|
21
|
+
const painel = require('./streaming');
|
|
19
22
|
|
|
20
23
|
const MAX_ITERATIONS = 15;
|
|
21
24
|
const MAX_PROFUNDIDADE = 2; // principal + 1 nível de subagente
|
|
@@ -282,7 +285,7 @@ async function runAct(userPrompt, opts) {
|
|
|
282
285
|
|
|
283
286
|
// Painel ao vivo (/streaming): registra o passo e fotografa a tela.
|
|
284
287
|
// Nunca pode derrubar o loop — falha de foto é problema do painel.
|
|
285
|
-
try {
|
|
288
|
+
try { painel.step(tc.name, tc.arguments, result); } catch {}
|
|
286
289
|
|
|
287
290
|
// Uma escrita bem-sucedida limpa a dívida: o agente se corrigiu.
|
|
288
291
|
if (result && result.ok === false) falhasPendentes.push(`${tc.name}(${tc.arguments?.path || ''}): ${result.error}`);
|
package/lib/desktop.js
CHANGED
|
@@ -407,8 +407,11 @@ function listWindows() {
|
|
|
407
407
|
return m ? `|${m[1]}` : '';
|
|
408
408
|
});
|
|
409
409
|
} else if (TOOLS.xdotool) {
|
|
410
|
+
// O xdotool sai com código ≠ 0 se UMA janela da lista não tiver nome
|
|
411
|
+
// — mas o stdout com as outras continua valendo. Jogar a saída fora
|
|
412
|
+
// por causa do código fazia o total dar 0 com janelas na cara.
|
|
410
413
|
const r = runOut(`xdotool search --onlyvisible --name '.*' getwindowname %@ 2>/dev/null`);
|
|
411
|
-
if (r.
|
|
414
|
+
if (r.out) linhas = r.out.split('\n').map((t) => (t.trim() ? `|${t.trim()}` : ''));
|
|
412
415
|
} else {
|
|
413
416
|
return { ok: false, error: 'Instale wmctrl ou xdotool para listar janelas no Linux.' };
|
|
414
417
|
}
|
|
@@ -464,8 +467,13 @@ Write-Output $p.MainWindowTitle`,
|
|
|
464
467
|
const busca = runOut(`xdotool search --onlyvisible --name ${sh1(t)} 2>/dev/null`);
|
|
465
468
|
const id = (busca.out || '').trim().split('\n')[0];
|
|
466
469
|
if (!id) return naoAchouJanela(t);
|
|
470
|
+
// windowactivate depende do window manager (EWMH). Sem ele — ou com um
|
|
471
|
+
// WM teimoso — raise+focus fazem o mesmo serviço.
|
|
467
472
|
const r = run(`xdotool windowactivate --sync ${id}`);
|
|
468
|
-
|
|
473
|
+
if (r.ok) return { ok: true, janela: t };
|
|
474
|
+
const raise = run(`xdotool windowraise ${id}`);
|
|
475
|
+
const foco = run(`xdotool windowfocus ${id}`);
|
|
476
|
+
return (raise.ok || foco.ok) ? { ok: true, janela: t } : r;
|
|
469
477
|
}
|
|
470
478
|
return { ok: false, error: 'Focar janela não disponível: instale xdotool (Linux).' };
|
|
471
479
|
}
|
package/lib/streaming.js
CHANGED
|
@@ -188,65 +188,74 @@ const MASCOTE_SVG = `<svg viewBox="0 0 9 3.6" aria-hidden="true">
|
|
|
188
188
|
<g fill="#0b1220"><rect x="3" y=".35" width=".8" height=".5" rx=".1"/><rect x="5.2" y=".35" width=".8" height=".5" rx=".1"/><rect x="3.9" y="1.25" width="1.2" height=".28" rx=".14"/></g>
|
|
189
189
|
</svg>`;
|
|
190
190
|
|
|
191
|
+
// A página segue o desenho do dono, traço a traço: fundo grafite, mascote
|
|
192
|
+
// inclinado no canto, "Primocode" branco no alto; a janela de borda branca
|
|
193
|
+
// grossa e canto bem redondo; barra #232323 com as três bolinhas e as abas em
|
|
194
|
+
// texto branco centralizado, separadas por "|"; dentro, a tela preta com a
|
|
195
|
+
// imagem ao vivo. O histórico (miniaturas + passos) fica ABAIXO da janela,
|
|
196
|
+
// discreto — a moldura fica idêntica ao desenho.
|
|
191
197
|
const PAGINA = `<!doctype html>
|
|
192
198
|
<html lang="pt-BR"><head><meta charset="utf-8">
|
|
193
199
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
194
200
|
<title>PrimoCode · Ao vivo</title>
|
|
195
201
|
<style>
|
|
196
202
|
* { box-sizing: border-box; margin: 0; }
|
|
197
|
-
body { background: #
|
|
198
|
-
|
|
199
|
-
/* Cabeçalho: mascote
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
.dot { width: 9px; height: 9px; border-radius: 50%; background: #22d3ee; animation: pulsa 1.4s infinite; }
|
|
203
|
+
body { background: #262626; color: #fff; font-family: 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; min-height: 100vh; display: flex; flex-direction: column; align-items: center; padding: 0 16px 30px; }
|
|
204
|
+
|
|
205
|
+
/* Cabeçalho: mascote inclinado no canto, nome centrado */
|
|
206
|
+
#topo { position: relative; width: min(1100px, 100%); padding: 44px 0 30px; text-align: center; }
|
|
207
|
+
#topo .mascote { position: absolute; left: 6px; top: 30px; width: 96px; height: 40px; transform: rotate(-32deg); }
|
|
208
|
+
#topo .mascote svg { width: 100%; height: 100%; display: block; }
|
|
209
|
+
#topo h1 { font-size: clamp(34px, 6vw, 58px); font-weight: 600; letter-spacing: .5px; }
|
|
210
|
+
#topo .vivo { position: absolute; right: 6px; top: 46px; display: inline-flex; align-items: center; gap: 7px; color: #8a8a8a; font-size: 12px; }
|
|
211
|
+
.dot { width: 9px; height: 9px; border-radius: 50%; background: #46d160; animation: pulsa 1.4s infinite; }
|
|
207
212
|
@keyframes pulsa { 50% { opacity: .25 } }
|
|
208
213
|
|
|
209
|
-
/* A
|
|
210
|
-
#janela { width: min(
|
|
211
|
-
#barra { display: flex; align-items: center;
|
|
212
|
-
#barra .b {
|
|
213
|
-
#barra .b.r {
|
|
214
|
+
/* A janela — borda branca grossa, canto bem redondo */
|
|
215
|
+
#janela { width: min(1100px, 100%); background: #000; border: 10px solid #f5f5f5; border-radius: 46px; overflow: hidden; display: flex; flex-direction: column; }
|
|
216
|
+
#barra { position: relative; display: flex; align-items: center; background: #232323; padding: 14px 26px; min-height: 58px; }
|
|
217
|
+
#barra .b { flex: none; margin-right: 12px; }
|
|
218
|
+
#barra .b.r { width: 17px; height: 17px; border-radius: 50%; background: #d40000; }
|
|
219
|
+
#barra .b.y { width: 30px; height: 17px; border-radius: 50%; background: #ffdd00; }
|
|
220
|
+
#barra .b.g { width: 24px; height: 14px; border-radius: 50%; background: #33cc33; }
|
|
214
221
|
|
|
215
|
-
|
|
222
|
+
/* Abas: texto branco centralizado na barra, separadas por | */
|
|
223
|
+
#abas { position: absolute; left: 50%; transform: translateX(-50%); max-width: 62%; display: flex; align-items: center; gap: 12px; overflow-x: auto; white-space: nowrap; scrollbar-width: none; font-size: clamp(16px, 2.4vw, 24px); font-weight: 500; }
|
|
216
224
|
#abas::-webkit-scrollbar { display: none; }
|
|
217
|
-
.aba { border: 0;
|
|
218
|
-
.aba:hover { color:
|
|
219
|
-
.aba.ativa {
|
|
220
|
-
.
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
#tela
|
|
224
|
-
#
|
|
225
|
-
#
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
#
|
|
230
|
-
|
|
225
|
+
.aba { border: 0; background: none; cursor: pointer; font: inherit; color: rgba(255,255,255,.5); padding: 2px 2px; }
|
|
226
|
+
.aba:hover { color: rgba(255,255,255,.8); }
|
|
227
|
+
.aba.ativa { color: #fff; }
|
|
228
|
+
.sep { color: rgba(255,255,255,.45); font-weight: 400; }
|
|
229
|
+
|
|
230
|
+
/* A tela */
|
|
231
|
+
#tela { position: relative; min-height: 470px; display: flex; align-items: center; justify-content: center; background: #000; }
|
|
232
|
+
#tela img { width: 100%; max-height: 64vh; object-fit: contain; display: none; }
|
|
233
|
+
#aviso { color: #fff; text-align: center; font-size: clamp(20px, 3.4vw, 34px); font-weight: 500; padding: 110px 20px; }
|
|
234
|
+
#carimbo { position: absolute; left: 16px; bottom: 12px; background: rgba(0,0,0,.75); border: 1px solid #2e2e2e; color: #bdbdbd; font-size: 12px; padding: 3px 10px; border-radius: 8px; display: none; max-width: 85%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
235
|
+
|
|
236
|
+
/* Histórico: fora da moldura, discreto, abaixo da janela */
|
|
237
|
+
#historico { width: min(1100px, 100%); padding-top: 14px; }
|
|
238
|
+
#filme { display: flex; gap: 6px; padding: 2px 2px 8px; overflow-x: auto; }
|
|
239
|
+
.foto { flex: none; border: 1px solid #3a3a3a; border-radius: 6px; background: #000; cursor: pointer; padding: 0; width: 84px; height: 50px; overflow: hidden; opacity: .7; }
|
|
231
240
|
.foto img { width: 100%; height: 100%; object-fit: cover; display: block; }
|
|
232
|
-
.foto.ativa { border-color: #
|
|
233
|
-
#passos { max-height:
|
|
234
|
-
.item { padding: 3px
|
|
235
|
-
.item .n { color: #
|
|
236
|
-
.item .ok { color: #
|
|
237
|
-
.item .txt { flex: 1; word-break: break-word; color: #
|
|
238
|
-
.item .txt .erro { color: #
|
|
239
|
-
.item .hora { color: #
|
|
240
|
-
.item.temfoto { cursor: pointer; } .item.temfoto:hover .txt { color: #
|
|
241
|
-
|
|
242
|
-
footer { color: #
|
|
241
|
+
.foto.ativa { border-color: #fff; opacity: 1; }
|
|
242
|
+
#passos { max-height: 170px; overflow-y: auto; }
|
|
243
|
+
.item { padding: 3px 6px; display: flex; gap: 8px; font-size: 13px; }
|
|
244
|
+
.item .n { color: #6f6f6f; min-width: 3ch; text-align: right; }
|
|
245
|
+
.item .ok { color: #46d160; } .item .falha { color: #e5484d; }
|
|
246
|
+
.item .txt { flex: 1; word-break: break-word; color: #d6d6d6; }
|
|
247
|
+
.item .txt .erro { color: #e5484d; font-size: 12px; display: block; }
|
|
248
|
+
.item .hora { color: #6f6f6f; font-size: 11px; }
|
|
249
|
+
.item.temfoto { cursor: pointer; } .item.temfoto:hover .txt { color: #fff; }
|
|
250
|
+
|
|
251
|
+
footer { color: #6f6f6f; font-size: 12px; padding-top: 14px; }
|
|
243
252
|
</style></head>
|
|
244
253
|
<body>
|
|
245
|
-
<
|
|
254
|
+
<div id="topo">
|
|
246
255
|
<div class="mascote">${MASCOTE_SVG}</div>
|
|
247
|
-
<h1
|
|
256
|
+
<h1>Primocode</h1>
|
|
248
257
|
<span class="vivo"><span class="dot"></span><span id="st">conectando…</span></span>
|
|
249
|
-
</
|
|
258
|
+
</div>
|
|
250
259
|
|
|
251
260
|
<div id="janela">
|
|
252
261
|
<div id="barra">
|
|
@@ -255,13 +264,14 @@ const PAGINA = `<!doctype html>
|
|
|
255
264
|
</div>
|
|
256
265
|
<section id="tela">
|
|
257
266
|
<img id="frame" alt="tela que a IA está usando">
|
|
258
|
-
<div id="aviso">Tela que a IA
|
|
267
|
+
<div id="aviso">Tela que a IA está</div>
|
|
259
268
|
<div id="carimbo"></div>
|
|
260
269
|
</section>
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
270
|
+
</div>
|
|
271
|
+
|
|
272
|
+
<div id="historico">
|
|
273
|
+
<div id="filme"></div>
|
|
274
|
+
<div id="passos"></div>
|
|
265
275
|
</div>
|
|
266
276
|
<footer>só nesta máquina (127.0.0.1) · /streaming desliga</footer>
|
|
267
277
|
|
|
@@ -282,14 +292,12 @@ function logDaAba(id) {
|
|
|
282
292
|
}
|
|
283
293
|
|
|
284
294
|
function render() {
|
|
285
|
-
// Abas: "Ao vivo" + uma por app/janela
|
|
295
|
+
// Abas: "Ao vivo" + uma por app/janela, texto separado por |
|
|
286
296
|
const nav = document.getElementById('abas');
|
|
287
297
|
const abas = [{ id: VIVO, nome: 'Ao vivo' }, ...dados.abas];
|
|
288
298
|
nav.innerHTML = abas.map((a) =>
|
|
289
|
-
'<button class="aba' + (a.id === abaSel ? ' ativa' : '') + '" data-id="' + a.id + '">'
|
|
290
|
-
|
|
291
|
-
+ (a.passos ? '<span class="qtd">' + a.passos + '</span>' : '')
|
|
292
|
-
+ '</button>').join('');
|
|
299
|
+
'<button class="aba' + (a.id === abaSel ? ' ativa' : '') + '" data-id="' + a.id + '">' + esc(a.nome) + '</button>'
|
|
300
|
+
).join('<span class="sep">|</span>');
|
|
293
301
|
nav.querySelectorAll('.aba').forEach((b) => b.onclick = () => { abaSel = Number(b.dataset.id); fotoSel = null; render(); });
|
|
294
302
|
|
|
295
303
|
// Imagem: a fixada no filmstrip, senão a mais nova da aba
|
|
@@ -306,7 +314,7 @@ function render() {
|
|
|
306
314
|
carimbo.textContent = '#' + atual.id + ' · ' + atual.hora + ' · ' + atual.acao + (fotoSel != null ? ' (clique na miniatura de novo para voltar ao mais recente)' : '');
|
|
307
315
|
} else {
|
|
308
316
|
img.style.display = 'none'; aviso.style.display = 'block'; carimbo.style.display = 'none';
|
|
309
|
-
if (dados.erroFoto) aviso.
|
|
317
|
+
if (dados.erroFoto) aviso.textContent = 'Sem foto da tela: ' + dados.erroFoto;
|
|
310
318
|
}
|
|
311
319
|
|
|
312
320
|
// Filmstrip da aba (as últimas 14)
|
|
@@ -336,7 +344,7 @@ async function tick() {
|
|
|
336
344
|
try {
|
|
337
345
|
const s = await (await fetch('/state')).json();
|
|
338
346
|
dados = s;
|
|
339
|
-
document.getElementById('st').textContent = 'ao vivo
|
|
347
|
+
document.getElementById('st').textContent = 'ao vivo';
|
|
340
348
|
const assinatura = JSON.stringify([s.frames.length, s.log.length, s.abas.length, s.erroFoto]);
|
|
341
349
|
if (assinatura !== ultimoEstado) { ultimoEstado = assinatura; render(); }
|
|
342
350
|
} catch (e) {
|
|
@@ -348,6 +356,7 @@ tick();
|
|
|
348
356
|
</script>
|
|
349
357
|
</body></html>`;
|
|
350
358
|
|
|
359
|
+
|
|
351
360
|
function start() {
|
|
352
361
|
if (state.on) return Promise.resolve(state.url);
|
|
353
362
|
try { fs.mkdirSync(DIR, { recursive: true }); } catch {}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "primocode",
|
|
3
|
-
"version": "7.12.
|
|
3
|
+
"version": "7.12.2",
|
|
4
4
|
"description": "PrimoCode — agente de engenharia com IA e cursor próprio. Modelos grátis. 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": {
|