cdp-edge 1.18.3 → 1.20.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.
@@ -1,138 +1,353 @@
1
- /**
2
- * Setup Wizard - Wrapper para invocar CDP Edge Skill
1
+ /**
2
+ * CDP Edge Setup Wizard Guiado e não-técnico
3
3
  *
4
- * O CLI é apenas um disparador. A skill (Master Orchestrator) faz tudo.
4
+ * Coleta os dados do projeto do cliente e gera:
5
+ * • Comandos wrangler secret put prontos para copiar
6
+ * • URLs de webhook com domínio preenchido
7
+ * • Checklist de próximos passos para o agente
5
8
  */
6
9
 
7
10
  import inquirer from 'inquirer';
8
11
  import chalk from 'chalk';
9
12
  import ora from 'ora';
13
+ import { writeFileSync } from 'fs';
14
+ import { join } from 'path';
10
15
 
11
16
  function printBanner() {
12
17
  console.log('');
13
- console.log(chalk.white.bold(' CDP Edge Setup Wizard'));
14
- console.log('');
15
- console.log(chalk.cyan(' ██████╗██████╗ ██████╗ ███████╗██████╗ ██████╗ ███████╗'));
16
- console.log(chalk.cyan('██╔════╝██╔══██╗██╔══██╗ ██╔════╝██╔══██╗██╔════╝ ██╔════╝'));
17
- console.log(chalk.cyan('██║ ██║ ██║██████╔╝ █████╗ ██║ ██║██║ ███╗█████╗ '));
18
- console.log(chalk.cyan('██║ ██║ ██║██╔═══╝ ██╔══╝ ██║ ██║██║ ██║██╔══╝ '));
19
- console.log(chalk.cyan('╚██████╗██████╔╝██║ ███████╗██████╔╝╚██████╔╝███████╗'));
20
- console.log(chalk.cyan(' ╚═════╝╚═════╝ ╚═╝ ╚══════╝╚═════╝ ╚═════╝╚══════╝'));
21
- console.log('');
22
- console.log(chalk.gray(' Customer Data Platform on the Edge · Global Edge Tracking · v2.0.2'));
23
- console.log('');
24
- console.log(chalk.gray('═'.repeat(68)));
18
+ console.log(chalk.cyan(' ╔═══════════════════════════════════════╗'));
19
+ console.log(chalk.cyan('') + chalk.white.bold(' CDP Edge — Setup Wizard ') + chalk.cyan('║'));
20
+ console.log(chalk.cyan(' ║') + chalk.gray(' Configuração Guiada de Tracking ') + chalk.cyan('║'));
21
+ console.log(chalk.cyan(' ╚═══════════════════════════════════════╝'));
25
22
  console.log('');
26
23
  }
27
24
 
25
+ function sep(label = '') {
26
+ if (label) {
27
+ console.log('\n' + chalk.gray('── ') + chalk.cyan.bold(label) + chalk.gray(' ' + '─'.repeat(Math.max(0, 40 - label.length))));
28
+ } else {
29
+ console.log(chalk.gray('─'.repeat(50)));
30
+ }
31
+ }
32
+
28
33
  export async function runSetupWizard(dir = '.') {
29
34
  printBanner();
30
35
 
31
- // === MENSAGEM INICIAL ===
36
+ console.log(chalk.cyan(' Vou te fazer algumas perguntas simples.'));
37
+ console.log(chalk.cyan(' Não precisa saber programar — é só preencher os dados.\n'));
38
+ console.log(chalk.gray(' Ao final, vou gerar todos os comandos prontos para instalar.\n'));
32
39
 
33
- console.log(chalk.gray('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
34
- console.log(chalk.green.bold(' ✅ CDP Edge INSTALADO COM SUCESSO!'));
35
- console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
40
+ // ── PASSO 1: Dados do projeto ──────────────────────────────────────────────
41
+ sep('1. Dados do Projeto');
36
42
 
37
- console.log(chalk.cyan('🎯 O Master Orchestrator controla todo o processo de criação.'));
38
- console.log(chalk.cyan(' Ele vai:\n'));
39
- console.log(chalk.cyan(' • Fazer as perguntas necessárias'));
40
- console.log(chalk.cyan(' • Analisar suas páginas automaticamente'));
41
- console.log(chalk.cyan(' Chamar os agentes especialistas'));
42
- console.log(chalk.cyan(' • Gerar todos os arquivos de tracking'));
43
- console.log(chalk.cyan(' Validar o código gerado\n'));
43
+ const projeto = await inquirer.prompt([
44
+ {
45
+ type: 'input',
46
+ name: 'domain',
47
+ message: 'Qual o domínio do site? (ex: meusite.com.br)',
48
+ validate: v => v.trim() ? true : 'Domínio é obrigatório',
49
+ filter: v => v.trim().replace(/^https?:\/\//, '').replace(/\/$/, ''),
50
+ },
51
+ {
52
+ type: 'input',
53
+ name: 'projectName',
54
+ message: 'Nome do projeto (para identificação):',
55
+ default: answers => answers.domain.split('.')[0],
56
+ },
57
+ {
58
+ type: 'list',
59
+ name: 'productType',
60
+ message: 'Que tipo de produto você vende?',
61
+ choices: [
62
+ { name: 'Infoproduto / Curso Online', value: 'infoproduto' },
63
+ { name: 'E-commerce físico', value: 'ecommerce' },
64
+ { name: 'SaaS / Assinatura', value: 'saas' },
65
+ { name: 'Serviço / Consultoria', value: 'servico' },
66
+ { name: 'Lead Generation (captação)', value: 'leadgen' },
67
+ ],
68
+ },
69
+ ]);
44
70
 
45
- // === MENU DE OPÇÃO ===
71
+ // ── PASSO 2: Plataforma de vendas ──────────────────────────────────────────
72
+ sep('2. Plataforma de Vendas / Checkout');
46
73
 
47
- const menu = await inquirer.prompt([
74
+ const { plataformas } = await inquirer.prompt([
48
75
  {
49
- type: 'list',
50
- name: 'action',
51
- message: 'O que você deseja fazer?',
76
+ type: 'checkbox',
77
+ name: 'plataformas',
78
+ message: 'Quais plataformas de checkout você usa? (espaço para marcar)',
52
79
  choices: [
53
- { name: '🚀 Iniciar Master Orchestrator (Configuração Completa)', value: 'start' },
54
- { name: '📖 Ver documentação', value: 'docs' },
55
- { name: '❌ Sair', value: 'exit' }
56
- ]
57
- }
80
+ { name: 'Hotmart', value: 'hotmart', checked: false },
81
+ { name: 'Kiwify', value: 'kiwify', checked: false },
82
+ { name: 'Ticto', value: 'ticto', checked: false },
83
+ { name: 'Eduzz', value: 'eduzz', checked: false },
84
+ { name: 'Stripe', value: 'stripe', checked: false },
85
+ { name: 'Nenhuma (apenas formulário de captação)', value: 'none', checked: false },
86
+ ],
87
+ validate: v => v.length > 0 ? true : 'Selecione pelo menos uma opção',
88
+ },
89
+ ]);
90
+
91
+ // ── PASSO 3: Plataformas de anúncios ───────────────────────────────────────
92
+ sep('3. Plataformas de Anúncios');
93
+
94
+ const { adsPlataformas } = await inquirer.prompt([
95
+ {
96
+ type: 'checkbox',
97
+ name: 'adsPlataformas',
98
+ message: 'Onde você faz anúncios? (espaço para marcar)',
99
+ choices: [
100
+ { name: 'Meta Ads (Facebook / Instagram)', value: 'meta', checked: true },
101
+ { name: 'Google Ads / YouTube', value: 'google', checked: false },
102
+ { name: 'TikTok Ads', value: 'tiktok', checked: false },
103
+ { name: 'Pinterest Ads', value: 'pinterest', checked: false },
104
+ { name: 'LinkedIn Ads', value: 'linkedin', checked: false },
105
+ ],
106
+ validate: v => v.length > 0 ? true : 'Selecione pelo menos uma plataforma',
107
+ },
58
108
  ]);
59
109
 
60
- if (menu.action === 'exit') {
61
- console.log(chalk.yellow('\n👋 Até logo!\n'));
62
- process.exit(0);
63
- }
64
-
65
- if (menu.action === 'docs') {
66
- console.log(chalk.cyan('\n📚 Documentação:'));
67
- console.log(' Acesse: ' + chalk.underline('docs/guia-cloudflare-iniciante.md'));
68
- console.log(' Ou visite: ' + chalk.underline('github.com/ricardosoli777/CDP-Edge-Premium'));
69
- return;
70
- }
71
-
72
- if (menu.action === 'start') {
73
- const spinner = ora('Iniciando Master Orchestrator...').start();
74
- await sleep(800);
75
- spinner.succeed(chalk.green('Master Orchestrator iniciado!'));
76
-
77
- console.log(chalk.gray('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
78
- console.log(chalk.cyan.bold(' ' + chalk.bold('🧠 MASTER ORCHESTRATOR ATIVO')));
79
- console.log(chalk.cyan(' Controlando todo o fluxo de criação...'));
80
- console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
81
-
82
- // === AQUI INVOCARIA A SKILL ===
83
- //
84
- // Em produção, aqui seria:
85
- //
86
- // import { spawnMasterOrchestrator } from '../skill-wrapper';
87
- // await spawnMasterOrchestrator({ dir, platforms: [] });
88
- //
89
- // A skill faria:
90
- // - Perguntar modo (guiado/livre)
91
- // - Perguntar plataformas
92
- // - Acessar projeto
93
- // - Spawnar Page Analyzer Agent
94
- // - Spawnar Browser Agent
95
- // - Spawnar Meta Agent, Google Agent, TikTok Agent
96
- // - Spawnar Server Agent
97
- // - Gerar todos os arquivos
98
- // - Validar
99
-
100
- // === DEMONSTRAÇÃO DO QUE ACONTECE ===
101
-
102
- console.log(chalk.yellow.bold('\n🔄 MODO DEMONSTRAÇÃO\n'));
103
- console.log(chalk.gray(' Nesta versão, veja o fluxo que a skill executaria:\n'));
104
-
105
- const demoFlow = [
106
- ' [1] Perguntar: Como prefere configurar?',
107
- ' → Guiado ou Livre',
108
- ' [2] Perguntar: Quais plataformas de ads usa?',
109
- ' → Meta, Google, TikTok, LinkedIn, Spotify...',
110
- ' [3] Acessar: Seu projeto (GitHub ou local)',
111
- ' [4] Page Analyzer: Analisar páginas',
112
- ' → Detecta tipo de produto, nicho, formulários',
113
- ' [5] Browser Agent: Gerar cdpTrack.js',
114
- ' → Tracking SDK + micro-events',
115
- ' [6] Meta Agent: Gerar Pixel + CAPI',
116
- ' [7] Google Agent: Gerar GA4 + Google Ads',
117
- ' [8] TikTok Agent: Gerar Pixel + Events API',
118
- ' [9] Server Agent: Gerar Worker + D1',
119
- ' [10] Validator: Auditar código gerado',
120
- ' [11] Entregar: Arquivos no seu projeto',
121
- ' → tracking.config.js, cdpTrack.js, worker.js, schema.sql'
122
- ];
123
-
124
- demoFlow.forEach(line => console.log(chalk.cyan(line)));
125
-
126
- console.log(chalk.gray('\n━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'));
127
- console.log(chalk.green.bold(' ✅ SETUP CONCLUÍDO!'));
128
- console.log(chalk.gray('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'));
129
-
130
- console.log(chalk.cyan('📁 Arquivos gerados: ' + chalk.underline(dir || '.')));
131
- console.log(chalk.gray('\nPróximos passos:'));
132
- console.log(' 1. Configure seus API tokens no Wrangler');
133
- console.log(' 2. Faça o deploy: ' + chalk.bold('wrangler deploy'));
134
- console.log(' 3. Configure o domínio no Cloudflare Dashboard');
110
+ // ── PASSO 4: Credenciais Meta ──────────────────────────────────────────────
111
+ const creds = {};
112
+
113
+ if (adsPlataformas.includes('meta')) {
114
+ sep('4a. Credenciais — Meta Ads');
115
+ console.log(chalk.gray(' Onde encontrar: Meta Events Manager → Configurações → Pixel\n'));
116
+
117
+ const meta = await inquirer.prompt([
118
+ {
119
+ type: 'input',
120
+ name: 'pixelId',
121
+ message: 'Meta Pixel ID:',
122
+ validate: v => v.trim() ? true : 'Pixel ID é obrigatório',
123
+ filter: v => v.trim(),
124
+ },
125
+ {
126
+ type: 'password',
127
+ name: 'accessToken',
128
+ message: 'Meta Access Token (CAPI):',
129
+ mask: '*',
130
+ validate: v => v.trim() ? true : 'Access Token é obrigatório',
131
+ },
132
+ {
133
+ type: 'input',
134
+ name: 'testCode',
135
+ message: 'Meta Test Event Code (opcional, para homologação):',
136
+ default: '',
137
+ filter: v => v.trim(),
138
+ },
139
+ ]);
140
+ creds.meta = meta;
141
+ }
142
+
143
+ if (adsPlataformas.includes('google')) {
144
+ sep('4b. Credenciais Google Analytics 4');
145
+ console.log(chalk.gray(' Onde encontrar: GA4 Admin Data Streams → Measurement Protocol\n'));
146
+
147
+ const google = await inquirer.prompt([
148
+ {
149
+ type: 'input',
150
+ name: 'measurementId',
151
+ message: 'GA4 Measurement ID (G-XXXXXXXX):',
152
+ validate: v => /^G-[A-Z0-9]+$/i.test(v.trim()) ? true : 'Formato: G-XXXXXXXX',
153
+ filter: v => v.trim().toUpperCase(),
154
+ },
155
+ {
156
+ type: 'password',
157
+ name: 'apiSecret',
158
+ message: 'GA4 API Secret:',
159
+ mask: '*',
160
+ validate: v => v.trim() ? true : 'API Secret é obrigatório',
161
+ },
162
+ ]);
163
+ creds.google = google;
164
+ }
165
+
166
+ if (adsPlataformas.includes('tiktok')) {
167
+ sep('4c. Credenciais TikTok');
168
+ console.log(chalk.gray(' Onde encontrar: TikTok Events Manager → Pixel → Detalhes\n'));
169
+
170
+ const tiktok = await inquirer.prompt([
171
+ {
172
+ type: 'input',
173
+ name: 'pixelId',
174
+ message: 'TikTok Pixel ID:',
175
+ validate: v => v.trim() ? true : 'Pixel ID é obrigatório',
176
+ filter: v => v.trim(),
177
+ },
178
+ {
179
+ type: 'password',
180
+ name: 'accessToken',
181
+ message: 'TikTok Access Token:',
182
+ mask: '*',
183
+ validate: v => v.trim() ? true : 'Access Token é obrigatório',
184
+ },
185
+ ]);
186
+ creds.tiktok = tiktok;
187
+ }
188
+
189
+ // ── PASSO 5: Alertas WhatsApp ──────────────────────────────────────────────
190
+ sep('5. Alertas WhatsApp (opcional)');
191
+ console.log(chalk.gray(' Receba alertas de erros e match quality direto no WhatsApp.\n'));
192
+
193
+ const { wantsWhatsapp } = await inquirer.prompt([
194
+ {
195
+ type: 'confirm',
196
+ name: 'wantsWhatsapp',
197
+ message: 'Deseja ativar alertas via WhatsApp?',
198
+ default: true,
199
+ },
200
+ ]);
201
+
202
+ let whatsapp = {};
203
+ if (wantsWhatsapp) {
204
+ console.log(chalk.gray('\n Como configurar: acesse callmebot.com e adicione o bot no WhatsApp.'));
205
+ console.log(chalk.gray(' Envie "I allow callmebot to send me messages" para +34 644 35 78 48\n'));
206
+
207
+ whatsapp = await inquirer.prompt([
208
+ {
209
+ type: 'input',
210
+ name: 'phone',
211
+ message: 'Seu número com DDI (ex: +5511999999999):',
212
+ validate: v => /^\+\d{10,15}$/.test(v.trim()) ? true : 'Formato: +5511999999999',
213
+ filter: v => v.trim(),
214
+ },
215
+ {
216
+ type: 'password',
217
+ name: 'apiKey',
218
+ message: 'CallMeBot API Key:',
219
+ mask: '*',
220
+ validate: v => v.trim() ? true : 'API Key é obrigatória',
221
+ },
222
+ ]);
223
+ }
224
+
225
+ // ── GERAÇÃO DOS OUTPUTS ────────────────────────────────────────────────────
226
+ const spinner = ora('Gerando configurações...').start();
227
+ await sleep(600);
228
+ spinner.succeed('Configurações geradas!');
229
+
230
+ const domain = projeto.domain;
231
+
232
+ // Monta lista de secrets
233
+ const secrets = [];
234
+
235
+ if (creds.meta) {
236
+ secrets.push({ key: 'META_PIXEL_ID', value: creds.meta.pixelId, platform: 'Meta' });
237
+ secrets.push({ key: 'META_ACCESS_TOKEN', value: creds.meta.accessToken, platform: 'Meta' });
238
+ if (creds.meta.testCode) {
239
+ secrets.push({ key: 'META_TEST_CODE', value: creds.meta.testCode, platform: 'Meta (homologação)' });
240
+ }
241
+ }
242
+
243
+ if (creds.google) {
244
+ secrets.push({ key: 'GA4_MEASUREMENT_ID', value: creds.google.measurementId, platform: 'Google' });
245
+ secrets.push({ key: 'GA4_API_SECRET', value: creds.google.apiSecret, platform: 'Google' });
246
+ }
247
+
248
+ if (creds.tiktok) {
249
+ secrets.push({ key: 'TIKTOK_PIXEL_ID', value: creds.tiktok.pixelId, platform: 'TikTok' });
250
+ secrets.push({ key: 'TIKTOK_ACCESS_TOKEN', value: creds.tiktok.accessToken, platform: 'TikTok' });
135
251
  }
252
+
253
+ if (whatsapp.phone) {
254
+ secrets.push({ key: 'CALLMEBOT_PHONE', value: whatsapp.phone, platform: 'WhatsApp' });
255
+ secrets.push({ key: 'CALLMEBOT_API_KEY', value: whatsapp.apiKey, platform: 'WhatsApp' });
256
+ }
257
+
258
+ secrets.push({ key: 'SITE_DOMAIN', value: domain, platform: 'Worker' });
259
+
260
+ // Webhooks
261
+ const webhookUrls = {};
262
+ const webhookSecrets = [];
263
+ if (plataformas.includes('hotmart')) {
264
+ webhookUrls.hotmart = `https://${domain}/webhook/hotmart`;
265
+ webhookSecrets.push({ key: 'WEBHOOK_SECRET_HOTMART', platform: 'Hotmart', note: 'Gere uma senha forte qualquer' });
266
+ }
267
+ if (plataformas.includes('kiwify')) {
268
+ webhookUrls.kiwify = `https://${domain}/webhook/kiwify`;
269
+ webhookSecrets.push({ key: 'WEBHOOK_SECRET_KIWIFY', platform: 'Kiwify', note: 'Gere uma senha forte qualquer' });
270
+ }
271
+ if (plataformas.includes('ticto')) {
272
+ webhookUrls.ticto = `https://${domain}/webhook/ticto`;
273
+ webhookSecrets.push({ key: 'WEBHOOK_SECRET_TICTO', platform: 'Ticto', note: 'Chave HMAC configurada no painel Ticto' });
274
+ }
275
+
276
+ // ── EXIBIR RESULTADO ───────────────────────────────────────────────────────
277
+ console.log('\n');
278
+ sep('CONFIGURAÇÃO PRONTA');
279
+
280
+ console.log(chalk.green.bold('\n Projeto: ') + chalk.white(projeto.projectName));
281
+ console.log(chalk.green.bold(' Domínio: ') + chalk.white(domain));
282
+ console.log(chalk.green.bold(' Produto: ') + chalk.white(projeto.productType));
283
+ console.log(chalk.green.bold(' Ads: ') + chalk.white(adsPlataformas.join(', ')));
284
+
285
+ // Comandos de secrets
286
+ sep('Comandos para configurar os secrets');
287
+ console.log(chalk.gray(' Execute dentro da pasta server-edge-tracker:\n'));
288
+
289
+ for (const s of secrets) {
290
+ const masked = s.value.length > 4 ? s.value.slice(0, 4) + '*'.repeat(Math.min(s.value.length - 4, 8)) : '****';
291
+ console.log(chalk.cyan(` wrangler secret put ${s.key}`));
292
+ console.log(chalk.gray(` # ${s.platform} — valor: ${masked}\n`));
293
+ }
294
+
295
+ if (webhookSecrets.length > 0) {
296
+ console.log(chalk.yellow(' # Secrets de webhook (gere senhas e configure abaixo + no painel da plataforma):'));
297
+ for (const ws of webhookSecrets) {
298
+ console.log(chalk.cyan(` wrangler secret put ${ws.key}`));
299
+ console.log(chalk.gray(` # ${ws.platform}: ${ws.note}\n`));
300
+ }
301
+ }
302
+
303
+ // URLs de webhook
304
+ if (Object.keys(webhookUrls).length > 0) {
305
+ sep('URLs de Webhook para configurar nas plataformas');
306
+ for (const [plat, url] of Object.entries(webhookUrls)) {
307
+ console.log(chalk.green.bold(` ${plat.charAt(0).toUpperCase() + plat.slice(1)}:`));
308
+ console.log(chalk.white(` ${url}\n`));
309
+ }
310
+ }
311
+
312
+ // Checklist
313
+ sep('Próximos passos');
314
+ const steps = [
315
+ 'Executar todos os wrangler secret put acima',
316
+ 'Executar as migrations D1 (schema.sql → migrate-v7.sql)',
317
+ 'Executar: wrangler deploy',
318
+ ...(Object.keys(webhookUrls).length > 0 ? ['Configurar as URLs de webhook nas plataformas de venda'] : []),
319
+ 'Configurar Worker Route no Cloudflare: ' + domain + '/*',
320
+ 'Testar o endpoint: https://' + domain + '/health',
321
+ ...(adsPlataformas.includes('meta') && creds.meta?.testCode ? ['Testar eventos no Meta Events Manager com o Test Event Code'] : []),
322
+ ];
323
+
324
+ steps.forEach((step, i) => {
325
+ console.log(chalk.cyan(` [${i + 1}] `) + chalk.white(step));
326
+ });
327
+
328
+ // Salvar config (sem tokens reais)
329
+ const configOutput = {
330
+ generatedAt: new Date().toISOString(),
331
+ domain,
332
+ projectName: projeto.projectName,
333
+ productType: projeto.productType,
334
+ adsPlataformas,
335
+ checkoutPlataformas: plataformas,
336
+ webhookUrls,
337
+ secretKeys: secrets.map(s => s.key),
338
+ whatsappAlertsEnabled: !!whatsapp.phone,
339
+ };
340
+
341
+ const outputPath = join(dir, 'cdp-edge-setup.json');
342
+ try {
343
+ writeFileSync(outputPath, JSON.stringify(configOutput, null, 2), 'utf8');
344
+ console.log(chalk.gray(`\n Configuração salva em: ${outputPath} (sem tokens)\n`));
345
+ } catch {
346
+ // não crítico se não conseguir salvar
347
+ }
348
+
349
+ sep();
350
+ console.log(chalk.green.bold('\n Setup concluído! Passe os comandos acima para o agente instalar.\n'));
136
351
  }
137
352
 
138
353
  function sleep(ms) {
@@ -526,7 +526,7 @@ async function syncIdentity(DB, body) {
526
526
  const fp = body.fingerprint || null;
527
527
  if (!fp || !DB) return body;
528
528
 
529
- const existing = await DB.prepare(
529
+ const existing = await env.DB.prepare(
530
530
  'SELECT * FROM identity_graph WHERE fingerprint = ?'
531
531
  ).bind(fp).first();
532
532
 
@@ -541,7 +541,7 @@ async function syncIdentity(DB, body) {
541
541
  visit_count: (existing.visit_count || 1) + 1
542
542
  };
543
543
  } else {
544
- await DB.prepare(`
544
+ await env.DB.prepare(`
545
545
  INSERT OR IGNORE INTO identity_graph (fingerprint, fbp, fbc, ga_client_id, external_id, ttclid, first_utm)
546
546
  VALUES (?, ?, ?, ?, ?, ?, ?)
547
547
  `).bind(fp, body.fbp, body.fbc, body.ga_client_id, body.external_id, body.ttclid, JSON.stringify(body.utm || {})).run();
@@ -635,7 +635,7 @@ function buildCookieHeader(visitor, umbrellaDomain) {
635
635
  async function logBehavioralEvent(DB, body, visitor, engagementScore) {
636
636
  if (!DB) return;
637
637
 
638
- await DB.prepare(`
638
+ await env.DB.prepare(`
639
639
  INSERT OR REPLACE INTO behavioral_events (
640
640
  event_id, user_id, session_id,
641
641
  engagement_score, time_level, scroll_score, click_score, video_score, hover_score, intention_level,
@@ -972,7 +972,7 @@ CREATE INDEX IF NOT EXISTS idx_retry_scheduled ON retry_queue(scheduled_at, stat
972
972
  async function logEventSuccess(DB, platform, eventId) {
973
973
  if (!DB) return;
974
974
 
975
- await DB.prepare(`
975
+ await env.DB.prepare(`
976
976
  UPDATE events_log
977
977
  SET status = 'success',
978
978
  retry_count = 0,
@@ -988,7 +988,7 @@ async function logEventFailure(DB, platform, eventId, errorMessage, retryCount)
988
988
  const maxRetries = 3;
989
989
  const isFinalFailure = retryCount >= maxRetries;
990
990
 
991
- await DB.prepare(`
991
+ await env.DB.prepare(`
992
992
  UPDATE events_log
993
993
  SET status = ?,
994
994
  retry_count = ?,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cdp-edge",
3
- "version": "1.18.3",
3
+ "version": "1.20.0",
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",
@@ -28,7 +28,12 @@
28
28
  "test:unit:hash": "node tests/unit/hashing.test.js",
29
29
  "test:unit:dedup": "node tests/unit/deduplication.test.js",
30
30
  "test:unit:payload": "node tests/unit/payload-validation.test.js",
31
- "test:all": "npm run test:unit"
31
+ "test:all": "npm run test:unit",
32
+ "test:integration": "cd tests/integration && npx vitest run",
33
+ "agents:check": "node scripts/validate-agents.js",
34
+ "agents:sync": "node scripts/sync-agents.js",
35
+ "agents:sync:list": "node scripts/sync-agents.js --list",
36
+ "agents:sync:all": "node scripts/sync-agents.js --apply-all"
32
37
  },
33
38
  "keywords": [
34
39
  "pixel",