cdp-edge 2.5.2 → 2.5.4

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 CHANGED
@@ -2,128 +2,21 @@
2
2
 
3
3
  **Padrão Quantum Tracking: 100% Cloudflare Edge.** Sem GTM. Sem Stape. Sem cookies de terceiros.
4
4
 
5
- > **v2.5.2** — Auditoria Completa + Sync Total de Agentes + Fix D1 (15 de Abril de 2026) 🔧
6
- > **v2.5.1** — SDK Bundle + Diagnóstico Pós-Deploy + Gap Fixes (15 de Abril de 2026) 🛠️
7
- > **v2.3.9** — Quiz Scoring Engine + Sales Engine (14 de Abril de 2026) 🤖💰
5
+ > **v2.5.4** — Auditoria de Dependências + Sync de Agentes + Fix D1 (25 de Abril de 2026) 🔧
8
6
 
9
7
  ---
10
8
 
11
- ## 📋 CHANGELOG v2.5.2 — Auditoria Completa + Sync Total de Agentes + Fix D1 (15 de Abril de 2026) 🔧
12
-
13
- ### Bug Fix Crítico
14
- - `schema-ltv-feedback.sql` adicionado à ordem de migração — `recordLtvFeedback()` falhava silenciosamente em todo Purchase, quebrando o ciclo preditivo LTV
15
-
16
- ### Fix de Compatibilidade D1
17
- - `schema-utm.sql`: `ADD COLUMN IF NOT EXISTS` → `ADD COLUMN` — D1/SQLite não suporta IF NOT EXISTS em ALTER TABLE
18
-
19
- ### Sincronização Total de Agentes
20
- - `contracts/agent-versions.json`: worker_version `2.2.3 → 2.5.2`, 49 agentes registrados (eram 26), `depends_on` `.js → .ts`, event count `19 → 25`, table count `24 → 31`
21
- - `database-agent.md`: `main = "worker.js" → "index.ts"`, 24 → 31 tabelas, roadmap atualizado
22
- - `devops-agent.md`: `RESEND_API_KEY`, `RESEND_FROM_EMAIL`, `WEBHOOK_SECRET_TICTO` adicionados ao `*secrets`
23
-
24
- ### D1 Produção
25
- - 31 tabelas aplicadas em `cdp-edge-db` — todos os schemas confirmados
26
- - Worker deployado: Version ID `37776c1a` — `/health` status: `ok`
27
-
28
- ---
29
-
30
- ## 📋 CHANGELOG v2.5.1 — SDK Bundle + Diagnóstico Pós-Deploy + Gap Fixes (15 de Abril de 2026) 🛠️
31
-
32
- ### SDK Bundle — cdpTrack.min.js pronto para `<script src="">`
33
- - `npm run sdk:build` → `dist/sdk/cdpTrack.min.js` (41.6 kB, IIFE, `window.cdpTrack`)
34
- - Um único arquivo sem dependências externas — cola no site e funciona
35
- - `dist/sdk/install-snippet.html` com snippet pronto para antes do `</body>`
36
-
37
- ### Diagnóstico Pós-Deploy — `cdp-edge validate <url>`
38
- ```bash
39
- cdp-edge validate https://meusite.com.br
40
- cdp-edge validate https://meusite.com.br --worker https://worker.meusite.workers.dev
41
- ```
42
- Verifica automaticamente:
43
- - Página carrega `cdpTrack.min.js` e Meta Pixel
44
- - Worker `/health` (D1, KV, AI, secrets)
45
- - Worker `/validate-install` (D1 write+read, KV, AI, secrets críticos)
46
- - `POST /track` aceita evento sintético → 200
47
-
48
- ### Gap Fixes Críticos
49
- - **Endpoint**: `/api/tracking` → `/track` em todos os módulos SDK
50
- - **utm_term**: injetado pelo Worker após quiz scoring (nunca pelo cliente)
51
- - **TikTok ttp**: cookie `_ttp` capturado e enviado ao Worker
52
- - **ROAS por origem**: segmentado por `utm_content` (quiz_* vs video_* vs landing_* vs ctwa_*)
53
-
54
- ### Novos Agentes
55
- - `lead-scoring-agent.md` — quiz de qualificação + análise dimensional Granite
56
- - `match-quality-agent.md` — guardião EMQ com cron 2h e alerta CallMeBot
57
-
58
- ---
9
+ ## 💻 INSTALAÇÃO
59
10
 
60
- ## 📋 CHANGELOG v2.3.9 — Quiz Scoring Engine + Sales Engine (14 de Abril de 2026) 🤖💰
61
-
62
- ### O que foi adicionado
63
-
64
- **ROAS Feedback Loop** (`modules/ml/roas.ts`)
65
- - Cruza leads com compras reais por campanha no D1
66
- - Calcula: `conversion_rate`, `revenue_per_lead`, `ltv_accuracy` (valida o modelo preditivo)
67
- - Recomendação automática de bid: `increase | maintain | decrease | pause`
68
- - Relatório semanal via CallMeBot com top campanhas e alertas
69
- - Persiste histórico em `roas_reports` + VIEW `v_roas_latest`
70
-
71
- **Nurture Engine** (`modules/nurture.ts`)
72
- - Sequências automáticas pós-quiz por qualificação:
73
- - `interessado` → D+1, D+3, D+7 (WhatsApp)
74
- - `curioso` → D+2, D+5 (conteúdo)
75
- - `comprador` → contato imediato via hot lead (já existente)
76
- - `perdido` → `cohort_label = excluded` (remove do remarketing)
77
- - `scheduleNurture()` chamado no QuizComplete em background
78
- - `runNurtureQueue()` executado pelo Intelligence Agent (cron diário)
79
- - D1: `nurture_sequences` + VIEWs de fila e stats
80
-
81
- **Lookalike Dinâmico** (`syncMetaLookalikeSeed()` em intelligence.ts)
82
- - Seed de Lookalike com compradores CONFIRMADOS (Purchase event) — não apenas high_intent
83
- - Une compradores reais + leads qualificados como `comprador` no quiz
84
- - Atualiza `cohort_label = buyer_confirmed` nos perfis
85
- - Persiste histórico em `lookalike_seeds`
86
- - Roda automaticamente no cron semanal do Intelligence Agent
87
-
88
- **Schema D1**: `schema-sales-engine.sql` — `roas_reports`, `nurture_sequences`, `lookalike_seeds` + 5 VIEWs
89
-
90
- ### Migração
91
11
  ```bash
92
- cd server-edge-tracker
93
- wrangler d1 execute cdp-edge-db --file=schema-sales-engine.sql --remote
94
- wrangler deploy
12
+ npx cdp-edge install
13
+ # ou
14
+ npm install -g cdp-edge
15
+ cdp-edge install . --name "Nome do Projeto"
95
16
  ```
96
17
 
97
18
  ---
98
19
 
99
- ## 📋 CHANGELOG v2.4.0 — Quiz Scoring Engine (14 de Abril de 2026) 🤖
100
-
101
- **Quiz Scoring Engine** (`modules/ml/quiz.ts`) — análise dimensional automática via Granite 4.0 Micro:
102
- - Detecta o TIPO de cada pergunta: `urgency | budget | timeline | fit | engagement | awareness | objection`
103
- - Atribui peso automático por dimensão (budget/urgency=5, fit=4, timeline=3, engagement=2)
104
- - Score ponderado → `comprador | interessado | curioso | perdido`
105
- - Fallback heurístico por palavras-chave quando AI indisponível
106
- - `quiz_sessions` no D1 com breakdown completo por dimensão
107
-
108
- **Integração no pipeline `/track`**:
109
- - `QuizComplete` + `quiz_answers[]` → scoring antes do LTV
110
- - `intentionLevel` qualificado alimenta LTV Prediction → `comprador` = LTV High automático
111
- - `QuizComplete` adicionado ao `LTV_EVENTS`
112
-
113
- **Migração**: `wrangler d1 execute cdp-edge-db --file=schema-quiz.sql --remote`
114
-
115
- ---
116
-
117
- ## 📋 CHANGELOG v2.3.8 — Fix Definitivo: api-versions.json (14 de Abril de 2026) 🔒
118
-
119
- `build.js` copia `extracted-skill/.../contracts/` → `contracts/` no `prepare` script. A fonte estava na v1.0.0, sobrescrevendo o arquivo correto a cada `npm publish`. Ambos os arquivos agora sincronizados em v1.1.0.
120
-
121
- ---
122
-
123
- > ⚠️ **REGRA DE OURO (SQUAD):** Todas as atualizações devem ser documentadas de forma sincronizada em `README.md`, `CLAUDE.md` e `CDP-EDGE-BUSINESS-BOOK.md`. Nenhuma alteração passa sem esse tripé.
124
-
125
- ---
126
-
127
20
  ## 🤖 MANIFESTO: EU, CDP EDGE
128
21
 
129
22
  *Eu não sou um simples "script de pixel". Eu sou uma arquitetura **Enterprise Tier 10** nascida e construída 100% nativa na infraestrutura da Cloudflare.*
@@ -161,7 +54,7 @@ Cron semanal (Intelligence Agent):
161
54
 
162
55
  ---
163
56
 
164
- ## 📊 BANCO DE DADOS D1 — 25 Tabelas + 12 VIEWs
57
+ ## 📊 BANCO DE DADOS D1 — 31 Tabelas + 12 VIEWs
165
58
 
166
59
  ```
167
60
  CORE TRACKING ML & INTELLIGENCE SALES ENGINE
@@ -231,6 +124,7 @@ v_leads_segmented v_ltv_feedback
231
124
  | `/health` | GET | Smoke test D1 + KV + AI |
232
125
  | `/webhook/ticto` | POST | Purchase webhook (HMAC) |
233
126
  | `/export/customer-match` | GET | Export leads para Google Ads |
127
+ | `/validate-install` | GET | Diagnóstico pós-deploy |
234
128
 
235
129
  ### ML Clustering (Fase 1)
236
130
  | Rota | Método | Função |
@@ -280,12 +174,12 @@ wrangler d1 execute cdp-edge-db --file=schema-ab-ltv.sql --remote
280
174
  wrangler d1 execute cdp-edge-db --file=schema-fraud.sql --remote
281
175
  wrangler d1 execute cdp-edge-db --file=schema-indexes.sql --remote
282
176
  wrangler d1 execute cdp-edge-db --file=migrate-v7.sql --remote
283
- wrangler d1 execute cdp-edge-db --file=schema-ltv-feedback.sql --remote # Fase 5b: colunas LTV em user_profiles
177
+ wrangler d1 execute cdp-edge-db --file=schema-ltv-feedback.sql --remote
284
178
  wrangler d1 execute cdp-edge-db --file=schema-utm.sql --remote
285
- wrangler d1 execute cdp-edge-db --file=schema-quiz.sql --remote # Fase 6
286
- wrangler d1 execute cdp-edge-db --file=schema-sales-engine.sql --remote # Fase 7
179
+ wrangler d1 execute cdp-edge-db --file=schema-quiz.sql --remote
180
+ wrangler d1 execute cdp-edge-db --file=schema-sales-engine.sql --remote
287
181
 
288
- # Secrets
182
+ # Secrets (nunca em arquivos — sempre via wrangler secret put)
289
183
  wrangler secret put META_ACCESS_TOKEN
290
184
  wrangler secret put GA4_API_SECRET
291
185
  wrangler secret put TIKTOK_ACCESS_TOKEN
@@ -296,17 +190,6 @@ wrangler deploy
296
190
 
297
191
  ---
298
192
 
299
- ## 💻 INSTALAÇÃO
300
-
301
- ```bash
302
- npx cdp-edge install
303
- # ou
304
- npm install -g cdp-edge
305
- cdp-edge install . --name "Nome do Projeto"
306
- ```
307
-
308
- ---
309
-
310
193
  ## 🗺️ GRAPHIFY — Navegando o Código
311
194
 
312
195
  ```bash
@@ -316,6 +199,8 @@ cdp-edge install . --name "Nome do Projeto"
316
199
  /graphify path "quiz_answers" "meta capi" # menor caminho
317
200
  ```
318
201
 
202
+ > Changelog completo → [CHANGELOG.md](./CHANGELOG.md)
203
+
319
204
  ---
320
205
 
321
206
  ### **CDP Edge — By Rica Soares**
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "_comment": "Fonte de verdade para versões dos agent files. Atualizar quando modules/ ou index.ts mudarem. Use scripts/validate-agents.js para detectar drifts.",
3
- "worker_version": "2.5.2",
4
- "worker_hash_date": "2026-04-15",
3
+ "worker_version": "2.5.4",
4
+ "worker_hash_date": "2026-04-25",
5
5
  "agents": {
6
6
  "master-orchestrator": {
7
7
  "version": "2.0.7",
@@ -140,7 +140,8 @@ const _wbraid = _urlParams.get('wbraid') || ''; // Google Ads (iOS, web-to-app,
140
140
  const _gbraid = _urlParams.get('gbraid') || ''; // Google Ads (app campaigns, privacy preserving)
141
141
 
142
142
  // TikTok
143
- const _ttclid = _urlParams.get('ttclid') || ''; // TikTok Ads click ID → complementa cookie _ttp
143
+ const _ttclid = _urlParams.get('ttclid') || ''; // TikTok Ads click ID → complementa cookie _ttp
144
+ const _msclkid = _urlParams.get('msclkid') || ''; // Microsoft Ads click ID
144
145
 
145
146
  // UTMs — rastreamento interno de origem de tráfego (independente da atribuição das plataformas)
146
147
  const _utms = {
@@ -217,7 +218,8 @@ const _getClickIDs = () => {
217
218
  wbraid: _wbraid || undefined,
218
219
  ttclid: _ttclid || undefined,
219
220
  ttp: document.cookie.match(/_ttp=([^;]+)/)?.[1] || undefined, // TikTok Pixel cookie — EMQ TikTok
220
- rclid: _urlParams.get('rclid') || undefined,
221
+ rclid: _urlParams.get('rclid') || undefined,
222
+ msclkid: _msclkid || undefined,
221
223
  };
222
224
  };
223
225
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-edge",
3
- "version": "2.5.2",
3
+ "version": "2.5.4",
4
4
  "description": "CDP Edge - Quantum Tracking - Sistema multi-agente para tracking digital Cloudflare Native (Workers + D1)",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -59,31 +59,31 @@
59
59
  "license": "MIT",
60
60
  "repository": {
61
61
  "type": "git",
62
- "url": "https://github.com/ricardosoli777/CDP-Edge-Premium"
62
+ "url": "https://github.com/ricardosoli777/CDP-Edge-Premium.ts"
63
63
  },
64
64
  "bugs": {
65
- "url": "https://github.com/ricardosoli777/CDP-Edge-Premium/issues"
65
+ "url": "https://github.com/ricardosoli777/CDP-Edge-Premium.ts/issues"
66
66
  },
67
- "homepage": "https://github.com/ricardosoli777/CDP-Edge-Premium#readme",
67
+ "homepage": "https://github.com/ricardosoli777/CDP-Edge-Premium.ts#readme",
68
68
  "engines": {
69
69
  "node": ">=18.0.0"
70
70
  },
71
71
  "dependencies": {
72
72
  "chalk": "^5.3.0",
73
- "commander": "^12.0.0",
74
- "inquirer": "^9.2.0",
75
- "ora": "^8.0.0"
73
+ "commander": "^14.0.3",
74
+ "inquirer": "^13.4.2",
75
+ "ora": "^9.4.0"
76
76
  },
77
77
  "devDependencies": {
78
- "@cloudflare/workers-types": "^4.20260412.1",
78
+ "@cloudflare/workers-types": "^4.20260425.1",
79
79
  "@semantic-release/changelog": "^6.0.3",
80
80
  "@semantic-release/commit-analyzer": "^13.0.1",
81
81
  "@semantic-release/github": "^12.0.6",
82
82
  "@semantic-release/npm": "^13.1.5",
83
83
  "@semantic-release/release-notes-generator": "^14.1.0",
84
- "@types/node": "^20.19.39",
84
+ "@types/node": "^25.6.0",
85
85
  "esbuild": "^0.28.0",
86
86
  "semantic-release": "^25.0.3",
87
- "typescript": "^6.0.2"
87
+ "typescript": "^6.0.3"
88
88
  }
89
89
  }
@@ -0,0 +1,5 @@
1
+ DATABASE_ID=
2
+ SITE_DOMAIN=
3
+ META_PIXEL_ID=
4
+ GA4_MEASUREMENT_ID=
5
+ TIKTOK_PIXEL_ID=
@@ -371,9 +371,12 @@ export default {
371
371
  'utmSource','utmMedium','utmCampaign','utmContent','utmTerm',
372
372
  'fbclid','ttclid','gclid','transactionId','productName','currency'];
373
373
 
374
- const { eventName, behavioral_data, ...payload } = body as { eventName?: string; behavioral_data?: BehavioralData; [key: string]: any };
374
+ const { eventName: _bodyEventName, behavioral_data, ...payload } = body as { eventName?: string; behavioral_data?: BehavioralData; [key: string]: any };
375
375
  const trackPayload: TrackPayload = payload;
376
376
 
377
+ // Aceita eventName (camelCase) ou event_name (snake_case — formato cdpTrack.js SDK)
378
+ const eventName = _bodyEventName || (payload.event_name as string | undefined);
379
+
377
380
  // ── Extrair click_ids e utms de sub-objetos (formato cdpTrack.js) ────────
378
381
  // cdpTrack.js envia fbp/fbc/fbclid dentro de click_ids{} e UTMs dentro de utms{}
379
382
  // O Worker trabalha com campos no nível raiz — esta extração resolve o mismatch.
@@ -383,8 +386,11 @@ export default {
383
386
  if (!trackPayload.fbc && c.fbc) trackPayload.fbc = c.fbc;
384
387
  if (!trackPayload.fbclid && c.fbclid) trackPayload.fbclid = c.fbclid;
385
388
  if (!trackPayload.gclid && c.gclid) trackPayload.gclid = c.gclid;
389
+ if (!trackPayload.wbraid && c.wbraid) trackPayload.wbraid = c.wbraid;
390
+ if (!trackPayload.gbraid && c.gbraid) trackPayload.gbraid = c.gbraid;
386
391
  if (!trackPayload.ttclid && c.ttclid) trackPayload.ttclid = c.ttclid;
387
- if (!trackPayload.ttp && c.ttp) trackPayload.ttp = c.ttp; // TikTok Pixel cookie
392
+ if (!trackPayload.ttp && c.ttp) trackPayload.ttp = c.ttp;
393
+ if (!trackPayload.msclkid && c.msclkid) trackPayload.msclkid = c.msclkid;
388
394
  }
389
395
  if (payload.utms && typeof payload.utms === 'object') {
390
396
  const u = payload.utms as Record<string, string>;
@@ -395,6 +401,12 @@ export default {
395
401
  if (!trackPayload.utmTerm && u.utm_term) trackPayload.utmTerm = u.utm_term;
396
402
  }
397
403
 
404
+ // ── Normalizar campos snake_case → camelCase (formato cdpTrack.js SDK) ──
405
+ if (!trackPayload.userId && payload.user_id) trackPayload.userId = payload.user_id;
406
+ if (!trackPayload.eventId && payload.event_id) trackPayload.eventId = payload.event_id;
407
+ if (!trackPayload.pageUrl && payload.page_url) trackPayload.pageUrl = payload.page_url;
408
+ if (!trackPayload.sessionId && payload.session_id) trackPayload.sessionId = payload.session_id;
409
+
398
410
  // ── Validação de eventName ────────────────────────────────────────
399
411
  if (!eventName) {
400
412
  return new Response(JSON.stringify({ error: 'eventName é obrigatório' }), { status: 400, headers });
@@ -750,6 +762,21 @@ export default {
750
762
  ctx.waitUntil(resolveDeviceGraph(env.DB, payload.userId, payload.email, payload.phone));
751
763
  }
752
764
 
765
+ // Deduplicação server-side — INSERT OR IGNORE retorna changes=0 para duplicatas
766
+ if (env.DB && payload.eventId) {
767
+ try {
768
+ const dedup = await env.DB.prepare(
769
+ `INSERT OR IGNORE INTO events (event_id, event_name, user_id, created_at)
770
+ VALUES (?, ?, ?, datetime('now'))`
771
+ ).bind(payload.eventId, eventName, payload.userId || null).run();
772
+ if (dedup.meta.changes === 0) {
773
+ return new Response(JSON.stringify({ ok: true, skipped: 'duplicate' }), { status: 200, headers });
774
+ }
775
+ } catch {
776
+ // Tabela ausente ou erro de DB — não bloqueia o pipeline
777
+ }
778
+ }
779
+
753
780
  // R2 Audit Log — background
754
781
  ctx.waitUntil(writeAuditLog(env, eventName, payload, geoData));
755
782
 
@@ -115,6 +115,7 @@ export async function upsertProfile(env: Env, eventName: string, payload: TrackP
115
115
  const {
116
116
  userId, email, phone,
117
117
  fbp, fbc, ttp, gclid, ttclid, gaClientId,
118
+ wbraid, gbraid, msclkid,
118
119
  city, state, country,
119
120
  engagementScore, userScore,
120
121
  } = payload;
@@ -131,8 +132,9 @@ export async function upsertProfile(env: Env, eventName: string, payload: TrackP
131
132
  await env.DB.prepare(`
132
133
  INSERT INTO user_profiles
133
134
  (user_id, email, phone, fbp, fbc, ttp, gclid, ttclid, ga_client_id,
135
+ wbraid, gbraid, msclkid,
134
136
  city, state, country, score, cohort_label, created_at, updated_at)
135
- VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,datetime('now'),datetime('now'))
137
+ VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,datetime('now'),datetime('now'))
136
138
  ON CONFLICT(user_id) DO UPDATE SET
137
139
  email = COALESCE(excluded.email, user_profiles.email),
138
140
  phone = COALESCE(excluded.phone, user_profiles.phone),
@@ -142,6 +144,9 @@ export async function upsertProfile(env: Env, eventName: string, payload: TrackP
142
144
  gclid = COALESCE(excluded.gclid, user_profiles.gclid),
143
145
  ttclid = COALESCE(excluded.ttclid, user_profiles.ttclid),
144
146
  ga_client_id = COALESCE(excluded.ga_client_id, user_profiles.ga_client_id),
147
+ wbraid = COALESCE(excluded.wbraid, user_profiles.wbraid),
148
+ gbraid = COALESCE(excluded.gbraid, user_profiles.gbraid),
149
+ msclkid = COALESCE(excluded.msclkid, user_profiles.msclkid),
145
150
  city = COALESCE(excluded.city, user_profiles.city),
146
151
  state = COALESCE(excluded.state, user_profiles.state),
147
152
  country = COALESCE(excluded.country, user_profiles.country),
@@ -158,6 +163,9 @@ export async function upsertProfile(env: Env, eventName: string, payload: TrackP
158
163
  gclid || null,
159
164
  ttclid || null,
160
165
  gaClientId || null,
166
+ wbraid || null,
167
+ gbraid || null,
168
+ msclkid || null,
161
169
  city || null,
162
170
  state || null,
163
171
  (country || (request as any).cf?.country || null),
@@ -99,9 +99,9 @@ export async function sendWhatsApp(env: Env, tipo: string, payload: TrackPayload
99
99
  }
100
100
 
101
101
  // Text fallback (dentro da janela de 24h)
102
- const nome = [payload.firstName, payload.lastName].filter(Boolean).join(' ') || 'sem nome';
103
- const valor = payload.value ? `R$ ${parseFloat(String(payload.value)).toFixed(2)}` : '—';
104
- const utm = payload.utmSource || 'direto';
102
+ const nome = [payload.firstName, payload.lastName].filter(Boolean).join(' ') || 'sem nome';
103
+ const valor = payload.value ? `R$ ${parseFloat(String(payload.value)).toFixed(2)}` : '—';
104
+ const utm = payload.utmSource || 'direto';
105
105
  const produto = payload.contentName || '';
106
106
 
107
107
  let texto = '';
@@ -127,7 +127,7 @@ export async function sendWhatsApp(env: Env, tipo: string, payload: TrackPayload
127
127
  async function _sendWARequest(env: Env, body: Record<string, any>): Promise<any> {
128
128
  try {
129
129
  const phoneNumberId = resolvePhoneNumberId(env);
130
- const accessToken = resolveAccessToken(env);
130
+ const accessToken = resolveAccessToken(env);
131
131
  const res = await fetch(`https://graph.facebook.com/v22.0/${phoneNumberId}/messages`, {
132
132
  method: 'POST',
133
133
  headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${accessToken}` },
@@ -169,14 +169,14 @@ export async function processWhatsAppWebhook(env: Env, body: any, request: Reque
169
169
  const results: any[] = [];
170
170
 
171
171
  for (const message of messages) {
172
- const phone = message.from;
173
- const wamid = message.id;
174
- const referral = message.referral || {};
175
- const ctwaClid = referral.ctwa_clid || null;
176
- const adId = referral.source_id || null;
177
- const sourceUrl = referral.source_url || null;
178
- const headline = referral.headline || null;
179
- const messageBody = message.text?.body || message.type || '';
172
+ const phone = message.from;
173
+ const wamid = message.id;
174
+ const referral = message.referral || {};
175
+ const ctwaClid = referral.ctwa_clid || null;
176
+ const adId = referral.source_id || null;
177
+ const sourceUrl = referral.source_url || null;
178
+ const headline = referral.headline || null;
179
+ const messageBody = message.text?.body || message.type || '';
180
180
 
181
181
  if (!phone) { results.push({ skipped: 'no phone' }); continue; }
182
182
 
@@ -202,9 +202,9 @@ export async function processWhatsAppWebhook(env: Env, body: any, request: Reque
202
202
  }
203
203
 
204
204
  const capiEvent: Record<string, any> = {
205
- event_name: 'Contact',
206
- event_time: Math.floor(Date.now() / 1000),
207
- event_id: eventId,
205
+ event_name: 'Contact',
206
+ event_time: Math.floor(Date.now() / 1000),
207
+ event_id: eventId,
208
208
  action_source: 'chat',
209
209
  user_data: {
210
210
  ph: phoneHash,
@@ -79,6 +79,22 @@ CREATE INDEX IF NOT EXISTS idx_profiles_fbp ON user_profiles(fbp);
79
79
  CREATE INDEX IF NOT EXISTS idx_profiles_msclkid ON user_profiles(msclkid);
80
80
  CREATE INDEX IF NOT EXISTS idx_profiles_li_fat ON user_profiles(li_fat_id);
81
81
 
82
+ -- ── Tabela de Eventos (Deduplicação + Label ML) ──────────────────────────────
83
+ -- Registra cada evento processado com sucesso.
84
+ -- Duplo uso: (1) deduplicação server-side via event_id UNIQUE;
85
+ -- (2) label de LTV para treinamento ML (lead comprou depois?)
86
+ CREATE TABLE IF NOT EXISTS events (
87
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
88
+ event_id TEXT UNIQUE, -- deduplicação: INSERT OR IGNORE descarta duplicatas
89
+ event_name TEXT NOT NULL,
90
+ user_id TEXT,
91
+ created_at TEXT NOT NULL DEFAULT (datetime('now'))
92
+ );
93
+
94
+ CREATE INDEX IF NOT EXISTS idx_events_user_id ON events(user_id);
95
+ CREATE INDEX IF NOT EXISTS idx_events_event_name ON events(event_name);
96
+ CREATE INDEX IF NOT EXISTS idx_events_created ON events(created_at);
97
+
82
98
  -- ── Tabela de LOG de Webhooks Offline ─────────────────────────────────────────
83
99
  CREATE TABLE IF NOT EXISTS webhook_events (
84
100
  id INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -85,8 +85,17 @@ export interface TrackPayload {
85
85
  // Identifiers
86
86
  fbp?: string | null;
87
87
  fbc?: string | null;
88
+ fbclid?: string | null;
88
89
  ttp?: string | null;
90
+ gclid?: string | null;
91
+ wbraid?: string | null;
92
+ gbraid?: string | null;
93
+ ttclid?: string | null;
94
+ rclid?: string | null;
95
+ msclkid?: string | null;
96
+ li_fat_id?: string | null;
89
97
  gaClientId?: string | null;
98
+ sessionId?: string | null;
90
99
 
91
100
  // Parameters
92
101
  value?: number | null;
@@ -1,155 +0,0 @@
1
- # CDP Edge — Instalação em Projetos de Clientes
2
-
3
- Como integrar o CDP Edge em qualquer projeto para que Claude Code, VS Code, Cursor e outras IDEs com IA o reconheçam automaticamente.
4
-
5
- ---
6
-
7
- ## Estrutura recomendada
8
-
9
- ```
10
- meu-projeto-cliente/ ← abrir esta pasta no Claude Code / VS Code
11
- ├── CLAUDE.md ← ⭐ faz o Claude Code ativar o CDP Edge automaticamente
12
- ├── .cursorrules ← (opcional) para Cursor IDE
13
- ├── cdp-edge/ ← CDP Edge instalado aqui
14
- │ ├── extracted-skill/
15
- │ ├── server-edge-tracker/
16
- │ ├── templates/
17
- │ └── docs/
18
- ├── src/ ← código do projeto do cliente
19
- └── ...
20
- ```
21
-
22
- ---
23
-
24
- ## Passo 1 — Instalar o CDP Edge na pasta do projeto
25
-
26
- ```bash
27
- # Dentro da pasta do projeto do cliente:
28
- cd meu-projeto-cliente
29
-
30
- # Opção A — clonar o CDP Edge como subpasta
31
- git clone https://github.com/ricardosoli777/CDP-Edge-Premium cdp-edge
32
-
33
- # Opção B — copiar manualmente
34
- # Copiar a pasta CDP-Edge inteira e renomear para cdp-edge/
35
- ```
36
-
37
- ---
38
-
39
- ## Passo 2 — Criar o CLAUDE.md na raiz do projeto
40
-
41
- Copiar o template de ativação:
42
-
43
- ```bash
44
- cp cdp-edge/templates/install/CLAUDE.md ./CLAUDE.md
45
- ```
46
-
47
- Editar o `CLAUDE.md` e substituir `[NOME DO PROJETO]` pelo nome real do projeto.
48
-
49
- **Por que isso funciona:**
50
- O Claude Code lê o `CLAUDE.md` da raiz do diretório aberto automaticamente. Quando encontra a instrução de leitura do `master-orchestrator.md`, ativa o sistema imediatamente na abertura da sessão.
51
-
52
- ---
53
-
54
- ## Passo 3 — Abrir o projeto no Claude Code
55
-
56
- ```bash
57
- # Na pasta do projeto (não dentro do cdp-edge):
58
- claude .
59
- ```
60
-
61
- O Claude Code vai:
62
- 1. Ler o `CLAUDE.md` da raiz
63
- 2. Encontrar a instrução de ativação do CDP Edge
64
- 3. Carregar o `master-orchestrator.md`
65
- 4. Exibir a mensagem de boas-vindas automaticamente
66
-
67
- ---
68
-
69
- ## Para VS Code com extensão Claude
70
-
71
- O mesmo mecanismo funciona. O `CLAUDE.md` é lido pela extensão Claude Code no VS Code quando você abre a pasta do projeto.
72
-
73
- ```bash
74
- code meu-projeto-cliente/
75
- ```
76
-
77
- ---
78
-
79
- ## Para Cursor IDE
80
-
81
- Criar adicionalmente um `.cursorrules` na raiz:
82
-
83
- ```bash
84
- cp cdp-edge/templates/install/CLAUDE.md .cursorrules
85
- ```
86
-
87
- O Cursor lê `.cursorrules` automaticamente e aplica as instruções ao agente AI.
88
-
89
- ---
90
-
91
- ## Para qualquer outra IDE com AI
92
-
93
- Qualquer IDE que suporte arquivos de instrução (`.aidioms`, `system-prompt.md`, etc.) pode usar o mesmo conteúdo do `CLAUDE.md` adaptado para o formato da IDE.
94
-
95
- ---
96
-
97
- ## Verificar se funcionou
98
-
99
- Ao abrir o projeto no Claude Code, você deve ver imediatamente:
100
-
101
- ```
102
- 👋 Olá! Bem-vindo ao CDP Edge — Quantum Tracking.
103
-
104
- Sou o seu Orquestrador de Rastreamento. Vou configurar toda a
105
- infraestrutura de tracking server-side para o seu projeto...
106
- ```
107
-
108
- Se não aparecer automaticamente, digitar:
109
-
110
- ```
111
- /cdp
112
- ```
113
-
114
- ou simplesmente:
115
-
116
- ```
117
- ative o master orchestrator do cdp edge
118
- ```
119
-
120
- ---
121
-
122
- ## Se o CDP Edge estiver em um caminho diferente
123
-
124
- Editar o `CLAUDE.md` na raiz e ajustar o path:
125
-
126
- ```markdown
127
- <!-- Padrão: -->
128
- Leia o arquivo: `cdp-edge/extracted-skill/tracking-events-generator/agents/master-orchestrator.md`
129
-
130
- <!-- Se instalado em outro lugar: -->
131
- Leia o arquivo: `ferramentas/cdp/extracted-skill/tracking-events-generator/agents/master-orchestrator.md`
132
- ```
133
-
134
- ---
135
-
136
- ## Estrutura de múltiplos projetos (workspace compartilhado)
137
-
138
- Se você trabalha com vários clientes e quer uma instalação única do CDP Edge:
139
-
140
- ```
141
- workspace/
142
- ├── cdp-edge/ ← instalação única compartilhada
143
- ├── cliente-1/
144
- │ ├── CLAUDE.md ← aponta para ../cdp-edge/...
145
- │ └── src/
146
- ├── cliente-2/
147
- │ ├── CLAUDE.md ← aponta para ../cdp-edge/...
148
- │ └── src/
149
- ```
150
-
151
- No `CLAUDE.md` de cada cliente, usar path relativo:
152
-
153
- ```markdown
154
- Leia o arquivo: `../cdp-edge/extracted-skill/tracking-events-generator/agents/master-orchestrator.md`
155
- ```
@@ -1,185 +0,0 @@
1
- # CDP Edge — Quick Start
2
- > Do zero ao primeiro evento em produção.
3
-
4
- ---
5
-
6
- ## Pré-requisitos
7
-
8
- | Ferramenta | Instalar |
9
- |---|---|
10
- | Node.js 18+ | nodejs.org |
11
- | Wrangler CLI | `npm install -g wrangler` |
12
- | Conta Cloudflare | cloudflare.com (plano gratuito suficiente) |
13
- | Conta Meta (Pixel + token CAPI) | business.facebook.com |
14
-
15
- ---
16
-
17
- ## 1. Autenticar no Cloudflare
18
-
19
- ```bash
20
- wrangler login
21
- ```
22
-
23
- ---
24
-
25
- ## 2. Criar o banco D1
26
-
27
- ```bash
28
- wrangler d1 create cdp-edge-db
29
- ```
30
-
31
- Copie o `database_id` retornado e cole no `wrangler.toml`:
32
-
33
- ```toml
34
- [[d1_databases]]
35
- binding = "DB"
36
- database_name = "cdp-edge-db"
37
- database_id = "COLE_O_ID_AQUI"
38
- ```
39
-
40
- ---
41
-
42
- ## 3. Aplicar o schema
43
-
44
- ```bash
45
- cd server-edge-tracker
46
-
47
- wrangler d1 execute cdp-edge-db --file=schema.sql --remote
48
- wrangler d1 execute cdp-edge-db --file=migrate-v2.sql --remote
49
- wrangler d1 execute cdp-edge-db --file=migrate-v3.sql --remote
50
- wrangler d1 execute cdp-edge-db --file=migrate-v4.sql --remote
51
- wrangler d1 execute cdp-edge-db --file=migrate-v5.sql --remote
52
- wrangler d1 execute cdp-edge-db --file=migrate-v6.sql --remote
53
- ```
54
-
55
- ---
56
-
57
- ## 4. Configurar secrets
58
-
59
- ```bash
60
- wrangler secret put META_ACCESS_TOKEN
61
- wrangler secret put GA4_API_SECRET
62
- wrangler secret put TIKTOK_ACCESS_TOKEN
63
- ```
64
-
65
- Para WhatsApp CTWA (opcional):
66
- ```bash
67
- wrangler secret put WA_WEBHOOK_VERIFY_TOKEN
68
- wrangler secret put WHATSAPP_TOKEN
69
- wrangler secret put WHATSAPP_PHONE_NUMBER_ID
70
- ```
71
-
72
- ---
73
-
74
- ## 5. Atualizar variáveis públicas no wrangler.toml
75
-
76
- ```toml
77
- [vars]
78
- META_PIXEL_ID = "SEU_PIXEL_ID"
79
- GA4_MEASUREMENT_ID = "G-XXXXXXXXXX"
80
- TIKTOK_PIXEL_ID = "CXXXXXXXXXXXXXXX"
81
- SITE_DOMAIN = "seudominio.com.br"
82
- ```
83
-
84
- ---
85
-
86
- ## 6. Deploy
87
-
88
- ```bash
89
- wrangler deploy
90
- ```
91
-
92
- O Worker ficará disponível em:
93
- ```
94
- https://server-edge-tracker.SEU-USUARIO.workers.dev
95
- ```
96
-
97
- ---
98
-
99
- ## 7. Testar o Worker
100
-
101
- ```bash
102
- # Health check
103
- curl https://server-edge-tracker.SEU-USUARIO.workers.dev/health
104
-
105
- # Evento de teste
106
- curl -X POST https://server-edge-tracker.SEU-USUARIO.workers.dev/track \
107
- -H "Content-Type: application/json" \
108
- -d '{
109
- "event": "PageView",
110
- "url": "https://seudominio.com.br/",
111
- "email": "teste@exemplo.com"
112
- }'
113
- ```
114
-
115
- Resposta esperada: `{"success":true}`
116
-
117
- ---
118
-
119
- ## 8. Instalar o SDK no site
120
-
121
- ```html
122
- <!-- No <head> de todas as páginas -->
123
- <script>
124
- window.CDP_CONFIG = {
125
- endpoint: 'https://server-edge-tracker.SEU-USUARIO.workers.dev/track',
126
- pixelId: 'SEU_PIXEL_ID'
127
- };
128
- </script>
129
- <script src="/js/cdpTrack.js" type="module" async></script>
130
- ```
131
-
132
- O SDK inicializa automaticamente e dispara `PageView` ao carregar.
133
-
134
- ---
135
-
136
- ## 9. Verificar no Meta Events Manager
137
-
138
- ```
139
- Meta Business Suite
140
- → Gerenciador de Eventos
141
- → Seu Pixel
142
- → Atividade de Teste
143
- → Filtrar por "Servidor"
144
- ```
145
-
146
- Você deve ver o `PageView` chegando com `action_source: website`.
147
-
148
- ---
149
-
150
- ## 10. (Opcional) Conectar domínio próprio
151
-
152
- Para máxima eficácia (cookie first-party no domínio do cliente):
153
-
154
- ```
155
- Cloudflare Dashboard → DNS → Add record
156
- Type: CNAME
157
- Name: track
158
- Target: server-edge-tracker.SEU-USUARIO.workers.dev
159
- Proxy: ✅ (nuvem laranja)
160
- ```
161
-
162
- Adicionar ao `wrangler.toml`:
163
- ```toml
164
- [[routes]]
165
- pattern = "track.seudominio.com.br/*"
166
- zone_name = "seudominio.com.br"
167
- ```
168
-
169
- ```bash
170
- wrangler deploy
171
- ```
172
-
173
- Ver detalhes: `agents/domain-setup-agent.md`
174
-
175
- ---
176
-
177
- ## Próximos passos
178
-
179
- | O que fazer | Onde está |
180
- |---|---|
181
- | Configurar WhatsApp CTWA | `agents/whatsapp-ctwa-setup-agent.md` |
182
- | Ativar R2 audit log | `agents/r2-setup-agent.md` |
183
- | Configurar gateways de pagamento | `agents/webhook-agent.md` |
184
- | Ver todos os eventos disponíveis | `docs/events-reference.md` |
185
- | Ver API do SDK browser | `docs/sdk-reference.md` |