@analitikisler/ai-tag-debugger 0.6.0 → 0.6.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.6.1 (2026-10-12)
4
+
5
+ - GA4 events were reported as "sent 2 times" on sites with Google signals or ads features. gtag.js also sends a copy of those events to `www.google.com/g/collect` (or `doubleclick.net`), which GA4 doesn't count. Those copies are now still answered locally and listed under network requests, but no longer counted as hits. Requests to `analytics.google.com` still count: it is a primary GA4 endpoint.
6
+ - GA4 requests answered locally showed as "failed" in the network list, because Chromium reports answered keepalive requests as aborted. They now show the 204 the page received.
7
+ - Hits are read as each request is sent, rather than when it is answered. A request sent while the page navigates away is then still counted, even if it is cancelled before it can be answered. Requests the private-network guard stops are taken back out and shown as "blocked".
8
+
3
9
  ## 0.6.0 (2026-10-10)
4
10
 
5
11
  - **Report v3.** The HTML report has four tabs: Overview, Findings, Journeys, and Scope and plan. They switch with the URL hash, so links work across tabs and every view can be shared. Still no scripts; printing shows everything.
package/README.md CHANGED
@@ -1,3 +1,5 @@
1
+ English | [Türkçe](README.tr.md)
2
+
1
3
  # AI Tag Debugger
2
4
 
3
5
  **An open-source AI agent that QA-tests your website's analytics and ad tracking.**
package/README.tr.md ADDED
@@ -0,0 +1,346 @@
1
+ [English](README.md) | Türkçe
2
+
3
+ # AI Tag Debugger
4
+
5
+ **Sitenizdeki analitik ve reklam ölçümlemesini test eden açık kaynak bir yapay zekâ ajanı.**
6
+
7
+ Bir yapay zekâ modeli, gerçek bir tarayıcıda önemli senaryolarınızı (ürün → sepet → ödeme, form doldurma) gerçek bir ziyaretçi gibi gezer. Gezinirken `ai-tag-debugger`, GA4, Google Ads, Meta Pixel ve TikTok'a giden her isteği ve her dataLayer push'unu kaydeder. Sonra bunları ölçümleme planınıza ve Consent Mode ayarlarınıza göre kontrol eder ve neyin bozulduğunu, nasıl düzeltileceğini anlaşılır bir dille, Türkçe ya da İngilizce bir raporda yazar.
8
+
9
+ İki şekilde kullanabilirsiniz:
10
+
11
+ - **Terminalden ya da CI'dan**, Anthropic (Claude), OpenAI, Google Gemini ya da OpenAI uyumlu herhangi bir API (Kimi, GLM, DeepSeek, OpenRouter, bilgisayarınızdaki Ollama ve daha fazlası) için kendi API anahtarınızla.
12
+ - **Yapay zekâ asistanınızdan** (Claude, ChatGPT, Gemini ve MCP destekleyen diğer uygulamalar). Testi uygulamanın kendi modeli yürütür, yani ayrı bir API anahtarına gerek yoktur.
13
+
14
+ [Analitik İşler](https://analitikisler.com/tr/) tarafından geliştirildi. MIT lisanslı.
15
+
16
+ ```
17
+ ▶ checkout (mobile)
18
+ Accept cookies
19
+ Open the first product
20
+ Add to cart
21
+ Open the cart
22
+ Go to checkout
23
+ …
24
+ Place the order
25
+ ✓ All scripted steps completed. · 20 tracking hits captured
26
+
27
+ 2 broken · 1 risk · 1 warning · 4 ok
28
+ Report: reports/demo/report.html
29
+ ```
30
+
31
+ ## Neden
32
+
33
+ Ölçümleme sessizce bozulur. Yeniden tasarımda bir butonun adı değişir, yeni bir şablon bir alanı unutur, çerez onayı banner'ındaki bir güncelleme pikselleri erken çalıştırır. Kimseye uyarı gelmez. Reklam platformları haftalarca hatalı dönüşümlerle optimizasyon yaptıktan sonra fark edersiniz.
34
+
35
+ Tag'leri elle test etmek bunu yakalar, ama zahmetli olduğu için her sürümden sonra pek yapılmaz. `ai-tag-debugger` bunu CI'da çalıştırabileceğiniz tek bir komuta dönüştürür.
36
+
37
+ ## Neleri kontrol eder
38
+
39
+ | Kontrol | Örnek bulgu |
40
+ |---|---|
41
+ | Eksik event'ler | `purchase`, ödeme senaryosunda hiç gönderilmedi |
42
+ | Eksik parametreler | `purchase`, mobilde `value` ve `currency` olmadan gönderildi |
43
+ | Tekrar eden event'ler | `add_to_cart` tek tıklamada iki kez gönderildi |
44
+ | Çerez onayı | Meta `PageView`, ziyaretçi çerezleri kabul etmeden gönderildi; Consent Mode varsayılanı yok ya da çok geç ayarlanmış |
45
+ | Kişisel veri | Bir e-posta adresi reklam platformuna açık metin olarak gönderildi |
46
+ | Planda olmayan event'ler | Ölçümleme planınızda bulunmayan event'ler gönderiliyor |
47
+
48
+ Platformlar: GA4 (server-side GTM adresleri dahil), Google Ads dönüşümleri, Meta Pixel, TikTok Pixel ve temel X Pixel event'leri.
49
+
50
+ ### Kontrol edilecek platformları seçin
51
+
52
+ Varsayılan olarak bir test GA4, Google Ads, Meta ve TikTok'u kontrol eder. Başka platformları `--providers ga4,meta,x` ile, ayar dosyasındaki `"providers"` ile ya da MCP `start_audit` ve `run_audit` araçlarındaki `providers` ile seçebilirsiniz. Seçenekler: `ga4`, `google_ads`, `meta`, `tiktok`, `x`, `linkedin`, `microsoft_ads`, `pinterest`, `snapchat`, `clarity`, `hotjar`, `doubleclick` ve `adsense`. GA4, Google Ads, Meta, TikTok ve X için event'ler ayrıştırılır; diğerlerinin ağ istekleri gösterilir.
53
+
54
+ Seçmediğiniz platformlara giden istekler de yerelde yanıtlanır, yani o hesaplara da ulaşmaz. Bu istekler zaman akışına, CSV'ye ve kontrollere alınmaz, sadece sayılır: rapor bunları istek sayılarıyla listeler (örneğin "Kapsam dışı 61 istek atlandı: Microsoft Clarity, DoubleClick") ve her adım kaç istek atladığını gösterir. `--providers` vermezseniz, ölçümleme planınızda geçen her platform varsayılan listeye eklenir. Dışarıda bıraktığınız platformlar için planlanmış event'ler eksik olarak raporlanmaz, tek bir bilgi bulgusunda listelenir. Tag yöneticisi ve çerez onayı platformu istekleri her zaman gösterilir.
55
+
56
+ ## Nasıl çalışır
57
+
58
+ 1. **Gezinme:** yapay zekâ modeline sade bir dille yazılmış bir hedef ("herhangi bir ürünü sepete ekle ve test ödeme yöntemiyle siparişi tamamla") ve sayfanın metin görünümü verilir. Model, Playwright üzerinden adım adım iki araç kullanır: `browser_action` (goto, click, fill, select, scroll, wait) ve `finish`.
59
+ 2. **Kayıt:** ölçümleme adreslerine giden her istek ayrıştırılıp standart bir biçime getirilir ve onu oluşturan adımla etiketlenir. Her dataLayer push'u, `gtag('consent', ...)` komutları dahil, bir init script ile kaydedilir. Varsayılan olarak GA4, Google Ads, Meta, TikTok, Microsoft Ads, LinkedIn, Pinterest, Snapchat ve X'in veri toplama istekleri yerelde yanıtlanır, yani **test trafiği bu gerçek hesaplara ulaşmaz**. Diğer sağlayıcıların tag'leri isteklerini göndermeye devam eder; zaman akışının ağ bölümü her çağrıyı ve yerelde yanıtlanıp yanıtlanmadığını gösterir.
60
+ 3. **Kontrol:** kurallar, yakalanan istekleri ölçümleme planınızla karşılaştırır. Bu kısım hiçbir API çağrısı yapmaz, bu yüzden sonuçlar tekrarlanabilirdir.
61
+ 4. **Açıklama:** model bulguları, her adımdaki istekleri ve (isterseniz) GTM container dışa aktarımınızı okur. Her sorunun olası nedenini ve çözümünü, ilgili tag ve trigger'ları adıyla anarak yazar.
62
+
63
+ ## Yapay zekâ sağlayıcıları
64
+
65
+ Kendi API anahtarınızı kullanırsınız. `ai-tag-debugger` her sağlayıcının resmi SDK'sını kullanır:
66
+
67
+ | Sağlayıcı | API anahtarı değişkeni | Varsayılan model |
68
+ |---|---|---|
69
+ | Anthropic (Claude) | `ANTHROPIC_API_KEY` | `claude-opus-5-5` |
70
+ | OpenAI | `OPENAI_API_KEY` | `gpt-5.5` |
71
+ | Google Gemini | `GEMINI_API_KEY` | `gemini-pro-latest` |
72
+
73
+ Sağlayıcı, hangi anahtar tanımlıysa ona göre seçilir (birden fazlası tanımlıysa önce Anthropic). Açıkça seçmek için `--provider anthropic|openai|gemini` ya da ayar dosyasında `"provider"` kullanın; başka bir model için `--model` verin. Gezinme, maliyeti düşük tutmak için düşük reasoning effort ister; açıklamalar ise yüksek effort ve yapılandırılmış JSON çıktısı ister.
74
+
75
+ ### OpenAI uyumlu her API: Kimi, GLM, DeepSeek, OpenRouter, Ollama ve daha fazlası
76
+
77
+ Birçok sağlayıcı, OpenAI'ınkine benzer çalışan bir API sunar. `ai-tag-debugger`'ı adresini ve model kimliğini vererek bu API'lere yönlendirebilirsiniz:
78
+
79
+ ```bash
80
+ export OPENAI_COMPATIBLE_API_KEY=... # leave unset for a local Ollama
81
+ ai-tag-debugger run --site https://yourshop.com --journey add_to_cart \
82
+ --provider openai-compatible --base-url <API address> --model <model id>
83
+ ```
84
+
85
+ | Sağlayıcı | `--base-url` |
86
+ |---|---|
87
+ | Moonshot (Kimi) | `https://api.moonshot.ai/v1` |
88
+ | Z.ai (GLM) | `https://api.z.ai/api/paas/v4` |
89
+ | DeepSeek | `https://api.deepseek.com` |
90
+ | Mistral | `https://api.mistral.ai/v1` |
91
+ | Groq | `https://api.groq.com/openai/v1` |
92
+ | OpenRouter (yüzlerce model) | `https://openrouter.ai/api/v1` |
93
+ | Bilgisayarınızdaki Ollama | `http://localhost:11434/v1` |
94
+
95
+ Model kimliğini sağlayıcınızın dokümantasyonundan alın. Bayrakların yerine `OPENAI_COMPATIBLE_BASE_URL` ve `OPENAI_COMPATIBLE_MODEL` değişkenlerini ya da ayar dosyasında `"provider"`, `"base_url"` ve `"model"` alanlarını da kullanabilirsiniz. API katı JSON şeması döndüremiyorsa, açıklamalar düz JSON moduna geçer.
96
+
97
+ **Model seçimi hakkında.** Yapay zekâ, 5 ile 30 adım arasında, her adımda bir araç çağırarak gezinir. Modeller bunu ne kadar güvenilir yaptıkları konusunda çok farklıdır: küçük ve yerel modeller yanlış yere tıklayabilir, döngüye girebilir ya da erken durabilir. Kontroller modelin ne yaptığından bağımsız olarak doğru kalır, çünkü yakalanan isteklere dayanır. Ama tamamlanmayan bir senaryo, sonraki adımlardaki event'leri gösteremez. Bir model zorlanıyorsa daha büyük bir model, kısa hedefli hazır bir senaryo ya da `--max-steps` deneyin. Test edilmiş olanlar Anthropic, OpenAI ve Gemini varsayılanlarıdır.
98
+
99
+ Yeni bir yerleşik sağlayıcı eklemek için [`src/providers.js`](src/providers.js) içinde iki fonksiyon yazmanız yeterli: araç çağıran bir konuşma ve yapılandırılmış bir JSON çağrısı.
100
+
101
+ ## Hızlı başlangıç
102
+
103
+ Node 20 ve üstü gerekir.
104
+
105
+ ```bash
106
+ npm install -g @analitikisler/ai-tag-debugger
107
+ npx -y playwright@1.56.1 install chromium # the browser version this release uses
108
+ export ANTHROPIC_API_KEY=... # or OPENAI_API_KEY, or GEMINI_API_KEY
109
+
110
+ ai-tag-debugger run --site https://staging.yourshop.com --journey add_to_cart --headed
111
+ ```
112
+
113
+ Ya da kurmadan çalıştırın: `npx -y @analitikisler/ai-tag-debugger run --site … --journey add_to_cart`.
114
+
115
+ ### Demo mağazayı deneyin (kaynak koddan)
116
+
117
+ Depoda, bilerek bırakılmış üç ölçümleme hatası olan bir demo mağaza var:
118
+
119
+ ```bash
120
+ git clone https://github.com/analitikisler/ai-tag-debugger.git
121
+ cd ai-tag-debugger
122
+ npm install
123
+ npx playwright install chromium # run again after upgrading, so the browser matches Playwright
124
+ npm run demo:serve # in one terminal: http://localhost:4321
125
+ node src/cli.js run --config examples/demo.config.json --out reports/demo
126
+ open reports/demo/report.html
127
+ ```
128
+
129
+ Henüz API anahtarınız yok mu? Kayıtlı adımları hiçbir yapay zekâ modeli kullanmadan tekrar oynatın:
130
+
131
+ ```bash
132
+ node src/cli.js run --config examples/demo.config.json --scripted --no-ai --out reports/demo
133
+ ```
134
+
135
+ ## Neyi test edeceğinizi seçin
136
+
137
+ Başlamak için ayar dosyasına gerek yok. Hazır senaryoları seçin, kendi senaryonuzu kendi cümlenizle yazın ve kontrolü önem verdiğiniz event'lerle sınırlayın:
138
+
139
+ ```bash
140
+ # Ready-made journeys come with a goal and the GA4 and Meta events to expect.
141
+ ai-tag-debugger run --site https://staging.yourshop.com --journey purchase --journey sign_up
142
+
143
+ # Describe a journey in your own words. The events you name are expected on it.
144
+ ai-tag-debugger run --site https://yoursite.com --goal "Subscribe to the newsletter in the footer" --events newsletter_signup
145
+
146
+ # Only check some events.
147
+ ai-tag-debugger run --config ai-tag-debugger.config.json --events purchase,add_to_cart
148
+ ```
149
+
150
+ Hedefi Türkçe de yazabilirsiniz, örneğin `--goal "Sayfanın altındaki bültene abone ol"`.
151
+
152
+ | Hazır senaryo | Yapay zekânın yaptığı | Beklenen event'ler |
153
+ |---|---|---|
154
+ | `purchase` (ya da `checkout`) | Ürün → sepet → ödeme → test ödeme seçeneğiyle sipariş | GA4 `view_item`, `add_to_cart`, `begin_checkout`, `purchase`; Meta `AddToCart`, `InitiateCheckout`, `Purchase` |
155
+ | `add_to_cart` (ya da `cart`) | Bir ürün açar ve sepete ekler | GA4 `view_item`, `add_to_cart`; Meta `AddToCart` |
156
+ | `sign_up` (ya da `signup`, `register`) | Test verileriyle hesap oluşturur | GA4 `sign_up`; Meta `CompleteRegistration` |
157
+ | `lead` (ya da `contact`, `form`) | İletişim, teklif ya da demo formunu doldurup gönderir | GA4 `generate_lead`; Meta `Lead` |
158
+ | `search` | Sitede arama yapar ve bir sonucu açar | `search_term` ile GA4 `search`; Meta `Search` |
159
+
160
+ `--journey`, ayar dosyanızdaki senaryoları da id'leriyle seçer. Ayar dosyasıyla birlikte kullanıldığında hazır senaryolar kendi event'lerini planınıza ekler.
161
+
162
+ ### API kullanımını düşük tutmak
163
+
164
+ Her gezinme adımı bir model çağrısıdır. Yani kullanım **senaryo × ekran boyutu × adım** ile artar, buna sonda bulguları açıklayan bir çağrı eklenir. Kontroller hiçbir zaman modeli çağırmaz.
165
+
166
+ - **Sadece ihtiyacınız olan senaryoları çalıştırın.** En büyük tasarruf budur: tam `purchase` senaryosu yerine `--journey add_to_cart`, on ya da daha fazla adım yerine birkaç adım sürer.
167
+ - **Tek ekran boyutu kullanın.** `--viewport mobile`, masaüstü ve mobile göre çağrıları yarıya indirir.
168
+ - **Adım sayısını sınırlayın.** `--max-steps 15`, yolunu kaybeden bir senaryoyu durdurur.
169
+ - **Büyük sayfaların daha pahalı olduğunu unutmayın.** Her adım modele sayfanın görünümünü (en fazla 250 öğe ve 3.000 karakter metin) gönderir. Bu yüzden kalabalık sayfalar adım başına daha fazla token kullanır.
170
+ - **Raporu odaklı tutmak için `--events` kullanın.** Gezinmeyi kısaltmadığı için kullanımı pek değiştirmez.
171
+ - **Yapay zekâ olmadan tekrar oynatın.** Bir yapay zekâ çalıştırmasından sonra adımları `journeys[].steps` içine kopyalayın ve her deploy'da `--scripted` (hiç model çağrısı olmaması için `--no-ai` ile birlikte) kullanın.
172
+ - **Bir yapay zekâ asistanı kullanın.** MCP üzerinden gezinmeyi asistanın kendi modeli yapar, hiç API anahtarı kullanılmaz.
173
+
174
+ ## Sitenizi ayarlayın
175
+
176
+ `ai-tag-debugger.config.json`:
177
+
178
+ ```json
179
+ {
180
+ "site": "https://staging.yourshop.com",
181
+ "plan": "measurement-plan.json",
182
+ "gtm_container": "GTM-XXXX_workspace.json",
183
+ "viewports": ["desktop", "mobile"],
184
+ "journeys": [
185
+ {
186
+ "id": "checkout",
187
+ "goal": "Accept cookies, add any product to the cart and complete checkout using the test payment method."
188
+ }
189
+ ]
190
+ }
191
+ ```
192
+
193
+ | Alan | Anlamı |
194
+ |---|---|
195
+ | `site` | Ana adres. Mümkünse staging kullanın. |
196
+ | `plan` | Ölçümleme planı: bir dosya yolu ya da satır içi bir nesne (aşağıda). Hazır senaryolarla isteğe bağlıdır. |
197
+ | `gtm_container` | İsteğe bağlı GTM container dışa aktarımı (Admin → Export container). Böylece açıklamalarda tag ve trigger'lar adıyla geçer. |
198
+ | `viewports` | `desktop` ve/veya `mobile`. |
199
+ | `journeys` | `--journey` ile hazır senaryo seçtiğinizde ya da `--goal` ile kendi senaryonuzu yazdığınızda isteğe bağlıdır. |
200
+ | `journeys[].goal` | Yapay zekânın yapması gereken, sade bir dille. |
201
+ | `journeys[].start` | İsteğe bağlı başlangıç yolu (varsayılan `/`). |
202
+ | `journeys[].steps` | `--scripted` çalıştırmaları için isteğe bağlı sabit adımlar: `{ "action": "click", "selector": "#add-to-cart" }`. |
203
+ | `allowed_hosts` | Senaryoların gidebileceği diğer adresler, örneğin harici bir ödeme sayfası için `["checkout.payments-provider.com"]`. Bunun dışında tarayıcı `site` adresinde ve alt alan adlarında kalır. `--allow-host` ile aynıdır. |
204
+ | `providers` | Kontrol edilecek ölçümleme platformları, örneğin `["ga4", "meta"]`. Varsayılan `["ga4", "google_ads", "meta", "tiktok"]`. `--providers` ile aynıdır. |
205
+ | `block_hits` | Varsayılan `true`. İsteklerin gerçek adreslerinize ulaşmasına izin vermek için `false` yapın. |
206
+ | `language` | Rapor dili, `en` (varsayılan) ya da `tr`. `--lang` ile aynıdır. |
207
+ | `keep_bodies` | Varsayılan `false`. Ölçümleme isteklerini kullanıcı verilerini gizlemeden, gönderildiği haliyle saklamak için `true` yapın. `--keep-bodies` ile aynıdır. |
208
+ | `branding` | Varsayılan `true`. Analitik İşler logosunu ve "AI Tag Debugger ile oluşturuldu · analitikisler.com" satırını raporlardan çıkarmak için `false` yapın. `--no-branding` ya da MCP `finish_audit` ve `run_audit` araçlarında `branding: false` ile aynıdır. |
209
+ | `provider` | İsteğe bağlı: `anthropic`, `openai`, `gemini` ya da `openai-compatible`. `--provider` ile aynıdır. |
210
+ | `base_url`, `model` | İsteğe bağlı: API adresi ve model kimliği, örneğin `openai-compatible` için. `--base-url` ve `--model` ile aynıdır. |
211
+
212
+ `measurement-plan.json`:
213
+
214
+ ```json
215
+ {
216
+ "consent_mode": true,
217
+ "events": [
218
+ { "platform": "ga4", "name": "add_to_cart", "required_params": ["currency", "value", "items"] },
219
+ { "platform": "ga4", "name": "purchase", "journeys": ["checkout"], "required_params": ["transaction_id", "currency", "value", "items"] },
220
+ { "platform": "meta", "name": "Purchase", "journeys": ["checkout"], "required_params": ["value", "currency"] }
221
+ ]
222
+ }
223
+ ```
224
+
225
+ `platform` değeri `ga4`, `google_ads`, `meta` ya da `tiktok` olabilir. `journeys`, bir event'in hangi senaryolarda beklendiğini sınırlar (varsayılan: her senaryo). Tek bir işlemde birden fazla kez gönderilmesi normal olan event'ler için `allow_multiple: true` kullanın.
226
+
227
+ ## CLI
228
+
229
+ ```
230
+ ai-tag-debugger run [--site <url>] [--config <file>] [--journey <id>] [--goal "<text>"] [--events <names>]
231
+ [--max-steps <n>] [--out <dir>] [--viewport desktop|mobile] [--providers <list>]
232
+ [--provider anthropic|openai|gemini|openai-compatible] [--model <id>] [--base-url <url>]
233
+ [--lang en|tr] [--allow-host <host>] [--keep-bodies] [--scripted] [--no-ai] [--fail-on broken|risk|warning|never] [--headed]
234
+ ai-tag-debugger mcp [--http] [--port <n>] [--host <addr>] [--token <secret>] [--public-url <https url>] [--reports <dir>]
235
+ [--allow-private-network]
236
+ ```
237
+
238
+ Bulgular `--fail-on` seviyesine (varsayılan `broken`) ulaştığında çıkış kodu `1` olur, böylece bir deploy'u durdurabilir. Her deploy sonrası çalıştırmak için [`examples/github-actions/ai-tag-debugger.yml`](examples/github-actions/ai-tag-debugger.yml) örneğine bakın.
239
+
240
+ Çıktılar:
241
+
242
+ - `report.html`: dört sekmeli tek bir dosya. Sekmeler arası bağlantılar çalışır (bir bulgudaki "Nerede" etiketi o adımı açar) ve her görünümün kaydedip paylaşabileceğiniz kendi adresi vardır.
243
+ - **Genel bakış**: sorun sayıları, yapay zekâ özeti (en önemli sorun en üstte, ayrıca bulguların kapsamadığı ama kontrol etmeye değer noktalar), sorun sayılarıyla senaryolar ve kontrol edilen platformlar.
244
+ - **Bulgular**: önem derecesine göre gruplanır ve filtrelenebilir. Her bulgunun etkisi, olası nedeni, kanıtı, numaralı çözüm adımları ve oluştuğu adımlar vardır.
245
+ - **Yolculuklar**: her seferinde bir senaryo; solda adımları, sağda seçili adım. İlk sorunlu adımda açılır. Her adımın üç sekmesi vardır:
246
+ - **Olaylar**: parametreleri, çerez onayı durumu, arkasındaki dataLayer push'u ve isteğiyle birlikte her ölçümleme isteği; bir bulgu ona işaret ediyorsa bir not.
247
+ - **dataLayer**: adımdaki her push.
248
+ - **Ağ istekleri**: seçilen platformlara giden her istek; her parametrede `?` ipucu olan bir parametre tablosuna çözülmüş halde. Toplu gönderilen bir GA4 isteği, içindeki her event'i gösterir.
249
+ - **Kapsam ve plan**: planlanan her event'in her senaryoda gönderilip gönderilmediği ve istek sayılarıyla kontrol edilen ya da dışarıda bırakılan platformlar.
250
+
251
+ Event'ler ve istekler kaynağa göre filtrelenebilir. GA4 ürün dizeleri (`pr1`…) ve dataLayer `ecommerce.items` ürün kartları olarak, özel ürün parametreleri ayrı bir grupta gösterilir. Raporda script yoktur: sekmeler ve filtreler düz HTML ve CSS'tir (Chrome 105, Safari 15.4 ya da Firefox 121 ve üstü gerekir) ve yazdırınca tüm sekmeler görünür. Logolar dosyanın içindedir, internetten hiçbir şey yüklenmez. `--no-branding` Analitik İşler logosunu ve imza satırını çıkarır.
252
+ - `timeline.csv`: aynı zaman akışı; her istek, dataLayer push'u ve ağ çağrısı bir satır, Excel ya da Google Sheets için hazır.
253
+ - `report.md`: raporun Markdown hali, örneğin bir pull request yorumu için.
254
+ - `capture.json`: yakalanan tüm veriler, kendi analiziniz için.
255
+
256
+ Raporlar bir imza satırıyla biter: "AI Tag Debugger ile oluşturuldu · analitikisler.com". Buradaki alan adı düz bir bağlantıdır (resim yok, takip yok, referrer gönderilmez, böylece rapor adresleri sızmaz). `--no-branding` bu satırı logoyla birlikte kaldırır.
257
+
258
+ Türkçe rapor için `--lang tr` kullanın. Bulgu metinleri, başlıklar, modelin açıklamaları ve çözümleri Türkçe yazılır; ajan da adımlarını Türkçe anlatır.
259
+
260
+ ## Yapay zekâ asistanınızdan kullanın (MCP)
261
+
262
+ `ai-tag-debugger mcp`, bir [Model Context Protocol](https://modelcontextprotocol.io) sunucusu başlatır. Böylece testi asistanınızın modeli kendisi yürütür: "staging.magazam.com'da ödeme adımlarının ölçümlemesini kontrol et" dersiniz, tarayıcıyı açar, adımları tıklar ve bulguları sohbette açıklar. API anahtarı gerekmez, çünkü düşünme işini uygulamanın kendi modeli yapar.
263
+
264
+ Sunucunun sunduğu araçlar:
265
+
266
+ | Araç | Ne yapar |
267
+ |---|---|
268
+ | `start_audit` | Bir adreste yeni bir tarayıcı açar ve tüm ölçümleme isteklerini kaydetmeye başlar. Hedefini ve beklenen event'lerini almak için hazır bir senaryo (`purchase`, `sign_up`, …), neyin kontrol edileceğini seçmek için `providers` verin. Asistan önce size hangi platformların kontrol edileceğini sorar. |
269
+ | `browser_action` | Tek bir click, fill, select, goto, scroll ya da wait. Sayfayı ve o adımda gönderilen ölçümleme isteklerini döndürür. |
270
+ | `get_page` | Mevcut sayfayı gösterir. |
271
+ | `finish_audit` | Her şeyi ölçümleme planınıza (JSON) ya da hazır senaryonun event'lerine göre, isterseniz sadece adını verdiğiniz `events` için kontrol eder ve zaman akışıyla birlikte tam HTML raporu yazar. |
272
+ | `run_audit` | Bir ayar dosyasını baştan sona çalıştırır (sadece yerelde). |
273
+
274
+ Tarayıcı, sunucunun çalıştığı bilgisayarda çalışır. Bu yüzden staging ve `localhost` sitelerinize erişebilir. Ölçümleme istekleri her zamanki gibi yerelde yanıtlanır.
275
+
276
+ ### Yerel sunucu başlatan uygulamalar: Claude Desktop, Gemini CLI, Cursor, VS Code, Claude Code
277
+
278
+ Sunucuyu uygulamanın MCP ayarlarına ekleyin. Claude Desktop'ta bu Settings → Developer → Edit Config'tir (`claude_desktop_config.json`):
279
+
280
+ ```json
281
+ {
282
+ "mcpServers": {
283
+ "ai-tag-debugger": {
284
+ "command": "npx",
285
+ "args": ["-y", "@analitikisler/ai-tag-debugger", "mcp"]
286
+ }
287
+ }
288
+ }
289
+ ```
290
+
291
+ Tarayıcıyı bir kez `npx -y playwright@1.56.1 install chromium` ile kurun. Gemini CLI, `~/.gemini/settings.json` içinde aynı `mcpServers` bloğunu kullanır. Claude Code için: `claude mcp add ai-tag-debugger -- npx -y @analitikisler/ai-tag-debugger mcp`. Uygulamayı yeniden başlatın, ai-tag-debugger araçları görünür. Uygulama `npx`'i bulamazsa tam yolunu kullanın (`which npx`; örneğin Homebrew kurulu bir Mac'te `/opt/homebrew/bin/npx`). Raporlar `~/ai-tag-debugger-reports` klasörüne yazılır.
292
+
293
+ Kaynak koddan mı çalıştırıyorsunuz? `"command": "node"` ve `"args": ["/path/to/ai-tag-debugger/src/cli.js", "mcp"]` kullanın.
294
+
295
+ ### Bir adrese bağlanan uygulamalar: ChatGPT, Gemini
296
+
297
+ Bu uygulamalar MCP sunucularına HTTPS üzerinden bağlanır (ChatGPT'de, planınıza bağlı olarak developer mode connector'ları ile). Sunucuyu HTTP modunda başlatın ve bir tünelle internete açın:
298
+
299
+ ```bash
300
+ cloudflared tunnel --url http://localhost:8787 # or: ngrok http 8787; note the https address it prints
301
+ npx -y @analitikisler/ai-tag-debugger mcp --http --port 8787 --public-url https://<your-tunnel-address>
302
+ ```
303
+
304
+ Sunucu, rastgele gizli bir yol içeren bir adres yazdırır, örneğin `https://<your-tunnel-address>/mcp/<secret>`. Bu adresi uygulamaya connector olarak ekleyin. Adrese sahip olan herkes bilgisayarınızdaki tarayıcıyı kullanabilir; adresi kimseyle paylaşmayın ve işiniz bitince sunucuyu kapatın. Kendi belirlediğiniz bir gizli anahtar (`--token`) en az 16 karakter olmalıdır.
305
+
306
+ HTTP modunda sunucu, yereldekinden daha kısıtlıdır:
307
+
308
+ - Diskinizden ölçümleme planı ya da ayar dosyası okumaz (planı sohbette JSON metni olarak verin) ve `run_audit` sunulmaz. Sadece kendi yazdığı raporları sunar.
309
+ - Tarayıcı sadece `http` ve `https` sayfalarını açar; `localhost` ve özel ağ adreslerini (`192.168.x.x` ya da `169.254.169.254` gibi) reddeder, böylece adres ağınızın içini görmek için kullanılamaz. WebSocket'ler dahil tüm tarayıcı trafiği küçük bir yerleşik proxy'den geçer. Bu proxy her adresi bir kez çözümler ve sadece kontrol ettiği IP'ye bağlanır, böylece test sırasında değişen bir DNS yanıtı bu kontrolü aşamaz. Yerel bir staging sitesini yine de bu şekilde test etmek için sunucuyu `--allow-private-network` ile başlatın.
310
+ - 15 dakika açık kalan testler kapatılır ve istek gövdeleri 1 MB ile sınırlıdır.
311
+
312
+ Tünele gerek kalmayan, hazır bir adresle barındırılan sunucu, [Analitik İşler](https://analitikisler.com/tr/) Cloud sürümünün bir parçasıdır.
313
+
314
+ ## Güvenlik
315
+
316
+ - Ajana bariz test verileri kullanması ve **asla gerçek ödeme bilgisi girmemesi** söylenir. Bir senaryo gerçek bir ödeme ya da hesap gerektiriyorsa, durur ve bunu raporlar.
317
+ - [Nasıl çalışır](#nasıl-çalışır) bölümünde listelenen platformlara giden ölçümleme istekleri varsayılan olarak yerelde yanıtlanır, böylece bu analitik ve reklam hesaplarına test dönüşümleri gitmez.
318
+ - Tarayıcı test edilen sitede ve alt alan adlarında, ayrıca `allowed_hosts` içindeki adreslerde kalır. Sadece `http` ve `https` sayfalarını açar.
319
+ - Modele, sayfa içeriğinin talimat değil veri olduğu söylenir. Bir prompt kuralı, modelin hiçbir zaman yanıltılmayacağını garanti edemez; bu yüzden yukarıdaki gezinme sınırları, yanıltılmış bir modelin yapabileceklerini kısıtlar.
320
+ - Tarayıcı, ortamında API anahtarlarınız ve diğer gizli bilgiler olmadan çalışır.
321
+ - **Reklam eşleştirmesi için gönderilen kullanıcı verileri**, `capture.json` dosyasına kaydedilmeden ya da raporda gösterilmeden önce **gizlenir**: GA4 ve Google Ads enhanced conversions (`em`, `ph`, `uid`, …), Meta advanced matching (`ud[*]`, `udff[*]`), TikTok `context.user` içindeki e-postalar, telefon numaraları, adlar, adresler ve kullanıcı kimlikleri ile sitenin enhanced conversions için dataLayer'a gönderdiği `user_data`. E-posta adresine benzeyen her değer, alanın adı ne olursa olsun gizlenir. Her değer `[redacted: 64 chars, looks hashed]` gibi bir işarete dönüşür; hash'lenmemiş bir değer kişisel veri riski olarak raporlanır. `--keep-bodies` (`"keep_bodies": true`) hata ayıklama için istekleri gönderildiği haliyle saklar.
322
+ - Saklanan istek gövdeleri istek başına 32 KB ve senaryo başına 2 MB ile sınırlıdır. Bu sınırın üstünde rapor, gövdenin saklanmadığını belirtir.
323
+ - Sadece size ait ya da test etme yetkiniz olan sitelerde çalıştırın.
324
+ - HTTP sunucusunu başkaları için mi barındırıyorsunuz? Ek bir güvenlik katmanı olarak özel ağ ve bulut metadata adres aralıklarını ağ seviyesinde de engelleyin (bir egress firewall ya da izole bir ağ ile).
325
+
326
+ ## Yol haritası
327
+
328
+ - Consent Check: çerez onayı banner'larının ve Consent Mode v2'nin GDPR/KVKK'ya göre daha derin denetimi.
329
+ - Ölçümleme planı: bir adresten plan ve dataLayer şartnamesi taslağı hazırlama.
330
+ - [analitikisler.com](https://analitikisler.com/tr/) üzerinde zamanlanmış testler, deploy webhook'ları, barındırılan MCP sunucusu ve ajanslar için çoklu site panelleri sunan barındırılan sürüm.
331
+
332
+ ## Geliştirme
333
+
334
+ ```bash
335
+ npm test # parsers, checks, each AI provider and the MCP server, end to end against the demo shop
336
+ ```
337
+
338
+ Uçtan uca testler sahte model istemcileri kullanır, bu yüzden API anahtarı olmadan çalışır.
339
+
340
+ ## Barındırılan sürüm
341
+
342
+ Barındırılan testler, zamanlama ve ajans panelleri için [analitikisler.com](https://analitikisler.com/tr/) adresine bakın.
343
+
344
+ ## Lisans
345
+
346
+ MIT © Analitik İşler
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@analitikisler/ai-tag-debugger",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "Open-source AI agent that QA-tests your website's analytics, ad and consent tracking (GA4, GTM, Google Ads, Meta, TikTok). Bring your own model (Claude, OpenAI, Gemini) or use it from your chat app over MCP.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/browser.js CHANGED
@@ -211,10 +211,21 @@ export class AuditSession {
211
211
  const onRequest = (request) => {
212
212
  const hits = parseTrackingRequest(request.url(), request.postData());
213
213
  if (!hits) return;
214
+ const recorded = [];
214
215
  for (const hit of hits) {
215
216
  if (!this.providers.includes(hit.platform)) continue;
216
- this.record("hits", { ...this.redactHit(hit), step: this.currentStep, pageUrl: this.page.url(), t: Date.now() });
217
+ const entry = { ...this.redactHit(hit), step: this.currentStep, pageUrl: this.page.url(), t: Date.now() };
218
+ if (this.record("hits", entry)) recorded.push(entry);
217
219
  }
220
+ if (recorded.length) hitsOf.set(request, recorded);
221
+ };
222
+ // Hits of requests the guard below stops never left the browser, so they are taken back out.
223
+ const hitsOf = new WeakMap();
224
+ const unrecord = (request) => {
225
+ const dropped = new Set(hitsOf.get(request) ?? []);
226
+ if (dropped.size) this.hits = this.hits.filter((h) => !dropped.has(h));
227
+ const call = calls.get(request);
228
+ if (call) call.status = "blocked";
218
229
  };
219
230
  // Every call to an analytics, ad or consent vendor, for the timeline.
220
231
  const calls = new Map();
@@ -247,16 +258,20 @@ export class AuditSession {
247
258
  });
248
259
  this.context.on("requestfailed", (request) => {
249
260
  const call = calls.get(request);
250
- if (call) call.status = "failed";
261
+ // Keepalive requests answered below are still reported as aborted; the page got the 204.
262
+ if (call && call.status === null) call.status = "failed";
251
263
  });
252
264
 
265
+ // Hits come from the request event rather than the route below: a request sent while
266
+ // the page navigates away (a page_view on the next load, a beacon on unload) can be
267
+ // cancelled before its route handler runs, but its request event has already fired.
268
+ this.context.on("request", onRequest);
253
269
  if (this.opts.blockHits) {
254
270
  await this.context.route((url) => isCollectionEndpoint(url.href), async (route) => {
255
- onRequest(route.request());
256
271
  await route.fulfill({ status: 204, body: "" });
272
+ const call = calls.get(route.request());
273
+ if (call) call.status = 204;
257
274
  });
258
- } else {
259
- this.context.on("request", onRequest);
260
275
  }
261
276
 
262
277
  // Registered last, so it runs before the route above. Route handlers only see the
@@ -275,6 +290,7 @@ export class AuditSession {
275
290
  }
276
291
  if (!error) return route.fallback();
277
292
  if (isMainNavigation) this.blocked = { step: this.currentStep, url: request.url(), error };
293
+ unrecord(request);
278
294
  return route.abort("blockedbyclient");
279
295
  });
280
296
  this.beginStep("start");
package/src/mcp.js CHANGED
@@ -22,7 +22,7 @@ import { loadConfig, readJson, runAudit, writeReports } from "./audit.js";
22
22
  import { DEFAULT_PROVIDERS, PROVIDERS as TRACKING_PROVIDERS } from "./parsers.js";
23
23
  import { expectEvents, filterFindings, JOURNEY_PRESETS, onlyEvents, presetFor, withPresetEvents } from "./journeys.js";
24
24
 
25
- const VERSION = "0.6.0";
25
+ const VERSION = "0.6.1";
26
26
  const MAX_OPEN_AUDITS = 4;
27
27
  const IDLE_MS = 15 * 60 * 1000; // an audit untouched this long is closed
28
28
  const MAX_BODY_BYTES = 1024 * 1024;
package/src/parsers.js CHANGED
@@ -26,12 +26,18 @@ function ga4Consent(searchParams) {
26
26
  return { ad_storage: state(gcs[2]), analytics_storage: state(gcs[3]) };
27
27
  }
28
28
 
29
+ // Only www.google.<tld> and doubleclick.net: analytics.google.com is a primary GA4 endpoint.
30
+ const GOOGLE_COMPANION_HOST = /^(www\.)?google\.[a-z]{2,3}(\.[a-z]{2})?$|(^|\.)doubleclick\.net$/;
31
+
29
32
  /** @returns {Hit[] | null} */
30
33
  export function parseGa4(url, postData) {
31
34
  const u = new URL(url);
32
35
  // Matched on the GA4 protocol rather than the host, so server-side GTM
33
36
  // endpoints on first-party domains are captured too.
34
37
  if (!u.pathname.endsWith("/g/collect") || u.searchParams.get("v") !== "2") return null;
38
+ // gtag.js sends a copy of some events to www.google.com/g/collect (and doubleclick.net)
39
+ // for Google signals and ads features. GA4 doesn't count those, so they aren't hits.
40
+ if (GOOGLE_COMPANION_HOST.test(u.hostname)) return null;
35
41
  const base = new URLSearchParams(u.search);
36
42
  const lines = postData ? postData.split("\n").filter(Boolean) : [""];
37
43
  return lines.map((line) => {
@@ -130,6 +136,7 @@ export function parseTrackingRequest(url, postData) {
130
136
  const OTHER_BEACONS = [
131
137
  [/(^|\.)(doubleclick\.net|googleadservices\.com|google\.[a-z.]+)$/, /^\/pagead\/(viewthroughconversion|1p-user-list|landing)\//],
132
138
  [/(^|\.)google\.[a-z.]+$/, /^\/ccm\/collect/],
139
+ [GOOGLE_COMPANION_HOST, /\/g\/collect$/],
133
140
  [/^td\.doubleclick\.net$/, /^\/td\//],
134
141
  [/(^|\.)facebook\.com$/, /^\/privacy_sandbox\/pixel\//],
135
142
  [/^bat\.bing\.com$/, /^\/action\//],