@adsuploader/cli 0.1.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.
Files changed (4) hide show
  1. package/README.md +50 -0
  2. package/SKILL.md +517 -0
  3. package/dist/cli.cjs +5971 -0
  4. package/package.json +46 -0
package/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Ads Uploader CLI
2
+
3
+ Create and manage Meta ads from the command line. Same pipeline as the web app.
4
+
5
+ ## Install
6
+
7
+ ```bash
8
+ npm install -g @adsuploader/cli
9
+ ```
10
+
11
+ Requires Node.js 18 or later.
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ ads login # authenticate via browser
17
+ ads accounts # list ad accounts
18
+ ads account act_123456 # set default account
19
+
20
+ ads upload hero.jpg banner.mp4 # upload media
21
+ ads create:preview spec.json # preview what would be created
22
+ ads create spec.json # create ads
23
+ ```
24
+
25
+ ## Documentation
26
+
27
+ Full documentation including the spec file reference, campaign structure, text configuration, and all available options is at [adsuploader.com/docs/ad-configuration/cli](https://adsuploader.com/docs/ad-configuration/cli).
28
+
29
+ ## Using with AI
30
+
31
+ This package includes a `SKILL.md` file that describes every command and option for AI agents (Claude Code, Cursor, etc.). Point your AI tool at `node_modules/@adsuploader/cli/SKILL.md` to get started.
32
+
33
+ ## Commands
34
+
35
+ | Command | Description |
36
+ |---------|-------------|
37
+ | `ads login` | Authenticate via browser |
38
+ | `ads accounts` | List ad accounts |
39
+ | `ads account <id>` | Set default account |
40
+ | `ads campaigns` | List campaigns |
41
+ | `ads campaign <id>` | Show ad sets in a campaign |
42
+ | `ads adset <id>` | Show ads in an ad set |
43
+ | `ads ad <id>` | Ad details and creative |
44
+ | `ads presets` | List saved presets |
45
+ | `ads upload <files...>` | Upload images and videos |
46
+ | `ads uploads` | List recent upload batches |
47
+ | `ads create spec.json` | Create ads from spec |
48
+ | `ads create:preview spec.json` | Dry run |
49
+ | `ads create:interactive` | Guided wizard |
50
+ | `ads jobs <id>` | Check job status |
package/SKILL.md ADDED
@@ -0,0 +1,517 @@
1
+ ---
2
+ name: ads
3
+ description: Create and manage Facebook/Meta ads using the Ads Uploader CLI. Upload media, preview configurations, create ads, browse campaigns and ad sets.
4
+ homepage: https://adsuploader.com
5
+ ---
6
+
7
+ ## Setup
8
+
9
+ ```bash
10
+ npm install -g @adsuploader/cli
11
+ ```
12
+
13
+ ## Authentication
14
+
15
+ ```bash
16
+ # 1. Login (opens browser OAuth flow)
17
+ ads login
18
+
19
+ # 2. List ad accounts
20
+ ads accounts
21
+
22
+ # 3. Set default account
23
+ ads account act_123456789
24
+ ```
25
+
26
+ Credentials are stored at `~/.config/adsuploader/credentials.json`. Check current auth:
27
+
28
+ ```bash
29
+ ads whoami
30
+ ads config
31
+ ```
32
+
33
+ ---
34
+
35
+ ## Core Workflow
36
+
37
+ Every ad creation follows three steps:
38
+
39
+ ### 1. Upload media
40
+
41
+ ```bash
42
+ ads upload hero.jpg banner.mp4
43
+ ads upload ./my-creatives/ # entire directory
44
+ ```
45
+
46
+ Returns a **batch ID** (e.g. `batch_abc123`) — you'll need this for the spec file. Files are uploaded directly to the selected ad account's Facebook media library, so the batch ID is tied to that account.
47
+
48
+ ### 2. Preview (dry run)
49
+
50
+ ```bash
51
+ ads create:preview spec.json
52
+ ```
53
+
54
+ Shows exactly what would be created without touching Facebook. **Always preview first.**
55
+
56
+ ### 3. Create ads
57
+
58
+ ```bash
59
+ ads create spec.json
60
+ ```
61
+
62
+ Ads are created **ACTIVE** by default. Use `--status PAUSED` to create them paused. Returns a job ID for progress tracking.
63
+
64
+ ---
65
+
66
+ ## Command Reference
67
+
68
+ ### Auth
69
+ | Command | Description |
70
+ |---------|-------------|
71
+ | `login` | Authenticate via browser |
72
+ | `logout` | Clear credentials |
73
+ | `whoami` | Show current user |
74
+ | `config` | Show configuration |
75
+
76
+ ### Browsing
77
+ | Command | Description |
78
+ |---------|-------------|
79
+ | `accounts` | List ad accounts |
80
+ | `account <id>` | Set default account |
81
+ | `campaigns` | List active campaigns (`--status all` for all, `--search <text>` to filter) |
82
+ | `campaign <id>` | Show ad sets in a campaign |
83
+ | `adsets --campaign <id>` | List ad sets (supports `--search`, `--status`) |
84
+ | `adset <adSetId>` | Show ads in an ad set |
85
+ | `ad <adId>` | Ad details + creative config |
86
+ | `presets` | List saved API presets (or `presets <id>` for details) |
87
+ | `text-presets` | List saved text presets (or `text-presets <id>` for details) |
88
+ | `uploads` | List recent upload batches (or `uploads <batchId>` for details) |
89
+
90
+ ### Actions
91
+ | Command | Description |
92
+ |---------|-------------|
93
+ | `upload <files...>` | Upload images/videos |
94
+ | `create spec.json` | Create ads from spec |
95
+ | `create:preview spec.json` | Dry run — show what would be created |
96
+ | `create:interactive` | Guided wizard (accepts all create flags) |
97
+
98
+ ### Jobs
99
+ | Command | Description |
100
+ |---------|-------------|
101
+ | `jobs <jobId>` | Check job status |
102
+ | `jobs <jobId> --follow` | Follow progress in real-time |
103
+ | `jobs cancel <jobId>` | Cancel a running job |
104
+
105
+ ### Create Flags
106
+ | Flag | Description |
107
+ |------|-------------|
108
+ | `--account <id>` | Override default ad account |
109
+ | `--preset <id>` | API preset ID (alternative to spec file) |
110
+ | `--text-preset <id>` | Text preset ID |
111
+ | `--copy-from <adId>` | Ad ID to copy settings from |
112
+ | `--upload <batchId>` | Upload batch ID |
113
+ | `--status <PAUSED\|ACTIVE>` | Set ad status (default: ACTIVE) |
114
+ | `--pause-at <level>` | Pause level: `ad` (default), `adSet`, or `campaign` |
115
+ | `--daily-budget <amount>` | Override daily budget (currency units) |
116
+ | `--bid-amount <amount>` | Override bid/cost cap (currency units) |
117
+ | `--text-file <path>` | Load text config from a JSON file |
118
+
119
+ ### Common Flags
120
+ | Flag | Description |
121
+ |------|-------------|
122
+ | `--account <id>` | Override default ad account |
123
+ | `--json` | Raw JSON output (available on most commands) |
124
+
125
+ ### Do not use `--json`
126
+
127
+ The `--json` flag is for shell scripts piping to `jq`. **Never use it** — the human-readable output already contains all the information you need (batch IDs, job progress, results). For `create`, `--json` replaces the live progress log with raw NDJSON poll dumps which are unreadable.
128
+
129
+ ---
130
+
131
+ ## Spec File Format
132
+
133
+ The JSON spec controls ad creation. Only `uploadId` and a template source are required.
134
+
135
+ ### Minimal Spec
136
+
137
+ ```json
138
+ {
139
+ "adPresetId": "preset_id_here",
140
+ "uploadId": "batch_abc123"
141
+ }
142
+ ```
143
+
144
+ ### Template Source (pick one)
145
+
146
+ | Field | Description |
147
+ |-------|-------------|
148
+ | `adPresetId` | Saved API preset ID (locks campaign, ad set, ad config) |
149
+ | `copyFromAd` | Facebook ad ID to copy settings from |
150
+
151
+ When using `copyFromAd`, provide the upload batch and optionally campaign/ad set:
152
+ ```json
153
+ {
154
+ "copyFromAd": "120233848667930472",
155
+ "uploadId": "batch_abc123",
156
+ "campaign": { "id": "120233848666410472" },
157
+ "adSet": { "id": "120233848666620472" }
158
+ }
159
+ ```
160
+
161
+ ### Campaign Structure
162
+
163
+ **Single campaign** (default): ads go into the template ad's campaign, or a new campaign if `campaign.name` is provided.
164
+
165
+ **Multi-campaign modes** — use `campaign.mode` with a `campaigns` array:
166
+
167
+ ```json
168
+ {
169
+ "campaign": {
170
+ "mode": "duplicate",
171
+ "campaigns": [
172
+ { "name": "Campaign A" },
173
+ { "name": "Campaign B" }
174
+ ]
175
+ }
176
+ }
177
+ ```
178
+
179
+ | Mode | Behavior |
180
+ |------|----------|
181
+ | `"single"` | Default — one campaign |
182
+ | `"duplicate"` | All media duplicated into each campaign |
183
+ | `"split"` | Media split across campaigns |
184
+
185
+ ### Ad Set Modes
186
+
187
+ **Single ad set** (default):
188
+ ```json
189
+ { "adSet": { "name": "My Ad Set" } }
190
+ ```
191
+
192
+ **Existing ad set:**
193
+ ```json
194
+ { "adSet": { "id": "120233848666620472" } }
195
+ ```
196
+
197
+ **Per upload** (one ad set per file):
198
+ ```json
199
+ { "adSet": { "mode": "perUpload" } }
200
+ ```
201
+
202
+ **Auto group** (split evenly):
203
+ ```json
204
+ { "adSet": { "mode": "autoGroup", "adsPerAdSet": 5 } }
205
+ ```
206
+
207
+ **Custom groups:**
208
+ ```json
209
+ {
210
+ "adSet": {
211
+ "groups": [
212
+ { "name": "Images", "media": ["hero.jpg", "banner.jpg"] },
213
+ { "name": "Video", "media": ["promo.mp4"] }
214
+ ]
215
+ }
216
+ }
217
+ ```
218
+
219
+ **Ad set naming pattern** (for multi-ad-set modes):
220
+ ```json
221
+ { "adSet": { "mode": "perUpload", "namePattern": "Ad Set {index:01}" } }
222
+ ```
223
+
224
+ **Variant grouping** (group ads by variation identifier into the same ad set):
225
+ ```json
226
+ { "adSet": { "mode": "autoGroup", "groupVariations": true, "variationIdentifier": "-" } }
227
+ ```
228
+
229
+ ### Budget / Bid Override
230
+
231
+ Values in account currency units (e.g. `50` = $50). Only one at a time.
232
+
233
+ ```json
234
+ { "adSet": { "dailyBudget": 50 } }
235
+ { "adSet": { "bidAmount": 5 } }
236
+ ```
237
+
238
+ Also available as CLI flags: `--daily-budget 50` or `--bid-amount 5`.
239
+
240
+ ### Text Configuration
241
+
242
+ **Common text** (same for all ads):
243
+ ```json
244
+ {
245
+ "texts": {
246
+ "common": {
247
+ "headlines": ["Headline 1", "Headline 2"],
248
+ "bodies": ["Primary text"],
249
+ "descriptions": ["Description"]
250
+ },
251
+ "strategy": "flexible"
252
+ }
253
+ }
254
+ ```
255
+
256
+ **Per-ad text** (unique per file):
257
+ ```json
258
+ {
259
+ "texts": {
260
+ "perAd": {
261
+ "hero.jpg": {
262
+ "headlines": ["Hero Headline"],
263
+ "bodies": ["Hero copy"],
264
+ "descriptions": ["Hero desc"],
265
+ "cta": "LEARN_MORE",
266
+ "link": "https://example.com/hero",
267
+ "urlTags": "utm_content=hero"
268
+ },
269
+ "banner.jpg": {
270
+ "headlines": ["Banner Headline"],
271
+ "bodies": ["Banner copy"]
272
+ }
273
+ }
274
+ }
275
+ }
276
+ ```
277
+
278
+ Per-ad keys are **filenames** (not full paths). Unspecified fields inherit from the template ad.
279
+
280
+ **Text preset:**
281
+ ```json
282
+ { "textPresetId": "preset_id_here" }
283
+ ```
284
+
285
+ Cannot combine `textPresetId` with `texts`.
286
+
287
+ **Strategy options:**
288
+ - `"flexible"` (default) — Meta optimizes across text variations (multiple headlines/bodies become options Facebook mixes)
289
+ - `"separate"` — each text combination becomes a separate ad
290
+
291
+ Per-ad entries support: `headlines`, `bodies`, `descriptions`, `cta`, `link`, `displayUrl`, `urlTags`. Unspecified fields inherit from the template ad.
292
+
293
+ ### CTA and Links
294
+
295
+ Top-level CTA applies to all ads (overridden by per-ad CTA):
296
+ ```json
297
+ {
298
+ "cta": {
299
+ "type": "SHOP_NOW",
300
+ "link": "https://example.com",
301
+ "displayUrl": "example.com"
302
+ },
303
+ "urlTags": "utm_source=facebook&utm_medium=paid"
304
+ }
305
+ ```
306
+
307
+ **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`
308
+
309
+ **Objective-specific CTAs** (inherited from template ad — do not set manually):
310
+
311
+ | CTA | Required Objective |
312
+ |-----|-------------------|
313
+ | `MESSAGE_PAGE` | Messenger destination |
314
+ | `WHATSAPP_MESSAGE` | WhatsApp destination |
315
+ | `INSTAGRAM_MESSAGE` | Instagram DM destination |
316
+ | `CALL_NOW` | Call campaign |
317
+
318
+ ### Creative Enhancements
319
+
320
+ ```json
321
+ { "creativeEnhancements": "none" }
322
+ ```
323
+
324
+ | Value | Effect |
325
+ |-------|--------|
326
+ | `"metaDefaults"` | Let Meta decide (default if omitted) |
327
+ | `"all"` | All features on |
328
+ | `"none"` | All features off |
329
+ | `["text_translation", "image_touchups"]` | Listed features on, rest off |
330
+
331
+ Available features: `text_translation`, `inline_comment`, `enhance_cta`, `text_optimizations`, `reveal_details_over_time`, `image_brightness_and_contrast`, `image_touchups`, `video_auto_crop`, `video_filtering`, `image_animation`, `image_templates`, `adapt_to_placement`, `product_extensions`, `description_automation`, `add_text_overlay`, `music`, `carousel_to_video`, `carousel_dynamic_description`, `multi_share_end_card`, `multi_share_optimized`
332
+
333
+ When cherry-picking features, only list ones relevant to the media type. `video_auto_crop` and `video_filtering` only apply to video ads. `carousel_to_video`, `carousel_dynamic_description`, `multi_share_end_card`, and `multi_share_optimized` only apply to carousel ads.
334
+
335
+ ### Carousel Ads
336
+
337
+ Group uploaded files into carousel ads with per-card text:
338
+
339
+ ```json
340
+ {
341
+ "carousel": [
342
+ {
343
+ "name": "My Carousel",
344
+ "cards": ["slide1.jpg", "slide2.jpg", "slide3.jpg"],
345
+ "cardTexts": [
346
+ { "headline": "Slide 1", "description": "First card", "link": "https://example.com/1" },
347
+ { "headline": "Slide 2", "description": "Second card", "link": "https://example.com/2" }
348
+ ]
349
+ }
350
+ ]
351
+ }
352
+ ```
353
+
354
+ Cards must reference filenames from the upload batch. Minimum 2 cards per carousel. Files claimed by a carousel are removed from the standard ad list.
355
+
356
+ ### Flexible Ads
357
+
358
+ Group multiple assets into a single flexible ad (Meta picks the best asset per placement):
359
+
360
+ ```json
361
+ {
362
+ "flexible": [
363
+ {
364
+ "name": "Multi-Asset Ad",
365
+ "assets": ["hero.jpg", "promo.mp4", "banner.jpg"]
366
+ }
367
+ ]
368
+ }
369
+ ```
370
+
371
+ Minimum 2 assets per group. Files claimed by a flexible group are removed from the standard ad list.
372
+
373
+ ### Ad Naming
374
+
375
+ ```json
376
+ { "adNamePattern": "{filename} - {date}" }
377
+ ```
378
+
379
+ Placeholders: `{filename}`, `{index:01}`, `{variation}`, `{campaign}`, `{date}`, `{date:short}`, `{timestamp}`
380
+
381
+ ### Options
382
+
383
+ ```json
384
+ {
385
+ "options": {
386
+ "status": "PAUSED",
387
+ "pauseAt": "ad",
388
+ "schedule": {
389
+ "startTime": "2026-04-01T09:00:00",
390
+ "endTime": "2026-04-30T23:59:59"
391
+ }
392
+ }
393
+ }
394
+ ```
395
+
396
+ | Field | Values | Description |
397
+ |-------|--------|-------------|
398
+ | `status` | `"PAUSED"`, `"ACTIVE"` | Ad launch status (default: ACTIVE) |
399
+ | `pauseAt` | `"ad"`, `"adSet"`, `"campaign"` | Which level to pause (default: ad) |
400
+ | `schedule.startTime` | ISO 8601 string | Scheduled start (uses ad account timezone) |
401
+ | `schedule.endTime` | ISO 8601 string | Scheduled end (optional) |
402
+
403
+ ---
404
+
405
+ ## Upload & Variant Detection
406
+
407
+ Variant groups are detected automatically from filename conventions:
408
+
409
+ **Ratio suffixes:**
410
+ `hero_4x5.jpg` + `hero_9x16.jpg` + `hero_16x9.jpg` → grouped as one variant ad
411
+
412
+ **Word suffixes:**
413
+ `hero.jpg` + `hero_vertical.jpg` + `hero_horizontal.jpg` → grouped as one variant ad
414
+
415
+ Both `_` and `-` delimiters work. A file without a ratio suffix (e.g. `hero.jpg`) only groups with `_vertical`/`_horizontal` variants.
416
+
417
+ ---
418
+
419
+ ## Common Patterns
420
+
421
+ ### Simple upload + create with preset
422
+
423
+ ```bash
424
+ # Upload
425
+ ads upload ./creatives/hero.jpg ./creatives/banner.jpg
426
+ # Note the batchId from output (printed as "Batch: batch_xxx")
427
+
428
+ # Write spec
429
+ cat > /tmp/spec.json << 'EOF'
430
+ {
431
+ "adPresetId": "PRESET_ID",
432
+ "uploadId": "BATCH_ID"
433
+ }
434
+ EOF
435
+
436
+ # Preview, then create
437
+ ads create:preview /tmp/spec.json
438
+ ads create /tmp/spec.json
439
+ ```
440
+
441
+ ### Per-ad text (unique copy per file)
442
+
443
+ ```json
444
+ {
445
+ "adPresetId": "PRESET_ID",
446
+ "uploadId": "BATCH_ID",
447
+ "texts": {
448
+ "perAd": {
449
+ "hero.jpg": {
450
+ "headlines": ["Summer Sale Now On"],
451
+ "bodies": ["Save up to 50% on all items"],
452
+ "cta": "SHOP_NOW",
453
+ "link": "https://example.com/summer"
454
+ },
455
+ "banner.jpg": {
456
+ "headlines": ["New Collection Available"],
457
+ "bodies": ["Browse our latest styles"],
458
+ "cta": "LEARN_MORE",
459
+ "link": "https://example.com/new"
460
+ }
461
+ }
462
+ },
463
+ "options": { "status": "PAUSED" }
464
+ }
465
+ ```
466
+
467
+ ### Auto-group into multiple ad sets
468
+
469
+ ```json
470
+ {
471
+ "adPresetId": "PRESET_ID",
472
+ "uploadId": "BATCH_ID",
473
+ "adSet": { "mode": "autoGroup", "adsPerAdSet": 3 },
474
+ "options": { "status": "PAUSED" }
475
+ }
476
+ ```
477
+
478
+ ### Copy from existing ad
479
+
480
+ ```bash
481
+ # Find the ad to copy from
482
+ ads campaigns
483
+ ads campaign 120233848666410472 # shows ad sets
484
+ ads adset 120233848666620472 # shows ads
485
+ ads ad 120233848667930472 # shows ad details
486
+ ```
487
+
488
+ ```json
489
+ {
490
+ "copyFromAd": "120233848667930472",
491
+ "campaign": { "id": "120233848666410472" },
492
+ "uploadId": "BATCH_ID",
493
+ "options": { "status": "PAUSED" }
494
+ }
495
+ ```
496
+
497
+ ### Follow job progress
498
+
499
+ ```bash
500
+ ads create spec.json
501
+ # Shows live progress automatically; to check a job later:
502
+ ads jobs JOB_ID --follow
503
+ ```
504
+
505
+ ---
506
+
507
+ ## Critical Gotchas
508
+
509
+ 1. **Always preview first** — `create:preview` catches config errors before touching Facebook
510
+ 2. **Ads are ACTIVE by default** — use `--status PAUSED` or `"status": "PAUSED"` in spec to create them paused
511
+ 3. **`uploadId` comes from upload output** — it's the batch ID returned by `ads upload`
512
+ 4. **Uploads are tied to an ad account** — files are uploaded directly to the selected account's Facebook media library. The batch ID can only be used with the same account
513
+ 5. **`copyFromAd` needs `uploadId`** — you must provide the upload batch. Optionally provide `campaign.id` and `adSet.id` to control placement
514
+ 6. **Per-ad text keys are filenames** — use `"hero.jpg"`, not `"/path/to/hero.jpg"`
515
+ 7. **`textPresetId` and `texts` are mutually exclusive** — use one or the other
516
+ 8. **Objective-specific CTAs** (`MESSAGE_PAGE`, `WHATSAPP_MESSAGE`, etc.) are inherited from the template — don't set them manually
517
+ 9. **Do not use `--json`** — it's for shell scripts, not interactive use. The human-readable output has everything you need