@adsuploader/cli 0.1.3 → 0.1.6
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/SKILL.md +29 -1
- package/dist/cli.cjs +19 -8
- package/package.json +4 -2
package/SKILL.md
CHANGED
|
@@ -308,6 +308,34 @@ Top-level CTA applies to all ads (overridden by per-ad CTA):
|
|
|
308
308
|
}
|
|
309
309
|
```
|
|
310
310
|
|
|
311
|
+
### URL Split-Test (LP A/B)
|
|
312
|
+
|
|
313
|
+
Provide 2–5 destination URLs under `texts.urlVariants` and every generated ad set is duplicated once per URL so Meta optimizes each ad+LP combination independently (Barry Hott's method — don't split traffic inside one ad).
|
|
314
|
+
|
|
315
|
+
```json
|
|
316
|
+
{
|
|
317
|
+
"texts": {
|
|
318
|
+
"common": { "headlines": ["Hero"], "bodies": ["Copy"] },
|
|
319
|
+
"urlVariants": [
|
|
320
|
+
{ "link": "https://example.com/homepage", "label": "homepage" },
|
|
321
|
+
{ "link": "https://example.com/quiz", "label": "quiz-v2" }
|
|
322
|
+
]
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
- `label` is optional; when omitted, the last URL path slug is used (`/quiz-v2` → `quiz-v2`), falling back to hostname for root URLs.
|
|
328
|
+
- Naming depends on ad set mode:
|
|
329
|
+
- `single`: each variant's label becomes the full ad set name.
|
|
330
|
+
- `perUpload` / `autoGroup`: each duplicated ad set name is `{base}-{label}`, or substitutes a `{destination}` token if present in the pattern.
|
|
331
|
+
- Explicit `adSet.groups[]`: use `{group}` (aliases: `{name}`, `{base}`, `{adset}`) in `adSet.namePattern` to reference `groups[i].name`.
|
|
332
|
+
- Variation grouping: use `{variation}` in `adSet.namePattern` to reference the filename prefix before `variationIdentifier`.
|
|
333
|
+
- The `{date}` token resolves to today's date in labels (`launch-{date}` → `launch-2026-04-27`).
|
|
334
|
+
- Each destination URL must be distinct. Trailing-slash and case variants of the same URL collapse to one — supply at least 2 truly different destinations.
|
|
335
|
+
- Your ad set budget is multiplied by the number of variants.
|
|
336
|
+
- Not compatible with per-ad `link` overrides — the variant URL wins when both are set.
|
|
337
|
+
- Rejected with a 400 ConfigError if the source ad (preset or `copyFromAd`) targets a special destination (lead form, Messenger, WhatsApp, Instagram DM, Call) — those formats don't route via `cta.link`.
|
|
338
|
+
|
|
311
339
|
**Standard CTA types:** `LEARN_MORE`, `SHOP_NOW`, `SIGN_UP`, `SUBSCRIBE`, `GET_OFFER`, `CONTACT_US`, `DOWNLOAD`, `ORDER_NOW`, `BUY_NOW`, `BOOK_NOW`, `APPLY_NOW`, `GET_QUOTE`, `GET_IN_TOUCH`, `WATCH_MORE`
|
|
312
340
|
|
|
313
341
|
**Objective-specific CTAs** (inherited from template ad — do not set manually):
|
|
@@ -380,7 +408,7 @@ Minimum 2 assets per group. Files claimed by a flexible group are removed from t
|
|
|
380
408
|
{ "adNamePattern": "{filename} - {date}" }
|
|
381
409
|
```
|
|
382
410
|
|
|
383
|
-
Placeholders: `{filename}`, `{index:01}`, `{variation}`, `{campaign}`, `{date}`, `{date:short}`, `{timestamp}`
|
|
411
|
+
Placeholders: `{filename}`, `{index:01}`, `{variation}`, `{group}`, `{adset}`, `{campaign}`, `{date}`, `{date:short}`, `{timestamp}`
|
|
384
412
|
|
|
385
413
|
### Options
|
|
386
414
|
|
package/dist/cli.cjs
CHANGED
|
@@ -5386,18 +5386,29 @@ async function executeCreate(body, opts, { preview = false, test = false } = {})
|
|
|
5386
5386
|
const sharedCta = allCtas.length > 0 && allCtas.every((c) => c === allCtas[0]) ? allCtas[0] : null;
|
|
5387
5387
|
const maxText = 80;
|
|
5388
5388
|
const trunc = (s) => s.length > maxText ? s.slice(0, maxText - 1) + "\u2026" : s;
|
|
5389
|
+
let lastDetailAdSet = null;
|
|
5390
|
+
const distinctAdSets = new Set(result.ads.map((a) => a.adSetName).filter(Boolean));
|
|
5391
|
+
const showAdSetHeaders = distinctAdSets.size > 1;
|
|
5389
5392
|
for (const ad of result.ads) {
|
|
5393
|
+
if (showAdSetHeaders && ad.adSetName && ad.adSetName !== lastDetailAdSet) {
|
|
5394
|
+
if (lastDetailAdSet !== null) console.log("");
|
|
5395
|
+
console.log(` ${import_picocolors11.default.dim("\u25B8 Ad Set:")} ${import_picocolors11.default.bold(ad.adSetName)}`);
|
|
5396
|
+
lastDetailAdSet = ad.adSetName;
|
|
5397
|
+
}
|
|
5390
5398
|
const typeLabel = ad.mediaType ? import_picocolors11.default.dim(` [${ad.mediaType}]`) : "";
|
|
5391
|
-
|
|
5399
|
+
const indent = showAdSetHeaders ? " " : " ";
|
|
5400
|
+
const fieldIndent = showAdSetHeaders ? " " : " ";
|
|
5401
|
+
const fieldIndentLen = fieldIndent.length;
|
|
5402
|
+
console.log(`${indent}${import_picocolors11.default.bold(import_picocolors11.default.blue(ad.name))}${typeLabel}`);
|
|
5392
5403
|
const headline = (ad.headline || []).join(" | ");
|
|
5393
5404
|
const primary = (ad.primaryText || []).join(" | ");
|
|
5394
5405
|
const desc = (ad.description || []).join(" | ");
|
|
5395
|
-
if (headline) console.log(
|
|
5396
|
-
if (primary) console.log(
|
|
5397
|
-
if (desc) console.log(
|
|
5398
|
-
if (ad.cta && !sharedCta) console.log(
|
|
5399
|
-
if (ad.link && !sharedLink) console.log(
|
|
5400
|
-
if (ad.urlTags && !sharedUrlTags) console.log(
|
|
5406
|
+
if (headline) console.log(`${fieldIndent}${import_picocolors11.default.dim("Headline:")} ${trunc(headline)}`);
|
|
5407
|
+
if (primary) console.log(`${fieldIndent}${import_picocolors11.default.dim("Primary Text:")} ${trunc(primary)}`);
|
|
5408
|
+
if (desc) console.log(`${fieldIndent}${import_picocolors11.default.dim("Description:")} ${trunc(desc)}`);
|
|
5409
|
+
if (ad.cta && !sharedCta) console.log(`${fieldIndent}${import_picocolors11.default.dim("CTA:")} ${ad.cta}`);
|
|
5410
|
+
if (ad.link && !sharedLink) console.log(`${fieldIndent}${import_picocolors11.default.dim("Link:")} ${wrapLine(ad.link, fieldIndentLen)}`);
|
|
5411
|
+
if (ad.urlTags && !sharedUrlTags) console.log(`${fieldIndent}${import_picocolors11.default.dim("URL Tags:")} ${wrapLine(ad.urlTags, fieldIndentLen)}`);
|
|
5401
5412
|
console.log("");
|
|
5402
5413
|
}
|
|
5403
5414
|
const valCol = 18;
|
|
@@ -5929,7 +5940,7 @@ function statusLabel(status) {
|
|
|
5929
5940
|
}
|
|
5930
5941
|
|
|
5931
5942
|
// src/cli.js
|
|
5932
|
-
var VERSION = true ? "0.1.
|
|
5943
|
+
var VERSION = true ? "0.1.6" : "0.0.0";
|
|
5933
5944
|
var apiUrl = process.env.ADS_API_URL || getBaseUrl();
|
|
5934
5945
|
if (apiUrl && (apiUrl.includes("localhost") || apiUrl.includes("127.0.0.1"))) {
|
|
5935
5946
|
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adsuploader/cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.6",
|
|
4
4
|
"description": "Create Facebook ads from the command line — bulk upload media, preview configurations, and launch ads at scale",
|
|
5
5
|
"author": "Ads Uploader <support@adsuploader.com> (https://adsuploader.com)",
|
|
6
6
|
"license": "UNLICENSED",
|
|
@@ -32,7 +32,9 @@
|
|
|
32
32
|
"scripts": {
|
|
33
33
|
"build": "node build.cjs",
|
|
34
34
|
"dev": "node src/cli.js",
|
|
35
|
-
"link": "npm link"
|
|
35
|
+
"link": "npm link",
|
|
36
|
+
"prepublishOnly": "npm run build",
|
|
37
|
+
"version": "npm run build"
|
|
36
38
|
},
|
|
37
39
|
"dependencies": {
|
|
38
40
|
"commander": "^13.0.0",
|