@wrangled/cli 0.3.45 → 0.3.46
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/dist/guide.js +5 -1
- package/dist/main.js +103 -19
- package/package.json +1 -1
package/dist/guide.js
CHANGED
|
@@ -140,6 +140,7 @@ A webhook address lets a tool such as a form or webinar platform push events int
|
|
|
140
140
|
3. A run that is still going prints its id. Check it with \`${cli} tools get <run-id>\`. Image results list an \`asset\` link. Save the file with \`${cli} creatives download <asset-link>\`, or fetch the link with your API key as a bearer token; it redirects to a copy that works for 10 minutes.
|
|
141
141
|
4. Record a succeeded run in the ledger with its lineage: \`${cli} creatives create --run <run-id> --lineage '{"persona": "...", "painPoint": "...", "desiredOutcome": "...", "angle": "...", "sourceAd": {"metaAdId": "..."}}'\`. Each output gets a SKU such as \`DLC-000123\`. The SKU goes in the Meta ad name. Lineage is free-text notes; give the ad's marketing tags with \`--tags '<json>'\` or \`--tags @tags.json\`, the same fields as ad tags (see Ad tags), such as \`{"offer_key": "course", "landing_funnel_key": "webinar_to_course", "landing_step_key": "registration_page", "persona": "near_retirement", "angle": "story", "format": "image"}\`. Every output of the run gets them. Values come from \`${cli} grammar\`; persona and concept from this client's lists, and offer, funnel and step from its confirmed map (\`${cli} funnels show\`), where the step must be in that funnel and \`reply\` or \`no_link\` take no step. A value off them is refused with the ones to use. Recording the same run again returns its creatives as they are, tags included; \`${cli} creatives tag <sku> --file tags.json\` is how to change a creative's tags. Your tags read proposed until an Owner or Admin confirms them on the creative's page in Content; after that only a person changes them.
|
|
142
142
|
5. \`${cli} creatives list\` and \`${cli} creatives show <sku>\` read the ledger. \`${cli} creatives download <sku>\` saves a creative's image or video as \`<sku>.<ext>\` in the current folder; \`--out <path>\` names the file or a folder, and \`--open\` opens it. It also takes an asset path or link, such as a \`meta_ad_creatives\` \`image_path\`. \`--format json\` prints \`{ path, bytes, contentType }\`. Copy has no file; its text is in \`${cli} creatives show\`. For performance, query the \`creatives\` table or the \`creative_performance_daily\` view with \`${cli} warehouse query\`.
|
|
143
|
+
6. \`${cli} media list\` lists every image and video the tools made, newest first, whether or not it became a creative: its media id (\`<run id>:<index>\`), tool, prompt, cost and SKU, if any. Narrow it with \`--sku <sku>\`, \`--run <run-id>\` (one batch), \`--tool kie:nano-banana\`, \`--kind image|video\`, \`--since\` and \`--until\` (a day such as \`2026-10-08\`, which \`--until\` counts through its end in UTC, or an ISO time), and \`--limit <n>\` (default 50, at most 200). When more remain it prints the \`--cursor <media-id>\` that shows the next page. A run's cost is split evenly across its outputs and marked as an estimate when the provider did not report a charge. \`${cli} media get <media-id>\` shows the full input that made one, to reuse a good prompt; \`${cli} media download <media-id>\` saves its file, named by its SKU when it has one (\`${cli} creatives download\` takes a media id too). \`--format json\` prints \`{ media, nextCursor }\`.
|
|
143
144
|
|
|
144
145
|
## Landing pages
|
|
145
146
|
|
|
@@ -326,9 +327,12 @@ ${cli} tools run <tool> --input '<json>' [--idempotency-key <key>] [--wait <seco
|
|
|
326
327
|
${cli} tools get <run-id> show a tool run, waiting for it to finish (--wait)
|
|
327
328
|
${cli} creatives list list creatives (--status, --kind, --limit)
|
|
328
329
|
${cli} creatives show <sku> show one creative with its spec and lineage
|
|
329
|
-
${cli} creatives download <sku | asset-path | asset-link> [--out <path>] [--open] save a creative's image or video
|
|
330
|
+
${cli} creatives download <sku | media-id | asset-path | asset-link> [--out <path>] [--open] save a creative's image or video
|
|
330
331
|
${cli} creatives create --run <run-id> [--lineage '<json>' | @file.json] [--tags '<json>' | @file.json] record a run's output with a new SKU
|
|
331
332
|
${cli} creatives tag <sku> --file <tags.json> replace a creative's marketing tags, proposed until a person confirms them
|
|
333
|
+
${cli} media list list every made image and video (--sku, --run, --tool, --kind, --since, --until, --limit, --cursor)
|
|
334
|
+
${cli} media get <media-id> show the tool, model, full input and cost that made one
|
|
335
|
+
${cli} media download <media-id> [--out <path>] [--open] save a made image or video
|
|
332
336
|
${cli} content create --kind <kind> [--body ...] [--file <path>] [--note <why>] [--plan <plan-id>] save made content with a new SKU
|
|
333
337
|
${cli} content search [--text <words>] [--mode word|meaning] [--kind <kind>] [--platform <p>] [--origin <o>] [--from <date>] [--to <date>] [--sort <column>] [--order asc|desc] [--limit <n>] find content with its numbers
|
|
334
338
|
${cli} content pull <dir> [--kind <kind>] [--platform <p>] [--origin <o>] [--media] [--full] copy the library to a folder of Markdown files; rerun for changes
|
package/dist/main.js
CHANGED
|
@@ -545,20 +545,11 @@ const commands = {
|
|
|
545
545
|
},
|
|
546
546
|
},
|
|
547
547
|
"creatives download": {
|
|
548
|
-
usage: "creatives download <sku | asset-path | asset-link> [--out <path>] [--open]",
|
|
548
|
+
usage: "creatives download <sku | media-id | asset-path | asset-link> [--out <path>] [--open]",
|
|
549
549
|
async run(args, options) {
|
|
550
550
|
if (args.length !== 1)
|
|
551
|
-
throw new CliError("Usage: creatives download <sku | asset-path | asset-link> [--out <path>] [--open]", exitCodes.usage);
|
|
552
|
-
|
|
553
|
-
const target = await downloadTarget(args[0], connection);
|
|
554
|
-
const { bytes, contentType } = await apiDownload(connection, `${ASSET_ROUTE}${target.assetPath.split("/").map(encodeURIComponent).join("/")}`);
|
|
555
|
-
const path = await savePath(options.out, extname(target.fileName) ? target.fileName : `${target.fileName}.${extensionFor(contentType)}`);
|
|
556
|
-
await saveFile(path, bytes);
|
|
557
|
-
if (options.open)
|
|
558
|
-
openFile(resolve(path));
|
|
559
|
-
if (options.format === "json")
|
|
560
|
-
return printJson({ path: resolve(path), bytes: bytes.byteLength, contentType });
|
|
561
|
-
console.log(`Saved ${target.label} to ${path} (${fileSize(bytes.byteLength)}).`);
|
|
551
|
+
throw new CliError("Usage: creatives download <sku | media-id | asset-path | asset-link> [--out <path>] [--open]", exitCodes.usage);
|
|
552
|
+
await download(args[0], options);
|
|
562
553
|
},
|
|
563
554
|
},
|
|
564
555
|
"creatives create": {
|
|
@@ -591,6 +582,48 @@ const commands = {
|
|
|
591
582
|
console.log(`Tagged ${creative.sku}. The tags read ${creative.tagsStatus} until a person confirms them in the app.`);
|
|
592
583
|
},
|
|
593
584
|
},
|
|
585
|
+
"media list": {
|
|
586
|
+
usage: "media list [--sku <sku>] [--run <tool-run-id>] [--tool <tool>] [--kind image|video] [--since <date>] [--until <date>] [--limit <n>] [--cursor <media-id>]",
|
|
587
|
+
async run(args, options) {
|
|
588
|
+
if (args.length > 0)
|
|
589
|
+
throw new CliError("Usage: media list [--sku <sku>] [--run <tool-run-id>] [--tool <tool>] [--kind image|video] [--since <date>] [--until <date>] [--limit <n>] [--cursor <media-id>]", exitCodes.usage);
|
|
590
|
+
const query = new URLSearchParams();
|
|
591
|
+
for (const name of ["sku", "run", "tool", "kind", "since", "until", "limit", "cursor"]) {
|
|
592
|
+
if (options[name])
|
|
593
|
+
query.set(name, options[name]);
|
|
594
|
+
}
|
|
595
|
+
const page = await api(await resolveConnection(options), `/api/v1/media${query.size ? `?${query}` : ""}`);
|
|
596
|
+
if (options.format === "json")
|
|
597
|
+
return printJson(page);
|
|
598
|
+
if (page.media.length === 0)
|
|
599
|
+
return console.log("No made images or videos match.");
|
|
600
|
+
const rows = page.media.map((m) => ({ ...m, prompt: excerpt(m.prompt ?? ""), cost: costLabel(m), sku: m.sku ?? "" }));
|
|
601
|
+
console.log(renderTable(["id", "tool", "prompt", "cost", "sku", "createdAt", "assetUrl"], rows));
|
|
602
|
+
if (page.nextCursor)
|
|
603
|
+
console.log(`\nMore: add --cursor ${page.nextCursor}`);
|
|
604
|
+
},
|
|
605
|
+
},
|
|
606
|
+
"media get": {
|
|
607
|
+
usage: "media get <media-id>",
|
|
608
|
+
async run(args, options) {
|
|
609
|
+
if (args.length !== 1)
|
|
610
|
+
throw new CliError("Usage: media get <media-id>", exitCodes.usage);
|
|
611
|
+
const { media } = await api(await resolveConnection(options), mediaPath(args[0]));
|
|
612
|
+
if (options.format === "json")
|
|
613
|
+
return printJson({ media });
|
|
614
|
+
const fields = { ...media, cost: costLabel(media) };
|
|
615
|
+
const shown = ["id", "tool", "model", "prompt", "input", "cost", "contentType", "sku", "creativeStatus", "assetUrl", "workerRunId", "createdAt"];
|
|
616
|
+
console.log(renderTable(["field", "value"], shown.filter((f) => fields[f] != null).map((f) => ({ field: f, value: fields[f] }))));
|
|
617
|
+
},
|
|
618
|
+
},
|
|
619
|
+
"media download": {
|
|
620
|
+
usage: "media download <media-id> [--out <path>] [--open]",
|
|
621
|
+
async run(args, options) {
|
|
622
|
+
if (args.length !== 1 || !parseMediaId(args[0]))
|
|
623
|
+
throw new CliError("Usage: media download <media-id> [--out <path>] [--open]", exitCodes.usage);
|
|
624
|
+
await download(args[0], options);
|
|
625
|
+
},
|
|
626
|
+
},
|
|
594
627
|
"content create": {
|
|
595
628
|
usage: "content create --kind <kind> [--name <name>] [--subject <s>] [--preview <s>] [--headline <s>] [--body <text> | @file] [--html @file] [--fields '<json>' | @file.json] [--file <path> ...] [--tool-run <id> ...] [--run <agent-run-id>] [--note <why>] [--plan <plan-id> [--role <role>]]",
|
|
596
629
|
async run(args, options) {
|
|
@@ -2539,11 +2572,11 @@ Options:
|
|
|
2539
2572
|
for warehouse schema, a table to describe in full; repeat for several
|
|
2540
2573
|
--summary warehouse schema: one short row per table (kind, source, column count, summary)
|
|
2541
2574
|
--search <words> warehouse schema: describe in full the tables whose name, description or columns mention a word
|
|
2542
|
-
--out <path> the file, or existing folder, creatives download and warehouse query save to; warehouse query
|
|
2575
|
+
--out <path> the file, or existing folder, creatives download, media download and warehouse query save to; warehouse query
|
|
2543
2576
|
writes CSV for a .csv path, otherwise JSON
|
|
2544
2577
|
--all with warehouse query --out, save the whole answer (up to 1,000,000 rows or 50 MiB) instead of
|
|
2545
2578
|
the first 1,000 rows or 5 MiB; .ndjson or .jsonl saves one JSON row per line
|
|
2546
|
-
--open open the file creatives download saved
|
|
2579
|
+
--open open the file creatives download or media download saved
|
|
2547
2580
|
--media with content pull, also save each item's files and HTML under media/ in the folder
|
|
2548
2581
|
--full with content pull, fetch every item, not only what changed since the last pull (brings new numbers)
|
|
2549
2582
|
--force content unpublish: take the page down even while delivering Meta ads link to it
|
|
@@ -2568,7 +2601,14 @@ Options:
|
|
|
2568
2601
|
--source <text> where the content content capture saves came from, in words
|
|
2569
2602
|
--url <url> the address of the content content capture saves
|
|
2570
2603
|
--tool-run <id> a succeeded tool run content create and edit link; repeat for several
|
|
2571
|
-
--run <id> the agent run making the content (content),
|
|
2604
|
+
--run <id> the agent run making the content (content), the tool run to record (creatives create), or the tool
|
|
2605
|
+
run whose outputs media list shows
|
|
2606
|
+
--sku <sku> with media list, only the output that became this creative
|
|
2607
|
+
--tool <tool> with media list, only this tool's outputs, such as kie:nano-banana
|
|
2608
|
+
--kind image|video with media list, only images or only videos
|
|
2609
|
+
--since, --until <YYYY-MM-DD | ISO time> with media list, made at or after --since and before --until (a day: through
|
|
2610
|
+
its end, in UTC)
|
|
2611
|
+
--cursor <media-id> with media list, the next page: the id it printed after "More"
|
|
2572
2612
|
--platform, --external-id where content status launched says it was published, and that platform's id for it
|
|
2573
2613
|
--object-type <type> what that platform's sync calls it (campaign_message or flow_message on klaviyo); needed where
|
|
2574
2614
|
the sync matches several kinds, so its next read joins the item
|
|
@@ -2600,9 +2640,9 @@ Options:
|
|
|
2600
2640
|
--from, --to <n> with content diff, the two versions to compare (default: the one before the current one, and the current one)
|
|
2601
2641
|
--sort <column> how content search sorts: recent (default) or a content_scorecard number, such as click_rate or roas
|
|
2602
2642
|
--order desc|asc content search's sort direction (default: desc, best or newest first)
|
|
2603
|
-
--limit <n> how many results content search, creatives list, campaigns, voice, ads tags, content tags
|
|
2604
|
-
return, how many ads ads autotag tags and how many items content autotag
|
|
2605
|
-
ads each ads tag-formats batch looks at (default 2000)
|
|
2643
|
+
--limit <n> how many results content search, creatives list, media list, campaigns, voice, ads tags, content tags
|
|
2644
|
+
and content to-check return, how many ads ads autotag tags and how many items content autotag
|
|
2645
|
+
tags, or how many ads each ads tag-formats batch looks at (default 2000)
|
|
2606
2646
|
--library <slug> with knowledge search, only this library: creative-cartel, exodus or meta-policy
|
|
2607
2647
|
--angle, --hook-kind, --ad-format, --awareness <value> with knowledge search, only library entries tagged that way, and
|
|
2608
2648
|
your own ads tagged the same way with their results; with knowledge tag, the tags to put on an entry;
|
|
@@ -2669,6 +2709,10 @@ function parseOptions(argv) {
|
|
|
2669
2709
|
yes: { type: "boolean", default: false },
|
|
2670
2710
|
from: { type: "string" },
|
|
2671
2711
|
since: { type: "string" },
|
|
2712
|
+
until: { type: "string" },
|
|
2713
|
+
sku: { type: "string" },
|
|
2714
|
+
tool: { type: "string" },
|
|
2715
|
+
cursor: { type: "string" },
|
|
2672
2716
|
to: { type: "string" },
|
|
2673
2717
|
grain: { type: "string" },
|
|
2674
2718
|
channel: { type: "string" },
|
|
@@ -2780,6 +2824,10 @@ function parseOptions(argv) {
|
|
|
2780
2824
|
yes: values.yes,
|
|
2781
2825
|
from: values.from,
|
|
2782
2826
|
since: values.since,
|
|
2827
|
+
until: values.until,
|
|
2828
|
+
sku: values.sku,
|
|
2829
|
+
tool: values.tool,
|
|
2830
|
+
cursor: values.cursor,
|
|
2783
2831
|
to: values.to,
|
|
2784
2832
|
grain: values.grain,
|
|
2785
2833
|
channel: values.channel,
|
|
@@ -3391,8 +3439,44 @@ async function readImportFile(path) {
|
|
|
3391
3439
|
return readFile(path);
|
|
3392
3440
|
}
|
|
3393
3441
|
const ASSET_ROUTE = "/api/v1/assets/";
|
|
3394
|
-
/**
|
|
3442
|
+
/** Saves a creative's or a made output's file, as `creatives download` and `media download` do. */
|
|
3443
|
+
async function download(arg, options) {
|
|
3444
|
+
const connection = await resolveConnection(options);
|
|
3445
|
+
const target = await downloadTarget(arg, connection);
|
|
3446
|
+
const { bytes, contentType } = await apiDownload(connection, `${ASSET_ROUTE}${target.assetPath.split("/").map(encodeURIComponent).join("/")}`);
|
|
3447
|
+
const path = await savePath(options.out, extname(target.fileName) ? target.fileName : `${target.fileName}.${extensionFor(contentType)}`);
|
|
3448
|
+
await saveFile(path, bytes);
|
|
3449
|
+
if (options.open)
|
|
3450
|
+
openFile(resolve(path));
|
|
3451
|
+
if (options.format === "json")
|
|
3452
|
+
return printJson({ path: resolve(path), bytes: bytes.byteLength, contentType });
|
|
3453
|
+
console.log(`Saved ${target.label} to ${path} (${fileSize(bytes.byteLength)}).`);
|
|
3454
|
+
}
|
|
3455
|
+
const MEDIA_ID = /^([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}):(0|[1-9]\d{0,3})$/i;
|
|
3456
|
+
function parseMediaId(raw) {
|
|
3457
|
+
const match = MEDIA_ID.exec(raw);
|
|
3458
|
+
return match ? { runId: match[1].toLowerCase(), index: Number(match[2]) } : null;
|
|
3459
|
+
}
|
|
3460
|
+
// A media id's colon is sent as it is, so the route reads the id whether or not the server decodes the path.
|
|
3461
|
+
function mediaPath(raw) {
|
|
3462
|
+
const id = parseMediaId(raw);
|
|
3463
|
+
if (!id)
|
|
3464
|
+
throw new CliError(`"${excerpt(raw)}" is not a media id; media list prints them as <run id>:<index>.`, exitCodes.usage);
|
|
3465
|
+
return `/api/v1/media/${id.runId}:${id.index}`;
|
|
3466
|
+
}
|
|
3467
|
+
function costLabel(media) {
|
|
3468
|
+
return `${media.costCents}¢${media.costIsEstimate ? " (estimate)" : ""}`;
|
|
3469
|
+
}
|
|
3470
|
+
/** What `creatives download` fetches, from a SKU, a media id, an asset path, or an asset link as `creatives show` prints it. */
|
|
3395
3471
|
async function downloadTarget(arg, connection) {
|
|
3472
|
+
const output = parseMediaId(arg);
|
|
3473
|
+
if (output) {
|
|
3474
|
+
const { media } = await api(connection, mediaPath(arg));
|
|
3475
|
+
if (!media.assetPath)
|
|
3476
|
+
throw new CliError(`${media.id} has no stored file to download.`, exitCodes.error);
|
|
3477
|
+
const name = media.sku ?? `${output.runId}-${output.index}`;
|
|
3478
|
+
return { label: media.sku ?? media.id, assetPath: media.assetPath, fileName: `${name}${extname(media.assetPath)}` };
|
|
3479
|
+
}
|
|
3396
3480
|
const routeAt = arg.indexOf(ASSET_ROUTE);
|
|
3397
3481
|
if (routeAt !== -1 || arg.includes("/")) {
|
|
3398
3482
|
const assetPath = routeAt === -1 ? arg : decodeLinkPath(arg.slice(routeAt + ASSET_ROUTE.length).replace(/[?#].*$/, ""));
|