@wrangled/cli 0.3.14 → 0.3.16
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 +3 -0
- package/dist/main.js +37 -4
- package/package.json +1 -1
package/dist/guide.js
CHANGED
|
@@ -172,6 +172,8 @@ Ads not made here (built by hand or by an agency, names in no pattern) get the m
|
|
|
172
172
|
|
|
173
173
|
To suggest tags, write them as JSON and run \`${cli} ads tag --file tags.json\` (or pipe it on stdin), up to 500 ads at a time: \`{"ads": [{"ad_id": "120214567890123", "tags": {"angle": "discovery", "desire": "money", "hook_spot": "spoken", "hook_text": "$29,000 is the smallest lead", "hook_kind": "big_win", "format": "video", "style": "presenter", "tag_reasons": {"worry": "none"}}, "confidence": "medium", "evidence": {"ad_name": "County lead v2", "transcript_start": "$29,000 is the smallest lead"}}]}\`. The tag fields are \`marketing\`, \`offer_key\`, \`landing_funnel_key\`, \`landing_step_key\`, \`persona\`, \`awareness\`, \`concept\`, \`angle\`, \`desire\`, \`hook_spot\`, \`hook_text\`, \`hook_kind\`, \`hook2_spot\`, \`hook2_kind\`, \`format\`, \`style\`, \`speaker\`, \`cta\`, \`proof\` and \`worry\`; leave out what you did not judge. A part with no value goes in \`tag_reasons\` as \`none\` (nothing on its list applies) or \`unclear\` (too little to judge, never \`high\` confidence). Confidence is \`high\`, \`medium\` or \`low\`. A value off its list is refused with the allowed values. New tags replace an ad's proposed ones, which stay as \`replaced\`; an ad whose tags a person confirmed is kept as it is.
|
|
174
174
|
|
|
175
|
+
To have the server tag ads for you, run \`${cli} ads autotag\` (\`--limit <n>\`, 1 to 100, default 25; \`--ad <id>\` for one ad; \`--json\`). It takes the biggest spenders with no tags yet, so each run moves on to the next ones (with \`--ad\`, that ad, its proposed tags asked about again and confirmed ones kept), reads each one's copy, video transcript and picture description, and asks TypeSafe's Jev the grammar's questions through the tool gateway, so a run's tool cap applies. Ads with the same copy, headlines, button, link and media are asked about once and share the tags; asking again about an unchanged ad costs nothing. It costs a tiny fraction of a cent per ad. The tags are proposed, with each part's confidence in the evidence; read them with \`${cli} ads tags\`.
|
|
176
|
+
|
|
175
177
|
For what worked, query the \`ad_performance_tagged\` view: every Meta ad's daily results with its live tags next to them, \`tags_source\` (\`ad_tags\`, or null when untagged) and \`tags_status\`.
|
|
176
178
|
|
|
177
179
|
## Ad words
|
|
@@ -285,6 +287,7 @@ ${cli} funnels draft --file <map.json> [--note <why>] [--evidence '<json>' | @fi
|
|
|
285
287
|
${cli} facts [--kind <kind>] [--subject <type>[:<ref>]] [--status <status>] [--json] | facts show <id> settled facts about the data, to treat as true
|
|
286
288
|
${cli} facts propose --file <fact.json> suggest a fact for a person to confirm
|
|
287
289
|
${cli} ads tags [--ad <id>] [--status <status>] [--limit <n>] | ads tag --file <tags.json> the marketing tags of ads not made here, and suggesting them
|
|
290
|
+
${cli} ads autotag [--limit <n>] [--ad <id>] have the server propose tags for the biggest spenders not yet tagged
|
|
288
291
|
${cli} grammar [--format json] every list ads are tagged from, this client's personas and concepts included
|
|
289
292
|
${cli} grammar propose --file <values.json> suggest personas and concepts for a person to confirm
|
|
290
293
|
${cli} voice [--kind opener|objection|phrase] [--limit <n>] [--json] what customers keep saying, grouped and counted, with real quotes
|
package/dist/main.js
CHANGED
|
@@ -686,13 +686,14 @@ const commands = {
|
|
|
686
686
|
},
|
|
687
687
|
},
|
|
688
688
|
"content status": {
|
|
689
|
-
usage: "content status <sku | item-id> <status> [--platform <platform> --external-id <id>]",
|
|
689
|
+
usage: "content status <sku | item-id> <status> [--platform <platform> [--object-type <type>] --external-id <id>]",
|
|
690
690
|
async run(args, options) {
|
|
691
691
|
if (args.length !== 2)
|
|
692
|
-
throw new CliError("Usage: content status <sku | item-id> <status> [--platform <platform> --external-id <id>]", exitCodes.usage);
|
|
692
|
+
throw new CliError("Usage: content status <sku | item-id> <status> [--platform <platform> [--object-type <type>] --external-id <id>]", exitCodes.usage);
|
|
693
693
|
const content = await api(await resolveConnection(options.apiUrl), `/api/v1/content/${encodeURIComponent(args[0])}/status`, {
|
|
694
694
|
status: args[1],
|
|
695
695
|
...(options.platform === undefined ? {} : { platform: options.platform }),
|
|
696
|
+
...(options.objectType === undefined ? {} : { objectType: options.objectType }),
|
|
696
697
|
...(options.externalId === undefined ? {} : { externalId: options.externalId }),
|
|
697
698
|
});
|
|
698
699
|
printContent(content, options.format);
|
|
@@ -1156,6 +1157,32 @@ const commands = {
|
|
|
1156
1157
|
console.log(`Kept as they are, since a person confirmed their tags: ${result.kept.map((k) => k.adId).join(", ")}.`);
|
|
1157
1158
|
},
|
|
1158
1159
|
},
|
|
1160
|
+
"ads autotag": {
|
|
1161
|
+
usage: "ads autotag [--limit <n>] [--ad <id>] [--json] propose marketing tags for the biggest-spending ads not yet tagged, or one ad",
|
|
1162
|
+
async run(args, options) {
|
|
1163
|
+
if (args.length > 0)
|
|
1164
|
+
throw new CliError("Usage: ads autotag [--limit <n>] [--ad <id>] [--json]", exitCodes.usage);
|
|
1165
|
+
const body = { ...(options.ad ? { ad_ids: [options.ad] } : {}), ...(options.limit ? { limit: Number(options.limit) } : {}) };
|
|
1166
|
+
const result = await api(await resolveConnection(options.apiUrl), AD_AUTOTAG_PATH, body);
|
|
1167
|
+
if (options.format === "json")
|
|
1168
|
+
return printJson(result);
|
|
1169
|
+
const plural = (n) => `${n} ${n === 1 ? "ad" : "ads"}`;
|
|
1170
|
+
console.log(`Proposed tags for ${plural(result.proposed.length)}, asking about ${result.creatives} ${result.creatives === 1 ? "creative" : "creatives"} for $${result.costUsd.toFixed(4)}. A person confirms or rejects them in the app.`);
|
|
1171
|
+
if (result.proposed.length > 0)
|
|
1172
|
+
console.log(renderTable(["ad", "id", "confidence"], result.proposed.map((p) => ({ ad: p.adId, id: p.id, confidence: p.confidence }))));
|
|
1173
|
+
if (result.kept.length > 0)
|
|
1174
|
+
console.log(`Kept as they are, since a person confirmed their tags: ${result.kept.join(", ")}.`);
|
|
1175
|
+
for (const reason of ["not_marketing", "gate_unclear", "no_content", "not_found"]) {
|
|
1176
|
+
const ids = result.skipped.filter((s) => s.reason === reason).map((s) => s.adId);
|
|
1177
|
+
if (ids.length > 0)
|
|
1178
|
+
console.log(`${SKIP_REASONS[reason]}: ${ids.join(", ")}.`);
|
|
1179
|
+
}
|
|
1180
|
+
for (const failure of result.failed)
|
|
1181
|
+
console.log(`Failed for ${failure.adIds.join(", ")}: ${failure.error}`);
|
|
1182
|
+
if (result.stopped === "tool_cap")
|
|
1183
|
+
console.log("Stopped early: the run's tool spending cap was reached.");
|
|
1184
|
+
},
|
|
1185
|
+
},
|
|
1159
1186
|
"facts show": {
|
|
1160
1187
|
usage: "facts show <id> one fact with its value and evidence",
|
|
1161
1188
|
async run(args, options) {
|
|
@@ -1683,6 +1710,8 @@ function printReport(title, report, labels, numbers) {
|
|
|
1683
1710
|
const agentPath = (options) => `/api/v1/agents/${encodeURIComponent(options.agent)}`;
|
|
1684
1711
|
const FACTS_PATH = "/api/v1/facts";
|
|
1685
1712
|
const AD_TAGS_PATH = "/api/v1/ad-tags";
|
|
1713
|
+
const AD_AUTOTAG_PATH = "/api/v1/ad-tags/autotag";
|
|
1714
|
+
const SKIP_REASONS = { not_marketing: "Not marketing, so only that answer was saved", gate_unclear: "Could not tell whether it is marketing, so only that was saved", no_content: "Nothing to read", not_found: "No such ad" };
|
|
1686
1715
|
const AD_TAG_COLUMNS = ["id", "ad", "status", "confidence", "offer", "persona", "angle", "hook", "format", "author"];
|
|
1687
1716
|
const GRAMMAR_PATH = "/api/v1/grammar";
|
|
1688
1717
|
const VOICE_LISTS_PATH = "/api/v1/voice-lists";
|
|
@@ -1799,6 +1828,8 @@ Options:
|
|
|
1799
1828
|
--tool-run <id> a succeeded tool run content create and edit link; repeat for several
|
|
1800
1829
|
--run <id> the agent run making the content (content), or the tool run to record (creatives create)
|
|
1801
1830
|
--platform, --external-id where content status launched says it was published, and that platform's id for it
|
|
1831
|
+
--object-type <type> what that platform's sync calls it (campaign_message or flow_message on klaviyo); needed where
|
|
1832
|
+
the sync matches several kinds, so its next read joins the item
|
|
1802
1833
|
--notes <text> what changed in the version skill publish adds
|
|
1803
1834
|
--version <n> the map version funnels show prints (default: the confirmed one)
|
|
1804
1835
|
--based-on <n> the map version funnels draft was written from; refused when someone saved since
|
|
@@ -1818,9 +1849,9 @@ Options:
|
|
|
1818
1849
|
--from, --to <n> with content diff, the two versions to compare (default: the one before the current one, and the current one)
|
|
1819
1850
|
--sort <column> how content search sorts: recent (default) or a content_scorecard number, such as click_rate or roas
|
|
1820
1851
|
--order desc|asc content search's sort direction (default: desc, best or newest first)
|
|
1821
|
-
--limit <n> how many results content search, creatives list, campaigns, voice and ads tags return
|
|
1852
|
+
--limit <n> how many results content search, creatives list, campaigns, voice and ads tags return, or how many ads ads autotag tags
|
|
1822
1853
|
--library <slug> with knowledge search, only this library, such as creative-cartel
|
|
1823
|
-
--ad <id> with ads tags, only this Meta ad's tags
|
|
1854
|
+
--ad <id> with ads tags, only this Meta ad's tags; with ads autotag, only this ad
|
|
1824
1855
|
--max-seconds <n> stop a runner's run after this many seconds, if that comes before its deadline
|
|
1825
1856
|
--workspace <slug> for worker promote and worker drop: the workspace whose hand they change
|
|
1826
1857
|
-h, --help show this help
|
|
@@ -1893,6 +1924,7 @@ function parseOptions(argv) {
|
|
|
1893
1924
|
platform: { type: "string" },
|
|
1894
1925
|
library: { type: "string" },
|
|
1895
1926
|
"external-id": { type: "string" },
|
|
1927
|
+
"object-type": { type: "string" },
|
|
1896
1928
|
origin: { type: "string" },
|
|
1897
1929
|
text: { type: "string" },
|
|
1898
1930
|
mode: { type: "string" },
|
|
@@ -1986,6 +2018,7 @@ function parseOptions(argv) {
|
|
|
1986
2018
|
platform: values.platform,
|
|
1987
2019
|
library: values.library,
|
|
1988
2020
|
externalId: values["external-id"],
|
|
2021
|
+
objectType: values["object-type"],
|
|
1989
2022
|
origin: values.origin,
|
|
1990
2023
|
text: values.text,
|
|
1991
2024
|
mode: values.mode,
|