adloop 0.26.0__tar.gz → 0.26.1__tar.gz
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.
- {adloop-0.26.0 → adloop-0.26.1}/PKG-INFO +19 -2
- {adloop-0.26.0 → adloop-0.26.1}/README.md +18 -1
- {adloop-0.26.0 → adloop-0.26.1}/pyproject.toml +1 -1
- {adloop-0.26.0 → adloop-0.26.1}/pyproject.toml.orig +1 -1
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/adloop.md +1 -1
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/server.py +9 -5
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/__init__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/__main__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/_mcp_patches.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/__init__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/ai_max.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/brands.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/client.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/conversion_actions.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/currency.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/custom_conversion_goals.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/enums.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/forecast.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/gaql.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/image_fetch.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/pmax.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/read.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/validate_only.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ads/write.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/auth.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/cli.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/config.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/crossref.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/diagnostics.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/__init__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/client.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/reports.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/tracking.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/ga4/write.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gsc/__init__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gsc/client.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gsc/reports.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/__init__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/client.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/entities.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/read.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/gtm/write.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/merchant/__init__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/merchant/client.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/merchant/read.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/pagespeed.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/__init__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/auth.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/client.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/read.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/schedule.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/reddit/write.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/__init__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/analyze-performance.md +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/budget-plan.md +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/create-ad.md +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/create-campaign.md +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/diagnose-tracking.md +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules/commands/optimize-campaign.md +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/rules_install.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/runtime.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/__init__.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/approval.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/approval_card.html +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/audit.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/guards.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/safety/preview.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/tracking.py +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/upload_card.html +0 -0
- {adloop-0.26.0 → adloop-0.26.1}/src/adloop/uploads.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.3
|
|
2
2
|
Name: adloop
|
|
3
|
-
Version: 0.26.
|
|
3
|
+
Version: 0.26.1
|
|
4
4
|
Summary: The AI command center for Google Ads, Reddit Ads, GA4, and tracking code.
|
|
5
5
|
Keywords: mcp,google-ads,reddit-ads,google-analytics,ga4,cursor,marketing
|
|
6
6
|
Author: Daniel Klose
|
|
@@ -262,6 +262,19 @@ A second ad platform, same safety model. Reddit is a separate connection: its ow
|
|
|
262
262
|
|
|
263
263
|
> **Setup for Reddit Ads tools** — In Reddit Ads Manager open **Business Manager → Developer Application → Create app** (business admins only; no approval wait). Register the redirect URL exactly as `http://localhost:8765/callback`, then run `adloop init` and complete the Reddit Ads step: it opens Reddit's consent page (scopes `adsread` + `adsedit`, permanent grant), stores the refresh token at `~/.adloop/reddit_token.json`, and lets you pick the default ad account. Reddit rate-limits per user (reporting: 60 requests/min) and requires a descriptive User-Agent, which AdLoop builds from your app id and Reddit username. Reddit has no validate-only mode, so `confirm_and_apply(dry_run=true)` re-reads the target and re-checks the safety caps instead.
|
|
264
264
|
|
|
265
|
+
### Image Uploads
|
|
266
|
+
|
|
267
|
+
An image on your computer, or one you attached in the chat, has no public URL, and an AI app cannot hand the file to a tool. `upload_images` gives it a way in.
|
|
268
|
+
|
|
269
|
+
<p align="center"><img src="https://raw.githubusercontent.com/kLOsk/adloop/main/assets/upload-card.gif" alt="An AdLoop upload card in an AI chat: the user drags a banner onto it, the card shows the upload id, posts it into the chat, and the AI drafts the image asset" width="760"></p>
|
|
270
|
+
|
|
271
|
+
| Tool | What It Does |
|
|
272
|
+
|------|-------------|
|
|
273
|
+
| `upload_images` | Shows an upload card in apps with MCP Apps (drop PNG, JPEG or GIF up to 5 MB); on AdLoop Cloud in other apps it returns a one-time upload link, which a coding agent can also use with `curl -T` |
|
|
274
|
+
| `list_uploads` | The uploads that have not expired yet (kept 24 hours), with id, dimensions and size |
|
|
275
|
+
|
|
276
|
+
The upload ids go into `draft_image_assets(upload_ids=[...])`, or as `upload:<id>` into the image fields of `draft_reddit_ad` on AdLoop Cloud.
|
|
277
|
+
|
|
265
278
|
### Planning Tools
|
|
266
279
|
|
|
267
280
|
| Tool | What It Does |
|
|
@@ -282,7 +295,7 @@ All write operations follow a **draft → preview → confirm** workflow. Nothin
|
|
|
282
295
|
| `draft_responsive_search_ad` | Create RSA preview (3-15 headlines ≤30 chars, 2-4 descriptions ≤90 chars). Warns if headline/description count is below best practice. |
|
|
283
296
|
| `draft_callouts` | Create campaign callout assets from 1-25 character text snippets. |
|
|
284
297
|
| `draft_structured_snippets` | Create campaign structured snippet assets using official header values and 3-10 snippet values. |
|
|
285
|
-
| `draft_image_assets` | Create campaign image assets from local files
|
|
298
|
+
| `draft_image_assets` | Create campaign image assets from local files, public image URLs or uploads (PNG, JPEG, or GIF). |
|
|
286
299
|
| `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy — flags BROAD match on campaigns without Smart Bidding as dangerous in the preview. |
|
|
287
300
|
| `add_negative_keywords` | Propose negative keywords directly on a campaign |
|
|
288
301
|
| `add_negative_locations` | Propose negative geo exclusions on a campaign — exclude cities/regions while keeping broader positive targets |
|
|
@@ -337,6 +350,10 @@ AdLoop includes pre-built slash commands in `.claude/commands/` for common workf
|
|
|
337
350
|
|
|
338
351
|
AdLoop manages real ad spend, so safety is not optional.
|
|
339
352
|
|
|
353
|
+
<p align="center"><img src="https://raw.githubusercontent.com/kLOsk/adloop/main/assets/approval-card.gif" alt="An AdLoop approval card in an AI chat: the user clicks Test run, Google validates the change, then Apply makes it live and the card reports back to the chat" width="760"></p>
|
|
354
|
+
|
|
355
|
+
- **Approval cards.** In apps that show MCP Apps (Claude, ChatGPT, VS Code and others), every change appears as a card with Test run and Apply. Only the click applies it: the AI's own `confirm_and_apply(dry_run=false)` is refused with `APPROVE_ON_CARD`, whatever it was told in the chat.
|
|
356
|
+
- **Confirmation dialogs.** Apps without cards that support elicitation (Claude Code, for example) get a dialog from AdLoop itself before every real change. The AI cannot answer it; declining changes nothing.
|
|
340
357
|
- **Two-step writes.** Every mutation returns a preview first. A separate `confirm_and_apply` call is required to execute.
|
|
341
358
|
- **Dry-run by default.** Even `confirm_and_apply` defaults to `dry_run=true`. Real changes require explicit `dry_run=false`.
|
|
342
359
|
- **Two-phase apply (optional).** With `safety.two_phase_apply: true`, `confirm_and_apply` refuses `dry_run=false` until the plan has completed one dry-run pass — preview-then-apply becomes server-enforced instead of a convention.
|
|
@@ -234,6 +234,19 @@ A second ad platform, same safety model. Reddit is a separate connection: its ow
|
|
|
234
234
|
|
|
235
235
|
> **Setup for Reddit Ads tools** — In Reddit Ads Manager open **Business Manager → Developer Application → Create app** (business admins only; no approval wait). Register the redirect URL exactly as `http://localhost:8765/callback`, then run `adloop init` and complete the Reddit Ads step: it opens Reddit's consent page (scopes `adsread` + `adsedit`, permanent grant), stores the refresh token at `~/.adloop/reddit_token.json`, and lets you pick the default ad account. Reddit rate-limits per user (reporting: 60 requests/min) and requires a descriptive User-Agent, which AdLoop builds from your app id and Reddit username. Reddit has no validate-only mode, so `confirm_and_apply(dry_run=true)` re-reads the target and re-checks the safety caps instead.
|
|
236
236
|
|
|
237
|
+
### Image Uploads
|
|
238
|
+
|
|
239
|
+
An image on your computer, or one you attached in the chat, has no public URL, and an AI app cannot hand the file to a tool. `upload_images` gives it a way in.
|
|
240
|
+
|
|
241
|
+
<p align="center"><img src="https://raw.githubusercontent.com/kLOsk/adloop/main/assets/upload-card.gif" alt="An AdLoop upload card in an AI chat: the user drags a banner onto it, the card shows the upload id, posts it into the chat, and the AI drafts the image asset" width="760"></p>
|
|
242
|
+
|
|
243
|
+
| Tool | What It Does |
|
|
244
|
+
|------|-------------|
|
|
245
|
+
| `upload_images` | Shows an upload card in apps with MCP Apps (drop PNG, JPEG or GIF up to 5 MB); on AdLoop Cloud in other apps it returns a one-time upload link, which a coding agent can also use with `curl -T` |
|
|
246
|
+
| `list_uploads` | The uploads that have not expired yet (kept 24 hours), with id, dimensions and size |
|
|
247
|
+
|
|
248
|
+
The upload ids go into `draft_image_assets(upload_ids=[...])`, or as `upload:<id>` into the image fields of `draft_reddit_ad` on AdLoop Cloud.
|
|
249
|
+
|
|
237
250
|
### Planning Tools
|
|
238
251
|
|
|
239
252
|
| Tool | What It Does |
|
|
@@ -254,7 +267,7 @@ All write operations follow a **draft → preview → confirm** workflow. Nothin
|
|
|
254
267
|
| `draft_responsive_search_ad` | Create RSA preview (3-15 headlines ≤30 chars, 2-4 descriptions ≤90 chars). Warns if headline/description count is below best practice. |
|
|
255
268
|
| `draft_callouts` | Create campaign callout assets from 1-25 character text snippets. |
|
|
256
269
|
| `draft_structured_snippets` | Create campaign structured snippet assets using official header values and 3-10 snippet values. |
|
|
257
|
-
| `draft_image_assets` | Create campaign image assets from local files
|
|
270
|
+
| `draft_image_assets` | Create campaign image assets from local files, public image URLs or uploads (PNG, JPEG, or GIF). |
|
|
258
271
|
| `draft_keywords` | Propose keyword additions with match types. Proactively checks bidding strategy — flags BROAD match on campaigns without Smart Bidding as dangerous in the preview. |
|
|
259
272
|
| `add_negative_keywords` | Propose negative keywords directly on a campaign |
|
|
260
273
|
| `add_negative_locations` | Propose negative geo exclusions on a campaign — exclude cities/regions while keeping broader positive targets |
|
|
@@ -309,6 +322,10 @@ AdLoop includes pre-built slash commands in `.claude/commands/` for common workf
|
|
|
309
322
|
|
|
310
323
|
AdLoop manages real ad spend, so safety is not optional.
|
|
311
324
|
|
|
325
|
+
<p align="center"><img src="https://raw.githubusercontent.com/kLOsk/adloop/main/assets/approval-card.gif" alt="An AdLoop approval card in an AI chat: the user clicks Test run, Google validates the change, then Apply makes it live and the card reports back to the chat" width="760"></p>
|
|
326
|
+
|
|
327
|
+
- **Approval cards.** In apps that show MCP Apps (Claude, ChatGPT, VS Code and others), every change appears as a card with Test run and Apply. Only the click applies it: the AI's own `confirm_and_apply(dry_run=false)` is refused with `APPROVE_ON_CARD`, whatever it was told in the chat.
|
|
328
|
+
- **Confirmation dialogs.** Apps without cards that support elicitation (Claude Code, for example) get a dialog from AdLoop itself before every real change. The AI cannot answer it; declining changes nothing.
|
|
312
329
|
- **Two-step writes.** Every mutation returns a preview first. A separate `confirm_and_apply` call is required to execute.
|
|
313
330
|
- **Dry-run by default.** Even `confirm_and_apply` defaults to `dry_run=true`. Real changes require explicit `dry_run=false`.
|
|
314
331
|
- **Two-phase apply (optional).** With `safety.two_phase_apply: true`, `confirm_and_apply` refuses `dry_run=false` until the plan has completed one dry-run pass — preview-then-apply becomes server-enforced instead of a convention.
|
|
@@ -227,7 +227,7 @@ Reddit is a second ad platform with its own connection (own OAuth app, no develo
|
|
|
227
227
|
|
|
228
228
|
### Image Uploads
|
|
229
229
|
|
|
230
|
-
For images that have no public URL: a file on the user's computer, or an image they attached in the chat
|
|
230
|
+
For images that have no public URL: a file on the user's computer, or an image they attached in the chat. **Attaching an image to the chat does not get it into an ad:** you can see it, but no tool can receive it. Never suggest attaching as the way in; offer `upload_images` (card or link) or a public image URL, and when the user already attached one, ask them to drop the same file on the upload card.
|
|
231
231
|
|
|
232
232
|
| Tool | When to Use | Key Parameters |
|
|
233
233
|
|------|-------------|----------------|
|
|
@@ -146,9 +146,12 @@ def _build_orchestration_instructions() -> str:
|
|
|
146
146
|
"come back as `row_errors` with DRY_RUN_SUCCESS, and the apply would "
|
|
147
147
|
"carry out the other rows.\n"
|
|
148
148
|
"- One change at a time — don't batch unrelated writes.\n"
|
|
149
|
-
"- Images without a public URL (a file on the user's computer,
|
|
149
|
+
"- Images without a public URL (a file on the user's computer, or one "
|
|
150
150
|
"attached in the chat): call `upload_images`, which shows an upload card "
|
|
151
|
-
"or returns an upload link, then use the upload ids it yields
|
|
151
|
+
"or returns an upload link, then use the upload ids it yields. An image "
|
|
152
|
+
"attached to the chat cannot be passed to any tool, so don't suggest "
|
|
153
|
+
"attaching as the way to get it into an ad; offer the upload or a public "
|
|
154
|
+
"image URL.\n\n"
|
|
152
155
|
"PRE-WRITE CHECKS (before any `draft_*`):\n"
|
|
153
156
|
"- BROAD match keywords require Smart Bidding (MAXIMIZE_CONVERSIONS, "
|
|
154
157
|
"tCPA, tROAS). Refuse BROAD on MANUAL_CPC. This is the #1 cause of "
|
|
@@ -4039,9 +4042,10 @@ def upload_images() -> dict:
|
|
|
4039
4042
|
"""Let the user upload images (PNG, JPEG, GIF up to 5 MB) for ad assets.
|
|
4040
4043
|
|
|
4041
4044
|
For images without a public URL, such as a file on the user's computer
|
|
4042
|
-
or an image attached in the chat.
|
|
4043
|
-
|
|
4044
|
-
|
|
4045
|
+
or an image attached in the chat. An attached image cannot reach a tool,
|
|
4046
|
+
so the user drops the same file on the card. Apps that show AdLoop's
|
|
4047
|
+
cards display an upload card where the user drops the files; the card
|
|
4048
|
+
then posts the upload ids into the chat. On AdLoop Cloud in other apps the result
|
|
4045
4049
|
carries an upload link for the user (or a coding agent, with curl).
|
|
4046
4050
|
Uploads expire after a day. The ids go into upload_ids of
|
|
4047
4051
|
draft_image_assets, or as "upload:<id>" into the image fields of
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|