codetac 0.1.0 → 0.1.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/package.json +1 -1
- package/readme.md +53 -11
- package/src/ai.mjs +1 -1
- package/src/browser/bar.js +2 -2
- package/src/cli.mjs +8 -8
- package/src/diagnose.mjs +1 -1
- package/src/digest.mjs +1 -1
- package/src/panel.mjs +1 -1
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -4,7 +4,13 @@
|
|
|
4
4
|
|
|
5
5
|
*See what your app's code does, one click at a time: a local tool for Node.js apps, including those generated by AI tools (Lovable, Bolt, v0, Cursor, Replit, Claude Code…). Explanations are in Portuguese by default (`CODETAC_LANG=en` for English).*
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+

|
|
8
|
+
|
|
9
|
+
## Para quê
|
|
10
|
+
|
|
11
|
+
A sua app foi feita por uma IA, ou por outra pessoa, e não sabe ao certo o que acontece quando carrega em «Guardar»? Que funções correm, que tabela é alterada, se sai um email, se o pagamento está em modo de teste ou de produção?
|
|
12
|
+
|
|
13
|
+
Carregue num botão da sua app e o codeTAC mostra um **dossier** dessa ação:
|
|
8
14
|
- **no browser:** o que foi clicado, o componente e onde está no código;
|
|
9
15
|
- **no servidor:** as funções do seu projeto que correram, pela ordem, com o ficheiro e a linha;
|
|
10
16
|
- **as fronteiras:** base de dados, serviços externos, email, pagamentos, ficheiros, IA;
|
|
@@ -12,8 +18,45 @@ Clica num botão da sua app e o codeTAC mostra um **dossiê** dessa ação:
|
|
|
12
18
|
- **os efeitos permanentes:** linhas gravadas, emails enviados, cookies…;
|
|
13
19
|
- uma frase simples em cada passo a dizer para que serve.
|
|
14
20
|
|
|
21
|
+
Em qualquer passo, pode abrir o código dessa função e fazer uma pergunta sobre ele.
|
|
22
|
+
|
|
15
23
|
Tudo corre no seu computador. O código da app não é alterado.
|
|
16
24
|
|
|
25
|
+
## Para que apps serve
|
|
26
|
+
|
|
27
|
+
**Apps web em Node.js (JavaScript ou TypeScript), a correr no seu computador.** Incluindo as feitas com Lovable, Bolt, v0, Cursor, Replit ou Claude Code.
|
|
28
|
+
|
|
29
|
+
**Ensaiado com:**
|
|
30
|
+
- Next.js (incluindo a versão 16);
|
|
31
|
+
- Vite + React;
|
|
32
|
+
- Express;
|
|
33
|
+
- TanStack Start;
|
|
34
|
+
- projetos com frontend e backend separados, e monorepos (npm, pnpm, yarn).
|
|
35
|
+
|
|
36
|
+
Outras apps Node devem funcionar (Nuxt, SvelteKit, Astro, Remix, NestJS, Fastify…), mas ainda não foram ensaiadas. Se algo falhar, veja a [secção 6](#6-quando-algo-não-funciona).
|
|
37
|
+
|
|
38
|
+
**Mostra à parte quando a app usa:**
|
|
39
|
+
|
|
40
|
+
| | |
|
|
41
|
+
| --- | --- |
|
|
42
|
+
| Base de dados | Postgres (`pg`), MySQL (`mysql2`), SQLite (`better-sqlite3`, `node:sqlite`), Supabase (com a tabela e a operação) |
|
|
43
|
+
| IA | OpenAI, Anthropic, Google, Mistral, Groq, OpenRouter, DeepSeek, Cohere, Together, modelos locais |
|
|
44
|
+
| Email e mensagens | nodemailer, Resend, SendGrid, Postmark, Mailgun, Brevo, Twilio |
|
|
45
|
+
| Pagamentos | Stripe, com a indicação de modo de teste ou de produção |
|
|
46
|
+
| Autenticação | NextAuth, Supabase Auth, Clerk |
|
|
47
|
+
| Ficheiros | disco do computador, S3, Supabase Storage |
|
|
48
|
+
| Outros | qualquer pedido do servidor a um serviço externo, cookies |
|
|
49
|
+
|
|
50
|
+
Os serviços chamados diretamente do browser (por exemplo o Supabase, em muitas apps do Lovable e do Bolt) aparecem como pedidos do browser, sem o passo a passo do servidor.
|
|
51
|
+
|
|
52
|
+
**Ainda não serve para:**
|
|
53
|
+
- apps em Python, PHP, Go ou Ruby (FastAPI, Flask, Django, Laravel…);
|
|
54
|
+
- sites só com HTML e JavaScript, sem um servidor Node;
|
|
55
|
+
- partes que correm em Bun, Deno, Cloudflare Workers ou no middleware do Next.js;
|
|
56
|
+
- apps em produção ou que só existem na nuvem.
|
|
57
|
+
|
|
58
|
+
Precisa do Node.js 24 ou mais recente. Ensaiado em macOS; o Windows ainda não foi ensaiado.
|
|
59
|
+
|
|
17
60
|
---
|
|
18
61
|
|
|
19
62
|
## 1. Antes de começar
|
|
@@ -42,7 +85,7 @@ Confirme com `codetac --version`, que mostra o número da versão.
|
|
|
42
85
|
|
|
43
86
|
Se aparecer um erro de permissões (`EACCES`), use `npx codetac` em vez de `codetac` nos passos abaixo. Funciona sem instalar.
|
|
44
87
|
|
|
45
|
-
## 3. O primeiro
|
|
88
|
+
## 3. O primeiro dossier
|
|
46
89
|
|
|
47
90
|
1. No Terminal, vá para a pasta do projeto: escreva `cd ` (com um espaço), arraste a pasta do projeto para a janela do Terminal e carregue em Enter.
|
|
48
91
|
2. Escreva:
|
|
@@ -57,13 +100,13 @@ Se aparecer um erro de permissões (`EACCES`), use `npx codetac` em vez de `code
|
|
|
57
100
|
4. Quando aparecer **«✓ App pronta em http://localhost:…»**, o browser abre com a sua app.
|
|
58
101
|
5. **Use a app:** clique num botão, numa ligação, envie um formulário.
|
|
59
102
|
- No canto inferior direito da página aparece uma pequena barra com «Gravada: …».
|
|
60
|
-
- No Terminal aparece «✓ Primeiro
|
|
61
|
-
6. Carregue na barra, ou abra o link, para ver o
|
|
103
|
+
- No Terminal aparece «✓ Primeiro dossier …» com um link.
|
|
104
|
+
6. Carregue na barra, ou abra o link, para ver o dossier.
|
|
62
105
|
7. Para terminar, volte ao Terminal e carregue em **Ctrl+C**.
|
|
63
106
|
|
|
64
|
-
Numa app que já conhece, o primeiro
|
|
107
|
+
Numa app que já conhece, o primeiro dossier chega normalmente em menos de um minuto.
|
|
65
108
|
|
|
66
|
-
## 4. Ler o
|
|
109
|
+
## 4. Ler o dossier
|
|
67
110
|
|
|
68
111
|
- **A frase do topo** resume a ação, por exemplo: «Clique em botão «Add» → POST /api/items (estado 201) → altera o ecrã».
|
|
69
112
|
- **browser → servidor:** cada pedido que a ação fez ao servidor. Por baixo estão as funções do seu projeto que correram.
|
|
@@ -108,7 +151,7 @@ Cada linha começa por **✓** (funciona), **!** (atenção) ou **✗** (problem
|
|
|
108
151
|
| «Não encontrei um package.json…» | Está na pasta errada. Vá para a pasta que tem o ficheiro `package.json`, ou indique o arranque: `codetac -- node server.js` |
|
|
109
152
|
| «A porta 3000 já está ocupada por outro programa…» | Feche o outro programa, por exemplo outra app aberta noutra janela do Terminal. Numa app com uma só parte, o codeTAC muda de porta sozinho |
|
|
110
153
|
| «A app falhou também em modo mínimo» | O problema é da própria app. Muitas vezes faltam as chaves no ficheiro `.env` (Supabase, Firebase…). Veja o `.env.example` ou as instruções do projeto |
|
|
111
|
-
| «A página inicial respondeu com erro 500» | A app arrancou, mas falha. O
|
|
154
|
+
| «A página inicial respondeu com erro 500» | A app arrancou, mas falha. O dossier desse carregamento mostra onde |
|
|
112
155
|
| A barra não aparece na página | Recarregue a página. Veja se o endereço é o que o codeTAC indicou |
|
|
113
156
|
|
|
114
157
|
**Comunicar um problema:**
|
|
@@ -149,11 +192,10 @@ Guarda o diagnóstico num ficheiro, sem código nem dados da app. Envie-o com um
|
|
|
149
192
|
|
|
150
193
|
## 9. Limites
|
|
151
194
|
|
|
152
|
-
-
|
|
153
|
-
- Não serve para apps em produção
|
|
154
|
-
-
|
|
195
|
+
- Os tipos de app que servem e os que não servem estão em [Para que apps serve](#para-que-apps-serve).
|
|
196
|
+
- É para usar em desenvolvimento, no seu computador. Não serve para apps em produção.
|
|
197
|
+
- As partes que não correm no Node não são observadas por dentro. O comando avisa quando as reconhece.
|
|
155
198
|
- No browser, mostra-se o elemento, o componente e o caminho até cada pedido, mas não cada função.
|
|
156
|
-
- Windows: ainda não ensaiado.
|
|
157
199
|
|
|
158
200
|
## Licença
|
|
159
201
|
|
package/src/ai.mjs
CHANGED
|
@@ -295,7 +295,7 @@ export async function answerQuestion({ config, dossier, stepId, question, readCo
|
|
|
295
295
|
let step = null;
|
|
296
296
|
let parent = null;
|
|
297
297
|
walk(dossier.digest.nodes, (node, holder) => { if (node.id === stepId) { step = node; parent = holder; } });
|
|
298
|
-
if (!step) return { available: true, known: false, text: 'Este passo não foi encontrado no
|
|
298
|
+
if (!step) return { available: true, known: false, text: 'Este passo não foi encontrado no dossier.' };
|
|
299
299
|
const target = step.type === 'function' ? step : parent?.type === 'function' ? parent : null;
|
|
300
300
|
const code = target ? readCode(dossier.request.run, target.file, target.line, target.endLine) : null;
|
|
301
301
|
const facts = target ? subtree(target) : { boundaries: [step], functions: [] };
|
package/src/browser/bar.js
CHANGED
|
@@ -521,7 +521,7 @@
|
|
|
521
521
|
'.top{display:flex;gap:6px;align-items:center;padding:6px 8px;background:#18181b;color:#f4f4f5}.top b{margin-right:auto}' +
|
|
522
522
|
'.top button,.top a{all:unset;cursor:pointer;padding:2px 8px;border-radius:5px;color:#e4e4e7}.top button:hover,.top a:hover{background:#3f3f46}' +
|
|
523
523
|
'iframe{border:0;flex:1;width:100%}.msg{padding:16px;color:#27272a}</style>' +
|
|
524
|
-
'<button class="pill" part="pill" title="CodeTAC: carregue para ver o
|
|
524
|
+
'<button class="pill" part="pill" title="CodeTAC: carregue para ver o dossier da última ação"><span class="dot"></span><span class="label">CodeTAC</span></button>';
|
|
525
525
|
shadow.querySelector('.pill').addEventListener('click', () => toggleSheet());
|
|
526
526
|
document.documentElement.appendChild(host);
|
|
527
527
|
updateBar(false);
|
|
@@ -562,7 +562,7 @@
|
|
|
562
562
|
}
|
|
563
563
|
shown.innerHTML = '<div class="top"><b></b><button data-go="-1" title="Ação anterior">◀</button><button data-go="1" title="Ação seguinte">▶</button>' +
|
|
564
564
|
'<a target="_blank" rel="noopener" title="Abrir no painel">painel ↗</a><button data-close title="Fechar">✕</button></div>' +
|
|
565
|
-
'<iframe title="
|
|
565
|
+
'<iframe title="Dossier da ação"></iframe>';
|
|
566
566
|
shown.querySelector('b').textContent = (index + 1) + '/' + recorded.length + ' · ' + item.label;
|
|
567
567
|
shown.querySelector('a').href = panel + '/?action=' + encodeURIComponent(item.id);
|
|
568
568
|
shown.querySelector('iframe').src = url;
|
package/src/cli.mjs
CHANGED
|
@@ -491,13 +491,13 @@ async function main() {
|
|
|
491
491
|
const appUrl = found.url ?? `http://localhost:${found.port}/`;
|
|
492
492
|
say('');
|
|
493
493
|
say(`✓ App pronta em ${appUrl} (${seconds()})${minimal ? ' · modo mínimo' : ''}`);
|
|
494
|
-
if (found.status >= 500) say(`! A página inicial respondeu com erro ${found.status}. Veja as mensagens da app acima (faltam variáveis de ambiente?). O
|
|
494
|
+
if (found.status >= 500) say(`! A página inicial respondeu com erro ${found.status}. Veja as mensagens da app acima (faltam variáveis de ambiente?). O dossier desse pedido mostra onde falhou.`);
|
|
495
495
|
if (found.page) {
|
|
496
496
|
say(' Abra-a no browser e use-a: a barra do CodeTAC aparece no canto inferior direito.');
|
|
497
|
-
say(' Cada ação (clique, formulário…) fica com um
|
|
497
|
+
say(' Cada ação (clique, formulário…) fica com um dossier; a barra abre-o.');
|
|
498
498
|
if (!options.noOpen && interactive) openBrowser(appUrl);
|
|
499
499
|
} else {
|
|
500
|
-
say(' Esta app responde sem páginas HTML (uma API). Faça pedidos como de costume; cada pedido fica com um
|
|
500
|
+
say(' Esta app responde sem páginas HTML (uma API). Faça pedidos como de costume; cada pedido fica com um dossier no painel.');
|
|
501
501
|
}
|
|
502
502
|
say(` Painel: ${panelUrl} · Terminar: Ctrl+C`);
|
|
503
503
|
say('');
|
|
@@ -511,23 +511,23 @@ async function main() {
|
|
|
511
511
|
if (seenActions.has(id)) continue;
|
|
512
512
|
seenActions.add(id);
|
|
513
513
|
const link = `${panelUrl}/?action=${encodeURIComponent(id)}`;
|
|
514
|
-
if (!firstShown) { firstShown = true; say(`✓ Primeiro
|
|
514
|
+
if (!firstShown) { firstShown = true; say(`✓ Primeiro dossier (${seconds()} desde o comando): ${link}`); }
|
|
515
515
|
else say(`• Ação gravada: ${link}`);
|
|
516
516
|
}
|
|
517
517
|
// A page that loads but where nothing is clicked yet (or that fails to
|
|
518
518
|
// render): the dossier of its own load.
|
|
519
519
|
if (!firstShown && summary.firstPage && Date.now() - summary.firstPage.seenAt > 8000) {
|
|
520
520
|
firstShown = true;
|
|
521
|
-
say(`✓ Primeiro
|
|
522
|
-
say(' Cada clique na app terá o seu próprio
|
|
521
|
+
say(`✓ Primeiro dossier (${seconds()} desde o comando), o do carregamento da página: ${panelUrl}/?request=${encodeURIComponent(summary.firstPage.requestId)}`);
|
|
522
|
+
say(' Cada clique na app terá o seu próprio dossier.');
|
|
523
523
|
}
|
|
524
524
|
if (!firstShown && summary.firstRequest && !found.page) {
|
|
525
525
|
firstShown = true;
|
|
526
|
-
say(`✓ Primeiro
|
|
526
|
+
say(`✓ Primeiro dossier (${seconds()} desde o comando): ${panelUrl}/?request=${encodeURIComponent(summary.firstRequest.requestId)}`);
|
|
527
527
|
}
|
|
528
528
|
if (!silentWarned && !minimal && summary.requests >= 3 && !summary.withFunctions.size && summary.functions === 0) {
|
|
529
529
|
silentWarned = true;
|
|
530
|
-
say('! Já chegaram pedidos, mas nenhuma função do projeto foi observada. Os
|
|
530
|
+
say('! Já chegaram pedidos, mas nenhuma função do projeto foi observada. Os dossiers mostram pedidos e fronteiras.');
|
|
531
531
|
say(' «codetac diagnostico» explica porquê.');
|
|
532
532
|
}
|
|
533
533
|
// A part that fails later with a taken port: its requests could reach the
|
package/src/diagnose.mjs
CHANGED
|
@@ -94,7 +94,7 @@ export async function diagnose(root, { panelPort = 4000, out = text => process.s
|
|
|
94
94
|
if (!minimal) {
|
|
95
95
|
if (summary.files) ok(`Ficheiros do projeto preparados: ${summary.files} (${summary.functions} funções).`);
|
|
96
96
|
else if (summary.requests) bad('Nenhum ficheiro do projeto passou pelo CodeTAC.',
|
|
97
|
-
'O servidor pode estar a correr código já empacotado sem source map, ou fora da pasta do projeto. Os
|
|
97
|
+
'O servidor pode estar a correr código já empacotado sem source map, ou fora da pasta do projeto. Os dossiers mostram só pedidos e fronteiras.');
|
|
98
98
|
const failed = summary.failed.length;
|
|
99
99
|
if (failed) {
|
|
100
100
|
note(`${failed} ficheiro(s) correm sem ser seguidos (não foi possível prepará-los):`);
|
package/src/digest.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
// Vista agrupada do
|
|
1
|
+
// Vista agrupada do dossier (Fase 3): árvore de passos com o ruído agrupado,
|
|
2
2
|
// uma frase de finalidade por função e por fronteira, e o resumo dos efeitos
|
|
3
3
|
// permanentes. Tudo deriva dos factos gravados; nada é removido, só agrupado
|
|
4
4
|
// (cada grupo guarda os passos originais para a expansão).
|
package/src/panel.mjs
CHANGED
|
@@ -202,7 +202,7 @@ const page = `<!doctype html>
|
|
|
202
202
|
<head>
|
|
203
203
|
<meta charset="utf-8">
|
|
204
204
|
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
205
|
-
<title>CodeTAC —
|
|
205
|
+
<title>CodeTAC — dossiers</title>
|
|
206
206
|
<style>
|
|
207
207
|
:root { --bg:#f7f7f5; --panel:#fff; --text:#1d1d1b; --muted:#6b6b66; --line:#e4e3de; --accent:#2f5bd3;
|
|
208
208
|
--db:#0f7b5f; --http:#6a4bc4; --ia:#b4531f; --mail:#1f73b4; --pay:#9b2c86; --file:#7a6a12; --auth:#3d6b2f; --error:#c0392b; --code:#f1f0ec;
|