codetac 0.1.1 → 0.2.0

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "codetac",
3
- "version": "0.1.1",
4
- "description": "codeTAC — perceber o que o código da sua app Node faz, ação a ação: do clique no browser às funções do servidor, base de dados e serviços externos.",
3
+ "version": "0.2.0",
4
+ "description": "codeTAC — perceber o que o código da sua app Node ou Python faz, ação a ação: do clique no browser às funções do servidor, base de dados e serviços externos.",
5
5
  "keywords": [
6
6
  "debug",
7
7
  "trace",
@@ -10,6 +10,9 @@
10
10
  "nextjs",
11
11
  "vite",
12
12
  "express",
13
+ "python",
14
+ "fastapi",
15
+ "flask",
13
16
  "ai-generated",
14
17
  "vibe-coding",
15
18
  "dossier"
@@ -29,7 +32,9 @@
29
32
  "codetac": "src/cli.mjs"
30
33
  },
31
34
  "files": [
32
- "src/"
35
+ "src/",
36
+ "!**/__pycache__/",
37
+ "!**/*.pyc"
33
38
  ],
34
39
  "engines": {
35
40
  "node": ">=24.0.0"
@@ -37,10 +42,10 @@
37
42
  "scripts": {
38
43
  "test": "node --test test/*.test.mjs",
39
44
  "test:stacks": "node scripts/validate-stacks.mjs",
40
- "check": "node --check src/register.mjs && node --check src/transform.mjs && node --check src/runtime.mjs && node --check src/boundaries.mjs && node --check src/store.mjs && node --check src/panel.mjs && node --check src/page.mjs && node --check src/origins.mjs && node --check src/action-view.mjs && node --check src/browser/bar.js && node --check src/digest.mjs && node --check src/sentences.mjs && node --check src/ai.mjs && node --check src/cli.mjs && node --check src/detect.mjs && node --check src/recording.mjs && node --check src/diagnose.mjs",
45
+ "check": "node --check src/register.mjs && node --check src/transform.mjs && node --check src/runtime.mjs && node --check src/boundaries.mjs && node --check src/store.mjs && node --check src/panel.mjs && node --check src/page.mjs && node --check src/origins.mjs && node --check src/action-view.mjs && node --check src/browser/bar.js && node --check src/digest.mjs && node --check src/sentences.mjs && node --check src/ai.mjs && node --check src/cli.mjs && node --check src/detect.mjs && node --check src/detect-python.mjs && node --check src/recording.mjs && node --check src/diagnose.mjs",
41
46
  "test:performance": "node scripts/benchmark-functions.mjs",
42
47
  "panel": "node src/panel.mjs",
43
- "test:browser": "node scripts/accept-browser.mjs docs/aceitacao-browser-exemplo.json",
48
+ "test:browser": "node scripts/accept-browser.mjs scripts/ensaios/browser-exemplo.json",
44
49
  "codetac": "node src/cli.mjs"
45
50
  },
46
51
  "dependencies": {
package/readme.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  **Perceber o que o código da sua app faz, ação a ação.**
4
4
 
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).*
5
+ *See what your app's code does, one click at a time: a local tool for Node.js and Python (FastAPI, Flask) 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
  ![Dossier de um clique no botão «Sign in»: as funções do servidor que correram, com o ficheiro e a linha, e as leituras da base de dados](https://raw.githubusercontent.com/deltaxmodules/codetacvibe/main/.github/imagens/dossier.png)
8
8
 
@@ -24,9 +24,9 @@ Tudo corre no seu computador. O código da app não é alterado.
24
24
 
25
25
  ## Para que apps serve
26
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.
27
+ **Apps web em Node.js (JavaScript ou TypeScript) ou em Python (FastAPI ou Flask), a correr no seu computador.** Incluindo as feitas com Lovable, Bolt, v0, Cursor, Replit ou Claude Code.
28
28
 
29
- **Ensaiado com:**
29
+ **Node.js — ensaiado com:**
30
30
  - Next.js (incluindo a versão 16);
31
31
  - Vite + React;
32
32
  - Express;
@@ -35,11 +35,19 @@ Tudo corre no seu computador. O código da app não é alterado.
35
35
 
36
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
37
 
38
+ **Python — ensaiado com:**
39
+ - FastAPI (funções `async def` e `def`), com o uvicorn, o `fastapi dev` ou o gunicorn;
40
+ - Flask, com o `flask run`, o `app.run()`, o gunicorn ou o waitress, incluindo os templates Jinja;
41
+ - um frontend Node (por exemplo Vite + React) com uma API Python na mesma pasta ou numa subpasta (`backend/`, `api/`…): arrancam juntos, e o dossier segue o clique do browser até às funções Python;
42
+ - projetos gerados pelo Claude Code, Replit Agent, Lovable, Codex e Cursor.
43
+
44
+ Para seguir as funções da app, precisa do **Python 3.12 ou mais recente**. Com o Python 3.8 a 3.11, o codeTAC funciona em **modo mínimo**: mostra os pedidos, as fronteiras e o browser, mas não as funções. Com o hypercorn ou o granian, a app corre, mas as ações não ficam com dossier (o comando avisa e sugere o uvicorn).
45
+
38
46
  **Mostra à parte quando a app usa:**
39
47
 
40
48
  | | |
41
49
  | --- | --- |
42
- | Base de dados | Postgres (`pg`), MySQL (`mysql2`), SQLite (`better-sqlite3`, `node:sqlite`), Supabase (com a tabela e a operação) |
50
+ | Base de dados | Postgres (`pg`), MySQL (`mysql2`), SQLite (`better-sqlite3`, `node:sqlite`), Supabase (com a tabela e a operação). Em Python: `sqlite3`, `psycopg2`, `psycopg`, `asyncpg`, `pymysql` e o SQLAlchemy |
43
51
  | IA | OpenAI, Anthropic, Google, Mistral, Groq, OpenRouter, DeepSeek, Cohere, Together, modelos locais |
44
52
  | Email e mensagens | nodemailer, Resend, SendGrid, Postmark, Mailgun, Brevo, Twilio |
45
53
  | Pagamentos | Stripe, com a indicação de modo de teste ou de produção |
@@ -50,12 +58,12 @@ Outras apps Node devem funcionar (Nuxt, SvelteKit, Astro, Remix, NestJS, Fastify
50
58
  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
59
 
52
60
  **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;
61
+ - Django, nem apps em PHP, Go ou Ruby (Laravel, Rails…);
62
+ - sites só com HTML e JavaScript, sem um servidor Node ou Python;
55
63
  - partes que correm em Bun, Deno, Cloudflare Workers ou no middleware do Next.js;
56
64
  - apps em produção ou que só existem na nuvem.
57
65
 
58
- Precisa do Node.js 24 ou mais recente. Ensaiado em macOS; o Windows ainda não foi ensaiado.
66
+ Precisa do Node.js 24 ou mais recente, também nas apps Python (o codeTAC é instalado pelo npm). Ensaiado em macOS; o Windows ainda não foi ensaiado.
59
67
 
60
68
  ---
61
69
 
@@ -68,6 +76,7 @@ Precisa de três coisas:
68
76
  - no GitHub, **Code → Download ZIP**, e descompacte o ficheiro;
69
77
  - ou `git clone <endereço>`, se usa o Git.
70
78
  3. **Um browser:** Chrome, Edge, Firefox ou Safari.
79
+ 4. **Só nas apps Python: o Python 3.12 ou mais recente.** Para verificar, escreva `python3 --version`. Se não tiver, instale-o em [python.org](https://www.python.org/downloads/) (no macOS, também `brew install python`). Se tiver o [uv](https://docs.astral.sh/uv/), o codeTAC usa-o, e ele descarrega o Python que faltar.
71
80
 
72
81
  **Como abrir o Terminal:**
73
82
  - **macOS:** Cmd+Espaço, escreva «Terminal», Enter;
@@ -96,6 +105,8 @@ Se aparecer um erro de permissões (`EACCES`), use `npx codetac` em vez de `code
96
105
 
97
106
  3. O codeTAC diz o que vai arrancar, por exemplo `Arranque: Vite · npm run dev`.
98
107
  - Se faltarem as dependências da app, pergunta se as instala. Carregue em **Enter**, que quer dizer sim.
108
+ - Numa app Python sem ambiente, pergunta se cria o `.venv` na pasta do projeto e instala as dependências (`requirements.txt`, `pyproject.toml` ou `uv.lock`). **Enter** quer dizer sim.
109
+ - Se o projeto tiver um `.env.example` e não um `.env`, avisa: muitas apps precisam dessa configuração (copie o ficheiro para `.env` e preencha-o).
99
110
  - Se o projeto tiver várias partes, pergunta quais arranca. **Enter** arranca todas.
100
111
  4. Quando aparecer **«✓ App pronta em http://localhost:…»**, o browser abre com a sua app.
101
112
  5. **Use a app:** clique num botão, numa ligação, envie um formulário.
@@ -148,10 +159,13 @@ Cada linha começa por **✓** (funciona), **!** (atenção) ou **✗** (problem
148
159
 
149
160
  | Mensagem | O que fazer |
150
161
  | --- | --- |
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` |
162
+ | «Não encontrei um package.json…» | Está na pasta errada. Vá para a pasta que tem o ficheiro `package.json` (ou, numa app Python, o `requirements.txt`, o `pyproject.toml` ou o `main.py`/`app.py`), ou indique o arranque: `codetac -- node server.js`, `codetac -- uvicorn main:app` |
152
163
  | «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 |
153
164
  | «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 |
154
165
  | «A página inicial respondeu com erro 500» | A app arrancou, mas falha. O dossier desse carregamento mostra onde |
166
+ | «…ocupada pelo Recetor AirPlay do macOS» | A porta 5000 do Flask é do AirPlay no macOS. O codeTAC arranca a app noutra porta; para usar a 5000, desligue o «Recetor AirPlay» em Definições do Sistema › Geral › AirDrop e Handoff |
167
+ | «Modo mínimo: Python …» | A app corre num Python anterior ao 3.12. Crie o `.venv` com um Python mais recente (apague o `.venv` e corra `codetac` outra vez) |
168
+ | «O hypercorn (ou o granian) ainda não é reconhecido» | Arranque a app com o uvicorn, por exemplo `codetac -- uvicorn main:app` |
155
169
  | A barra não aparece na página | Recarregue a página. Veja se o endereço é o que o codeTAC indicou |
156
170
 
157
171
  **Comunicar um problema:**
@@ -183,20 +197,31 @@ Guarda o diagnóstico num ficheiro, sem código nem dados da app. Envie-o com um
183
197
  | Opção | Para quê |
184
198
  | --- | --- |
185
199
  | `--script <nome>` | usar outro script do `package.json` |
200
+ | `--parte <pasta>` | num projeto com várias partes, qual arrancar (pode repetir) |
186
201
  | `--porta <n>` | a porta da app, se não for descoberta sozinha |
187
202
  | `--painel <n>` | a porta do painel (por omissão 4000) |
188
203
  | `--minimo` | não seguir as funções do projeto |
189
204
  | `--sim` | responder «sim» às perguntas |
190
205
  | `--nao-abrir` | não abrir o browser |
191
- | `-- <comando>` | o comando de arranque, por exemplo `codetac -- node server.js` |
206
+ | `-- <comando>` | o comando de arranque, por exemplo `codetac -- node server.js` ou `codetac -- uvicorn main:app --reload` |
192
207
 
193
208
  ## 9. Limites
194
209
 
195
210
  - Os tipos de app que servem e os que não servem estão em [Para que apps serve](#para-que-apps-serve).
196
211
  - É 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.
212
+ - As partes que não correm no Node nem no Python não são observadas por dentro. O comando avisa quando as reconhece.
198
213
  - No browser, mostra-se o elemento, o componente e o caminho até cada pedido, mas não cada função.
199
214
 
215
+ **Nas apps Python:**
216
+ - As funções só são seguidas com o Python 3.12 ou mais recente. Abaixo, modo mínimo: pedidos, fronteiras e browser.
217
+ - A app é encontrada quando o `FastAPI(...)` ou o `Flask(...)` (ou uma fábrica `create_app`) está num ficheiro `.py` até dois níveis de pastas abaixo da pasta indicada. Noutros casos, indique o arranque com `codetac -- <comando>`.
218
+ - A configuração da app (`.env`, base de dados, Redis…) é sua: sem ela, o codeTAC mostra o erro da própria app.
219
+ - As threads criadas à mão (`threading.Thread`) não ficam ligadas ao pedido que as criou; as do FastAPI e do `asyncio` ficam.
220
+ - O Redis, o MongoDB e o Celery ainda não aparecem como fronteiras: o tempo passado neles fica dentro da função que os chamou.
221
+ - As páginas enviadas aos pedaços (stream) não recebem a barra; a ação continua a ser gravada.
222
+ - Com muitas consultas por página, o servidor fica mais lento: numa página com 52 consultas SQL, passou de 1,0 para 2,8 ms. Nas outras apps ensaiadas, o tempo ficou entre 1 e 1,3 vezes o normal.
223
+ - MySQL pelo `mysqlclient` e o Django ainda não foram ensaiados.
224
+
200
225
  ## Licença
201
226
 
202
227
  [MIT](LICENSE).
package/src/ai.mjs CHANGED
@@ -326,7 +326,9 @@ export async function answerQuestion({ config, dossier, stepId, question, readCo
326
326
  const allowedWords = wordsOf(code?.lines.join('\n'), question, ...lines);
327
327
  // Answers may talk about other cases of the code, so conditionals are allowed here.
328
328
  const rejected = validateSentence(text, { boundaries: facts.boundaries, allowedWords, knownTables, knownHosts, maxLength: 1500, conditionals: true });
329
- return { available: true, known: Boolean(result?.sabe), text, rejected, model: config.model, local: Boolean(config.local), valuesSent: Boolean(detail && withValues) };
329
+ // With a model outside this machine, the answer says the recorded values were not sent.
330
+ return { available: true, known: Boolean(result?.sabe), text, rejected, model: config.model, local: Boolean(config.local),
331
+ valuesSent: Boolean(detail && withValues), valuesWithheld: Boolean(detail && !withValues) };
330
332
  }
331
333
 
332
334
  export function createPurposes({ config, cache, readCode, redact = createRedactor(), limit = Number(process.env.CODETAC_AI_LIMIT || 40) }) {
@@ -89,6 +89,18 @@ export function classifyHttp({ method, url, headers, body, client }) {
89
89
  return { ...base, kind: 'ia', provider: AI[host] ?? (local ? 'modelo local' : `compatível (${host})`), operation: path,
90
90
  local: local || undefined, model: typeof json?.model === 'string' ? json.model : undefined };
91
91
  }
92
+ // S3-compatible storage: signed header, or a presigned URL (signature in
93
+ // the query: SigV4, or SigV2, which boto3 still makes by default). Before
94
+ // the local case: a local MinIO or LocalStack is S3.
95
+ if (/^AWS4-HMAC-SHA256/.test(String(headers.authorization ?? '')) || headers['x-amz-content-sha256']
96
+ || queryKeys.some(key => /^x-amz-(signature|algorithm)$/i.test(key))
97
+ || (queryKeys.includes('AWSAccessKeyId') && queryKeys.includes('Signature'))) {
98
+ const virtualHost = host.match(/^(.+?)\.s3[.-]/);
99
+ const bucket = virtualHost ? virtualHost[1] : parsed.pathname.split('/')[1];
100
+ const operation = { GET: 'leitura', HEAD: 'verificação', PUT: 'escrita', POST: 'escrita', DELETE: 'remoção' }[method] ?? method;
101
+ const size = Number(headers['content-length']);
102
+ return { ...base, kind: 'ficheiros', provider: 'S3', operation, bucket, bytes: Number.isFinite(size) ? size : undefined };
103
+ }
92
104
  if (local) return { ...base, local: true };
93
105
  if (host === 'api.stripe.com') {
94
106
  const auth = String(headers.authorization ?? '');
@@ -115,15 +127,6 @@ export function classifyHttp({ method, url, headers, body, client }) {
115
127
  if (area === 'storage') return { ...base, kind: 'ficheiros', provider: 'Supabase Storage', operation: method, bucket: second === undefined ? first : second };
116
128
  }
117
129
  if (host === 'api.clerk.com' || host.endsWith('.clerk.accounts.dev')) return { ...base, kind: 'autenticação', provider: 'Clerk' };
118
- // S3-compatible storage: signed header, or a presigned URL (signature in the query).
119
- if (/^AWS4-HMAC-SHA256/.test(String(headers.authorization ?? '')) || headers['x-amz-content-sha256']
120
- || queryKeys.some(key => /^x-amz-(signature|algorithm)$/i.test(key))) {
121
- const virtualHost = host.match(/^(.+?)\.s3[.-]/);
122
- const bucket = virtualHost ? virtualHost[1] : parsed.pathname.split('/')[1];
123
- const operation = { GET: 'leitura', HEAD: 'verificação', PUT: 'escrita', POST: 'escrita', DELETE: 'remoção' }[method] ?? method;
124
- const size = Number(headers['content-length']);
125
- return { ...base, kind: 'ficheiros', provider: 'S3', operation, bucket, bytes: Number.isFinite(size) ? size : undefined };
126
- }
127
130
  return base;
128
131
  }
129
132
 
@@ -218,7 +218,9 @@
218
218
  if (leaving && navigator.sendBeacon) navigator.sendBeacon(ENDPOINT, new Blob([body], { type: 'text/plain' }));
219
219
  else originalFetch.call(window, ENDPOINT, { method: 'POST', body, keepalive: body.length < 60000, headers: { 'content-type': 'text/plain' } }).catch(() => {});
220
220
  } catch {}
221
- if (action.trigger || action.requests.length) {
221
+ // A continuation on a new page counts too: in server-rendered apps (a form
222
+ // that loads the next page) it is how the bar shows what brought you here.
223
+ if (action.trigger || action.requests.length || action.segment > 1) {
222
224
  recorded.push({ id: action.id, label: labelOf(action), requests: action.requests.length, closedBy });
223
225
  if (recorded.length > 30) recorded.shift();
224
226
  updateBar(true);