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 +10 -5
- package/readme.md +35 -10
- package/src/ai.mjs +3 -1
- package/src/boundaries.mjs +12 -9
- package/src/browser/bar.js +3 -1
- package/src/cli.mjs +243 -25
- package/src/detect-python.mjs +286 -0
- package/src/detect.mjs +21 -6
- package/src/diagnose.mjs +34 -4
- package/src/digest.mjs +17 -3
- package/src/page.mjs +19 -7
- package/src/panel.mjs +55 -13
- package/src/python/codetac_py/__init__.py +88 -0
- package/src/python/codetac_py/boundaries.py +435 -0
- package/src/python/codetac_py/capture.py +351 -0
- package/src/python/codetac_py/context.py +50 -0
- package/src/python/codetac_py/detail.py +333 -0
- package/src/python/codetac_py/files.py +135 -0
- package/src/python/codetac_py/frameworks.py +72 -0
- package/src/python/codetac_py/hooks.py +72 -0
- package/src/python/codetac_py/jinja_map.py +132 -0
- package/src/python/codetac_py/network.py +704 -0
- package/src/python/codetac_py/page.py +513 -0
- package/src/python/codetac_py/project.py +60 -0
- package/src/python/codetac_py/redact.py +104 -0
- package/src/python/codetac_py/servers.py +514 -0
- package/src/python/codetac_py/sitecustomize.py +62 -0
- package/src/python/codetac_py/writer.py +237 -0
- package/src/python/probe.py +99 -0
- package/src/recording.mjs +22 -4
- package/src/runtime.mjs +13 -1
- package/src/sentences.mjs +4 -0
- package/src/store.mjs +61 -9
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "codetac",
|
|
3
|
-
"version": "0.
|
|
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
|
|
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
|

|
|
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
|
-
**
|
|
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
|
|
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
|
-
|
|
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) }) {
|
package/src/boundaries.mjs
CHANGED
|
@@ -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
|
|
package/src/browser/bar.js
CHANGED
|
@@ -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
|
-
|
|
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);
|