@koda-sl/baker-cli 0.377.0-dev.1bb9432b7 → 0.377.0-dev.302280df6
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/README.md +54 -5
- package/dist/cli.js +2037 -548
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/dist/cli.js
CHANGED
|
@@ -133,7 +133,7 @@ import {
|
|
|
133
133
|
} from "./chunk-7YNOGIWQ.js";
|
|
134
134
|
|
|
135
135
|
// src/cli.ts
|
|
136
|
-
import { defineCommand as
|
|
136
|
+
import { defineCommand as defineCommand256, runMain } from "citty";
|
|
137
137
|
|
|
138
138
|
// src/cache-flag.ts
|
|
139
139
|
var NO_CACHE_ARG = {
|
|
@@ -1209,8 +1209,8 @@ var campaignUpdateSchema = z3.object({
|
|
|
1209
1209
|
}
|
|
1210
1210
|
validateCampaignBudgets(p, ctx, { requireBudget: false });
|
|
1211
1211
|
});
|
|
1212
|
-
function trackingParamsLevel(
|
|
1213
|
-
return SPONSORED_ACCOUNT_URN_REGEX.test(
|
|
1212
|
+
function trackingParamsLevel(target4) {
|
|
1213
|
+
return SPONSORED_ACCOUNT_URN_REGEX.test(target4) ? "account" : "campaign";
|
|
1214
1214
|
}
|
|
1215
1215
|
function bareLinkedinUrnId(urn) {
|
|
1216
1216
|
return urn.split(":").pop() ?? urn;
|
|
@@ -4329,7 +4329,8 @@ var CAPABILITY_SURFACES = [
|
|
|
4329
4329
|
"x-ads",
|
|
4330
4330
|
"tag-manager",
|
|
4331
4331
|
"ga4",
|
|
4332
|
-
"gsc"
|
|
4332
|
+
"gsc",
|
|
4333
|
+
"shopify"
|
|
4333
4334
|
];
|
|
4334
4335
|
var CAPABILITY_SURFACE_SPECS = {
|
|
4335
4336
|
"google-ads": {
|
|
@@ -4412,6 +4413,26 @@ var CAPABILITY_SURFACE_SPECS = {
|
|
|
4412
4413
|
writePath: "read-only",
|
|
4413
4414
|
writeScopes: ["https://www.googleapis.com/auth/webmasters"],
|
|
4414
4415
|
commands: "baker gsc"
|
|
4416
|
+
},
|
|
4417
|
+
shopify: {
|
|
4418
|
+
id: "shopify",
|
|
4419
|
+
label: "Shopify store",
|
|
4420
|
+
provider: "shopify",
|
|
4421
|
+
resourceNoun: "store",
|
|
4422
|
+
// Staged during the chat and applied to the real store when it completes,
|
|
4423
|
+
// every time; each applied change can be put back by another publish
|
|
4424
|
+
// (`baker shopify undo`). The write permissions are optional scopes the
|
|
4425
|
+
// store grants when a change first needs one, so `scopeBlockers` is the
|
|
4426
|
+
// per-company signal.
|
|
4427
|
+
writePath: "staged-immediate",
|
|
4428
|
+
writeScopes: [
|
|
4429
|
+
"write_products",
|
|
4430
|
+
"write_publications",
|
|
4431
|
+
"write_discounts",
|
|
4432
|
+
"write_online_store_navigation",
|
|
4433
|
+
"write_marketing_events"
|
|
4434
|
+
],
|
|
4435
|
+
commands: "baker shopify"
|
|
4415
4436
|
}
|
|
4416
4437
|
};
|
|
4417
4438
|
|
|
@@ -4530,6 +4551,8 @@ var chatChangeTypeSchema = z13.enum([
|
|
|
4530
4551
|
"x-ads",
|
|
4531
4552
|
"tag-manager",
|
|
4532
4553
|
"ga4",
|
|
4554
|
+
// Changes to the company's Shopify store (S9). Staged, applied at Publish.
|
|
4555
|
+
"shopify",
|
|
4533
4556
|
// A recipe the company added to their own marketplace, staged like any other
|
|
4534
4557
|
// Session effect — see convex/companyTemplates/mutations.ts.
|
|
4535
4558
|
"recipe",
|
|
@@ -4750,6 +4773,7 @@ var chatEffectsSchema = z13.object({
|
|
|
4750
4773
|
creatives: z13.array(creativeEffectSchema),
|
|
4751
4774
|
tagManager: z13.array(configEffectSchema),
|
|
4752
4775
|
ga4: z13.array(configEffectSchema),
|
|
4776
|
+
shopify: z13.array(configEffectSchema),
|
|
4753
4777
|
funnel: z13.array(funnelEffectSchema),
|
|
4754
4778
|
conversions: z13.array(settingEffectSchema),
|
|
4755
4779
|
conversionSends: z13.array(settingEffectSchema),
|
|
@@ -5087,6 +5111,20 @@ var FLOW_SIDE_EFFECT_CATALOG = [
|
|
|
5087
5111
|
reference: SIDE_EFFECT_REF("httpWebhook")
|
|
5088
5112
|
},
|
|
5089
5113
|
crmSideEffect("googleSpreadsheet", "Appends a row to a Google Sheet.", "columnMapping", MAP_FIRST),
|
|
5114
|
+
{
|
|
5115
|
+
kind: "side-effect",
|
|
5116
|
+
type: "shopifyCustomer",
|
|
5117
|
+
summary: "Adds a sign-up to the connected Shopify store with the consent they ticked.",
|
|
5118
|
+
skeleton: {
|
|
5119
|
+
...SIDE_EFFECT_ENVELOPE,
|
|
5120
|
+
type: "shopifyCustomer",
|
|
5121
|
+
customer: { email: null, phone: null, acceptsEmailMarketing: null, acceptsSmsMarketing: null },
|
|
5122
|
+
offer: null
|
|
5123
|
+
},
|
|
5124
|
+
owners: { customer: "agent", offer: "agent" },
|
|
5125
|
+
gotcha: "Map email to an email field with baker flows map. Email and SMS consent each need their own optional, visible, initially unticked checkbox; SMS also needs phone. No connection secret is needed. An optional offer names a published sign-up offer; read baker shopify signup-offers. Show its code with ShopifySignupCode.",
|
|
5126
|
+
reference: SIDE_EFFECT_REF("shopifyCustomer")
|
|
5127
|
+
},
|
|
5090
5128
|
crmSideEffect("hubspotForm", "Submits to a HubSpot form endpoint.", "fieldMapping", MAP_FIRST),
|
|
5091
5129
|
crmSideEffect("pipedrive", "Creates or updates a Pipedrive record.", "fieldMapping", MAP_FIRST),
|
|
5092
5130
|
crmSideEffect("goHighlevelContact", "Creates or updates a GoHighLevel contact.", "fieldMapping", MAP_FIRST),
|
|
@@ -6970,7 +7008,6 @@ var STORE_EVENTS = {
|
|
|
6970
7008
|
product_out_of_stock: {
|
|
6971
7009
|
label: "A product runs out of stock",
|
|
6972
7010
|
sentence: "When a product in your Shopify store runs out of stock",
|
|
6973
|
-
description: "One or more of its options can no longer be bought; whole_product says whether nothing at all can be.",
|
|
6974
7011
|
payload: [
|
|
6975
7012
|
"title",
|
|
6976
7013
|
"handle",
|
|
@@ -6981,13 +7018,11 @@ var STORE_EVENTS = {
|
|
|
6981
7018
|
"promoted_by",
|
|
6982
7019
|
"promotions_checked",
|
|
6983
7020
|
...PRODUCT_FIELDS.slice(2)
|
|
6984
|
-
]
|
|
6985
|
-
namesProduct: true
|
|
7021
|
+
]
|
|
6986
7022
|
},
|
|
6987
7023
|
product_back_in_stock: {
|
|
6988
7024
|
label: "A product is back in stock",
|
|
6989
7025
|
sentence: "When a product in your Shopify store is back in stock",
|
|
6990
|
-
description: "Options that ran out can be bought again, or ads were paused for it after it came back.",
|
|
6991
7026
|
payload: [
|
|
6992
7027
|
"title",
|
|
6993
7028
|
"handle",
|
|
@@ -6998,20 +7033,16 @@ var STORE_EVENTS = {
|
|
|
6998
7033
|
"reason",
|
|
6999
7034
|
"promoted_by",
|
|
7000
7035
|
...PRODUCT_FIELDS.slice(2)
|
|
7001
|
-
]
|
|
7002
|
-
namesProduct: true
|
|
7036
|
+
]
|
|
7003
7037
|
},
|
|
7004
7038
|
product_created: {
|
|
7005
7039
|
label: "A new product is added",
|
|
7006
7040
|
sentence: "When a new product is added to your Shopify store",
|
|
7007
|
-
|
|
7008
|
-
payload: [...PRODUCT_FIELDS],
|
|
7009
|
-
namesProduct: true
|
|
7041
|
+
payload: [...PRODUCT_FIELDS]
|
|
7010
7042
|
},
|
|
7011
7043
|
product_updated: {
|
|
7012
7044
|
label: "A product is changed",
|
|
7013
7045
|
sentence: "When a product in your Shopify store is changed",
|
|
7014
|
-
description: "Its price, title, status, publication, handle, tags, type, vendor or description changed; stock changes and changes Baker made do not count.",
|
|
7015
7046
|
payload: [
|
|
7016
7047
|
"title",
|
|
7017
7048
|
"handle",
|
|
@@ -7022,41 +7053,33 @@ var STORE_EVENTS = {
|
|
|
7022
7053
|
"before_published",
|
|
7023
7054
|
"price_changes",
|
|
7024
7055
|
...PRODUCT_FIELDS.slice(2)
|
|
7025
|
-
]
|
|
7026
|
-
namesProduct: true
|
|
7056
|
+
]
|
|
7027
7057
|
},
|
|
7028
7058
|
collection_updated: {
|
|
7029
7059
|
label: "A collection changes",
|
|
7030
7060
|
sentence: "When a collection in your Shopify store changes",
|
|
7031
|
-
|
|
7032
|
-
payload: ["title", "collection", "changed", "product_count", "before_product_count", "published", "store"],
|
|
7033
|
-
namesProduct: false
|
|
7061
|
+
payload: ["title", "collection", "changed", "product_count", "before_product_count", "published", "store"]
|
|
7034
7062
|
},
|
|
7035
7063
|
purchase: {
|
|
7036
7064
|
label: "An order is paid",
|
|
7037
7065
|
sentence: "When an order is paid",
|
|
7038
|
-
description: "The store's sale; value is the order's total in the store's currency.",
|
|
7039
7066
|
payload: ["order_name", "ordered_at", "shopify_source", "visitor_from", "campaign_from", "store"],
|
|
7040
|
-
|
|
7067
|
+
alsoTheSite: true
|
|
7041
7068
|
},
|
|
7042
7069
|
refund: {
|
|
7043
7070
|
label: "Money is refunded on an order",
|
|
7044
7071
|
sentence: "When money is refunded on an order",
|
|
7045
|
-
description: "Money given back on an order; value is the amount refunded.",
|
|
7046
7072
|
payload: ["order_name", "store"],
|
|
7047
|
-
|
|
7073
|
+
alsoTheSite: true
|
|
7048
7074
|
},
|
|
7049
7075
|
customer_created: {
|
|
7050
7076
|
label: "A new customer signs up",
|
|
7051
7077
|
sentence: "When a new customer account is created in your Shopify store",
|
|
7052
|
-
|
|
7053
|
-
payload: ["customer", "country", "accepts_email", "orders", "tags", "account_state", "store"],
|
|
7054
|
-
namesProduct: false
|
|
7078
|
+
payload: ["customer", "country", "accepts_email", "orders", "tags", "account_state", "store"]
|
|
7055
7079
|
},
|
|
7056
7080
|
checkout_abandoned: {
|
|
7057
7081
|
label: "A checkout is abandoned",
|
|
7058
7082
|
sentence: "When a shopper leaves a checkout in your Shopify store without buying",
|
|
7059
|
-
description: "A checkout with contact details was not completed after an hour.",
|
|
7060
7083
|
payload: [
|
|
7061
7084
|
"total",
|
|
7062
7085
|
"currency",
|
|
@@ -7069,13 +7092,11 @@ var STORE_EVENTS = {
|
|
|
7069
7092
|
"started_at",
|
|
7070
7093
|
"last_activity_at",
|
|
7071
7094
|
"store"
|
|
7072
|
-
]
|
|
7073
|
-
namesProduct: false
|
|
7095
|
+
]
|
|
7074
7096
|
},
|
|
7075
7097
|
sales_drop: {
|
|
7076
7098
|
label: "Sales drop",
|
|
7077
7099
|
sentence: "When your Shopify store's sales drop well below usual",
|
|
7078
|
-
description: "The last 6 hours brought far fewer orders than the same hours usually do.",
|
|
7079
7100
|
payload: [
|
|
7080
7101
|
"window_hours",
|
|
7081
7102
|
"window_start",
|
|
@@ -7088,8 +7109,7 @@ var STORE_EVENTS = {
|
|
|
7088
7109
|
"drop_percent",
|
|
7089
7110
|
"baseline_days",
|
|
7090
7111
|
"store"
|
|
7091
|
-
]
|
|
7092
|
-
namesProduct: false
|
|
7112
|
+
]
|
|
7093
7113
|
}
|
|
7094
7114
|
};
|
|
7095
7115
|
var STORE_EVENT_NAMES = Object.keys(STORE_EVENTS);
|
|
@@ -8435,8 +8455,8 @@ var videosDeleteResponseSchema = z29.object({ ok: z29.literal(true) });
|
|
|
8435
8455
|
|
|
8436
8456
|
// src/commands/actions/tagError.ts
|
|
8437
8457
|
var UNKNOWN_TAG_RE = /Unknown action tag\(s\): ([^.]+)\. Valid tags: ([^.]+)\./;
|
|
8438
|
-
function splitSlugs(
|
|
8439
|
-
return
|
|
8458
|
+
function splitSlugs(list3) {
|
|
8459
|
+
return list3.split(",").map((slug) => slug.trim()).filter((slug) => slug.length > 0);
|
|
8440
8460
|
}
|
|
8441
8461
|
function actionTagFix(message) {
|
|
8442
8462
|
const match = UNKNOWN_TAG_RE.exec(message);
|
|
@@ -8535,9 +8555,9 @@ function parsePriority(value, { allowClear }) {
|
|
|
8535
8555
|
`Unknown --priority "${value}". Expected one of: ${ACTION_PRIORITIES.join(", ")}${allowClear ? ", or 'none' to clear" : ""}.`
|
|
8536
8556
|
);
|
|
8537
8557
|
}
|
|
8538
|
-
function looksScheduled(name,
|
|
8558
|
+
function looksScheduled(name, description2) {
|
|
8539
8559
|
const haystack = fold(`${name}
|
|
8540
|
-
${
|
|
8560
|
+
${description2}`);
|
|
8541
8561
|
return SCHEDULE_SIGNALS.some((re2) => re2.test(haystack));
|
|
8542
8562
|
}
|
|
8543
8563
|
var CAMPAIGN_ENTITY = String.raw`(?:campaigns?|campanas?|ad\s*sets?|adsets?|ad\s*groups?|adgroups?|ads?|anuncios?|conjuntos?\s+de\s+anuncios|grupos?\s+de\s+anuncios)`;
|
|
@@ -8695,10 +8715,10 @@ var SURFACE_RULES = [
|
|
|
8695
8715
|
var FAN_OUT_NUDGE_THRESHOLD = 2;
|
|
8696
8716
|
var FAN_OUT_THRESHOLD = 3;
|
|
8697
8717
|
var SCHEDULED_HINT = 'This reads as recurring/scheduled work. If it should run on a cadence or a future date, create a Scheduled agent instead \u2014 baker scheduled-agents create --cron "0 9 * * MON" (or --run-at, or --trigger for a reaction rather than a clock; guide: `__tooling__/docs/tools/baker/scheduled-agents.md`). Do not capture "set up a schedule" as a Work Action.';
|
|
8698
|
-
function looksExecutable(name,
|
|
8718
|
+
function looksExecutable(name, description2) {
|
|
8699
8719
|
const foldedName = fold(name);
|
|
8700
8720
|
const full = `${foldedName}
|
|
8701
|
-
${fold(
|
|
8721
|
+
${fold(description2)}`;
|
|
8702
8722
|
for (const rule of SURFACE_RULES) {
|
|
8703
8723
|
const haystack = rule.nameOnly ? foldedName : full;
|
|
8704
8724
|
if (rule.except?.some((re2) => re2.test(full))) {
|
|
@@ -8718,14 +8738,21 @@ ${fold(description)}`;
|
|
|
8718
8738
|
}
|
|
8719
8739
|
function buildCreateHints({
|
|
8720
8740
|
name,
|
|
8721
|
-
description,
|
|
8741
|
+
description: description2,
|
|
8722
8742
|
tempId,
|
|
8723
8743
|
tags,
|
|
8724
8744
|
prioritySet,
|
|
8725
8745
|
draftCreateCount
|
|
8726
8746
|
}) {
|
|
8727
8747
|
const hints2 = [];
|
|
8728
|
-
const
|
|
8748
|
+
const expanded = shellExpandedIn(`${name}
|
|
8749
|
+
${description2}`);
|
|
8750
|
+
if (expanded) {
|
|
8751
|
+
hints2.push(
|
|
8752
|
+
`The text says \u201C${expanded}\u201D: the shell replaced a $ amount (like $0) inside double quotes before Baker saw it. Fix it now: baker actions update ${tempId} --description '<the text, with the amounts as you read them>' (and --name '\u2026' if the name has it) \u2014 single quotes keep $ as written.`
|
|
8753
|
+
);
|
|
8754
|
+
}
|
|
8755
|
+
const advisory = advisoryHint(name, description2);
|
|
8729
8756
|
if (advisory) {
|
|
8730
8757
|
hints2.push(
|
|
8731
8758
|
`${advisory.hint} If nothing actually blocks it: baker actions draft remove ${tempId}, then do the work.`
|
|
@@ -8741,7 +8768,7 @@ function buildCreateHints({
|
|
|
8741
8768
|
);
|
|
8742
8769
|
}
|
|
8743
8770
|
hints2.push(`Link dependencies: baker actions link --blocker <id> --blocked ${tempId}`);
|
|
8744
|
-
if (!
|
|
8771
|
+
if (!description2) {
|
|
8745
8772
|
hints2.push("Add description: baker actions update <tempId> --description '...' (what/why/where/done-when)");
|
|
8746
8773
|
}
|
|
8747
8774
|
if (!tags || tags.length === 0) {
|
|
@@ -8756,11 +8783,14 @@ function buildCreateHints({
|
|
|
8756
8783
|
}
|
|
8757
8784
|
return hints2;
|
|
8758
8785
|
}
|
|
8759
|
-
function
|
|
8760
|
-
|
|
8786
|
+
function shellExpandedIn(text2) {
|
|
8787
|
+
return /(?:^|[\s(])((?:\/usr)?\/bin\/(?:ba|z|da|k)?sh|-?(?:ba|z|da|k)sh)\b/.exec(text2)?.[1] ?? null;
|
|
8788
|
+
}
|
|
8789
|
+
function advisoryHint(name, description2) {
|
|
8790
|
+
if (looksScheduled(name, description2)) {
|
|
8761
8791
|
return { kind: "scheduled", hint: SCHEDULED_HINT };
|
|
8762
8792
|
}
|
|
8763
|
-
const executable = looksExecutable(name,
|
|
8793
|
+
const executable = looksExecutable(name, description2);
|
|
8764
8794
|
return executable ? { kind: "executable", ...executable } : null;
|
|
8765
8795
|
}
|
|
8766
8796
|
var ACTIONS_LIST_DEFAULT_LIMIT = 500;
|
|
@@ -8846,9 +8876,9 @@ function parseSkillFrontmatter(md) {
|
|
|
8846
8876
|
const lines = extractFrontmatterLines(md);
|
|
8847
8877
|
if (!lines) return null;
|
|
8848
8878
|
const name = readField(lines, "name");
|
|
8849
|
-
const
|
|
8850
|
-
if (!name || !
|
|
8851
|
-
const trimmed =
|
|
8879
|
+
const description2 = readField(lines, "description");
|
|
8880
|
+
if (!name || !description2) return null;
|
|
8881
|
+
const trimmed = description2.length > DESCRIPTION_MAX ? `${description2.slice(0, DESCRIPTION_MAX)}\u2026` : description2;
|
|
8852
8882
|
return { name, description: trimmed };
|
|
8853
8883
|
}
|
|
8854
8884
|
function findSkillsDir(startDir) {
|
|
@@ -9948,7 +9978,7 @@ import { defineCommand as defineCommand19 } from "citty";
|
|
|
9948
9978
|
function listed(resources) {
|
|
9949
9979
|
return resources.map((resource) => resource.label ? `${resource.id} (${resource.label})` : resource.id).join("; ");
|
|
9950
9980
|
}
|
|
9951
|
-
function connectedResourceHints({ plural:
|
|
9981
|
+
function connectedResourceHints({ plural: plural3, singular, flag: flag3, resources }) {
|
|
9952
9982
|
if (resources.length === 0) {
|
|
9953
9983
|
return [];
|
|
9954
9984
|
}
|
|
@@ -9956,7 +9986,7 @@ function connectedResourceHints({ plural: plural2, singular, flag: flag3, resour
|
|
|
9956
9986
|
return [`This company connected one ${singular}: ${listed(resources)}. Commands use it unless you pass ${flag3}.`];
|
|
9957
9987
|
}
|
|
9958
9988
|
return [
|
|
9959
|
-
`This company connected ${resources.length} ${
|
|
9989
|
+
`This company connected ${resources.length} ${plural3}: ${listed(resources)}. Each command works on one, so pass ${flag3} \u2014 nothing is picked for you. A question about the whole business covers all ${resources.length}: answering from one and reporting it as the total is wrong.`
|
|
9960
9990
|
];
|
|
9961
9991
|
}
|
|
9962
9992
|
|
|
@@ -11591,8 +11621,8 @@ function capabilityFix(surface) {
|
|
|
11591
11621
|
// src/commands/ads/boolean-word-positional.ts
|
|
11592
11622
|
var BOOLEAN_WORD = /^(true|false)$/i;
|
|
11593
11623
|
function booleanWordPositionalError(args5, entity) {
|
|
11594
|
-
const
|
|
11595
|
-
const word =
|
|
11624
|
+
const positionals2 = Array.isArray(args5._) ? args5._ : [];
|
|
11625
|
+
const word = positionals2.find((value2) => typeof value2 === "string" && BOOLEAN_WORD.test(value2));
|
|
11596
11626
|
if (word === void 0) {
|
|
11597
11627
|
return void 0;
|
|
11598
11628
|
}
|
|
@@ -11926,7 +11956,7 @@ function words(headline) {
|
|
|
11926
11956
|
return headline.toLowerCase().replace(/[^\p{L}\p{N}\s]/gu, " ").split(/\s+/).filter(Boolean);
|
|
11927
11957
|
}
|
|
11928
11958
|
function canonical(word) {
|
|
11929
|
-
for (const [i,
|
|
11959
|
+
for (const [i, group2] of CYCLED_VERBS.entries()) if (group2.includes(word)) return `group:${i}`;
|
|
11930
11960
|
return word;
|
|
11931
11961
|
}
|
|
11932
11962
|
function overlap(a, b) {
|
|
@@ -12201,11 +12231,11 @@ function requireTarget(args5, entity) {
|
|
|
12201
12231
|
failWriteValidation(booleanWord);
|
|
12202
12232
|
}
|
|
12203
12233
|
const positional = Array.isArray(args5._) ? args5._[0] : void 0;
|
|
12204
|
-
const
|
|
12205
|
-
if (typeof
|
|
12234
|
+
const target4 = args5.id ?? args5.target ?? args5.ref ?? positional;
|
|
12235
|
+
if (typeof target4 !== "string" || target4.length === 0) {
|
|
12206
12236
|
failWriteValidation(`pass the ${entity} resource name or id as the positional argument`);
|
|
12207
12237
|
}
|
|
12208
|
-
return
|
|
12238
|
+
return target4;
|
|
12209
12239
|
}
|
|
12210
12240
|
function requireStringFlag(value, flag3) {
|
|
12211
12241
|
if (typeof value !== "string" || value.length === 0) {
|
|
@@ -12413,11 +12443,11 @@ async function stageGoogleOps(rawOps, hints2) {
|
|
|
12413
12443
|
handleGoogleError(err);
|
|
12414
12444
|
}
|
|
12415
12445
|
}
|
|
12416
|
-
async function stageUpdate(kind, customerId,
|
|
12417
|
-
await stageGoogleOp({ kind, customerId, target:
|
|
12446
|
+
async function stageUpdate(kind, customerId, target4, payload, hints2) {
|
|
12447
|
+
await stageGoogleOp({ kind, customerId, target: target4, payload }, hints2);
|
|
12418
12448
|
}
|
|
12419
|
-
async function stageTarget(kind, customerId,
|
|
12420
|
-
await stageGoogleOp({ kind, customerId, target:
|
|
12449
|
+
async function stageTarget(kind, customerId, target4, hints2) {
|
|
12450
|
+
await stageGoogleOp({ kind, customerId, target: target4 }, hints2);
|
|
12421
12451
|
}
|
|
12422
12452
|
async function draftAction(path49, body, chat) {
|
|
12423
12453
|
try {
|
|
@@ -12596,9 +12626,9 @@ var DATAFORSEO_LANGUAGES = [
|
|
|
12596
12626
|
{ code: "da", name: "Danish" },
|
|
12597
12627
|
{ code: "fi", name: "Finnish" }
|
|
12598
12628
|
];
|
|
12599
|
-
function toRecord(
|
|
12629
|
+
function toRecord(list3) {
|
|
12600
12630
|
const map = {};
|
|
12601
|
-
for (const item of
|
|
12631
|
+
for (const item of list3) {
|
|
12602
12632
|
const key = item.id ?? item.code;
|
|
12603
12633
|
if (key) map[key] = item.name;
|
|
12604
12634
|
}
|
|
@@ -13974,9 +14004,9 @@ function servingScopeWarnings(args5, finalQuery) {
|
|
|
13974
14004
|
}
|
|
13975
14005
|
function parseSelectedFields(gaql) {
|
|
13976
14006
|
const match = gaql.match(/\bSELECT\b([\s\S]*?)\bFROM\b/i);
|
|
13977
|
-
const
|
|
13978
|
-
if (!
|
|
13979
|
-
return
|
|
14007
|
+
const list3 = match?.[1];
|
|
14008
|
+
if (!list3) return [];
|
|
14009
|
+
return list3.split(",").map((field) => field.trim()).filter((field) => /^[A-Za-z_][A-Za-z0-9_]*(\.[A-Za-z0-9_]+)*$/.test(field));
|
|
13980
14010
|
}
|
|
13981
14011
|
function foldObservedFields(rows, observed) {
|
|
13982
14012
|
for (const row of rows) {
|
|
@@ -14973,6 +15003,7 @@ var DESTINATION_NAMES = {
|
|
|
14973
15003
|
googleSpreadsheet: "Google Sheets",
|
|
14974
15004
|
hubspotForm: "HubSpot Form",
|
|
14975
15005
|
goHighlevelContact: "GoHighLevel",
|
|
15006
|
+
shopifyCustomer: "Shopify",
|
|
14976
15007
|
crmble: "Crmble"
|
|
14977
15008
|
};
|
|
14978
15009
|
function destinationKey(type) {
|
|
@@ -18075,19 +18106,19 @@ var CSV_SEPARATORS = /* @__PURE__ */ new Set([",", ";", " "]);
|
|
|
18075
18106
|
function splitCsvLine(line) {
|
|
18076
18107
|
const fields = [];
|
|
18077
18108
|
let current = "";
|
|
18078
|
-
let
|
|
18109
|
+
let quoted2 = false;
|
|
18079
18110
|
for (let i = 0; i < line.length; i++) {
|
|
18080
18111
|
const char = line[i];
|
|
18081
|
-
if (
|
|
18112
|
+
if (quoted2 && char !== '"') {
|
|
18082
18113
|
current += char;
|
|
18083
|
-
} else if (
|
|
18084
|
-
|
|
18085
|
-
if (
|
|
18114
|
+
} else if (quoted2) {
|
|
18115
|
+
quoted2 = line[i + 1] === '"';
|
|
18116
|
+
if (quoted2) {
|
|
18086
18117
|
current += '"';
|
|
18087
18118
|
i += 1;
|
|
18088
18119
|
}
|
|
18089
18120
|
} else if (char === '"') {
|
|
18090
|
-
|
|
18121
|
+
quoted2 = true;
|
|
18091
18122
|
} else if (CSV_SEPARATORS.has(char ?? "")) {
|
|
18092
18123
|
fields.push(current);
|
|
18093
18124
|
current = "";
|
|
@@ -18431,13 +18462,13 @@ var budgetsCommand = defineCommand31({
|
|
|
18431
18462
|
},
|
|
18432
18463
|
run: async ({ args: args5 }) => {
|
|
18433
18464
|
const customerId = requireCustomerId(args5);
|
|
18434
|
-
const
|
|
18465
|
+
const target4 = requireTarget(args5, "budget");
|
|
18435
18466
|
const payload = mergePayload(loadJsonFileArg(args5.file), {
|
|
18436
18467
|
name: args5.name,
|
|
18437
18468
|
amountMicros: microsFlag(args5.amount, "--amount"),
|
|
18438
18469
|
deliveryMethod: args5.delivery
|
|
18439
18470
|
});
|
|
18440
|
-
await stageUpdate("google.budget.update", customerId,
|
|
18471
|
+
await stageUpdate("google.budget.update", customerId, target4, payload, BUDGET_DEMAND_HINTS);
|
|
18441
18472
|
}
|
|
18442
18473
|
})
|
|
18443
18474
|
}
|
|
@@ -18753,7 +18784,7 @@ var campaignsCommand = defineCommand31({
|
|
|
18753
18784
|
if (level !== "campaign") {
|
|
18754
18785
|
failWriteValidation("--level must be campaign or account");
|
|
18755
18786
|
}
|
|
18756
|
-
const
|
|
18787
|
+
const target4 = requireTarget(args5, "campaign");
|
|
18757
18788
|
const payload = mergePayload(loadJsonFileArg(args5.file), {
|
|
18758
18789
|
name: args5.name,
|
|
18759
18790
|
budget: args5["budget-ref"],
|
|
@@ -18766,7 +18797,7 @@ var campaignsCommand = defineCommand31({
|
|
|
18766
18797
|
euPoliticalAds: args5["eu-political-ads"],
|
|
18767
18798
|
status: args5.status
|
|
18768
18799
|
});
|
|
18769
|
-
await stageUpdate("google.campaign.update", customerId,
|
|
18800
|
+
await stageUpdate("google.campaign.update", customerId, target4, payload);
|
|
18770
18801
|
}
|
|
18771
18802
|
}),
|
|
18772
18803
|
pause: statusCommand2("google.campaign.pause", "campaign"),
|
|
@@ -18836,13 +18867,13 @@ var adGroupsCommand = defineCommand31({
|
|
|
18836
18867
|
},
|
|
18837
18868
|
run: async ({ args: args5 }) => {
|
|
18838
18869
|
const customerId = requireCustomerId(args5);
|
|
18839
|
-
const
|
|
18870
|
+
const target4 = requireTarget(args5, "ad group");
|
|
18840
18871
|
const payload = mergePayload(loadJsonFileArg(args5.file), {
|
|
18841
18872
|
name: args5.name,
|
|
18842
18873
|
cpcBidMicros: microsFlag(args5["cpc-bid"], "--cpc-bid"),
|
|
18843
18874
|
status: args5.status
|
|
18844
18875
|
});
|
|
18845
|
-
await stageUpdate("google.adGroup.update", customerId,
|
|
18876
|
+
await stageUpdate("google.adGroup.update", customerId, target4, payload);
|
|
18846
18877
|
}
|
|
18847
18878
|
}),
|
|
18848
18879
|
pause: statusCommand2("google.adGroup.pause", "ad group"),
|
|
@@ -18870,11 +18901,11 @@ function keywordFinalUrlHints(finalUrl, count2) {
|
|
|
18870
18901
|
}
|
|
18871
18902
|
function keywordUpdateFinalUrls(args5) {
|
|
18872
18903
|
const finalUrl = args5["final-url"];
|
|
18873
|
-
const
|
|
18904
|
+
const clear2 = args5["clear-final-url"] === true;
|
|
18874
18905
|
if (typeof finalUrl !== "string" || finalUrl.length === 0) {
|
|
18875
|
-
return
|
|
18906
|
+
return clear2 ? [] : void 0;
|
|
18876
18907
|
}
|
|
18877
|
-
if (
|
|
18908
|
+
if (clear2) {
|
|
18878
18909
|
failWriteValidation(
|
|
18879
18910
|
"--clear-final-url sends the keyword back to the ad's landing page \u2014 pass it OR --final-url, not both"
|
|
18880
18911
|
);
|
|
@@ -19490,13 +19521,13 @@ var assetGroupsCommand = defineCommand31({
|
|
|
19490
19521
|
},
|
|
19491
19522
|
run: async ({ args: args5 }) => {
|
|
19492
19523
|
const customerId = requireCustomerId(args5);
|
|
19493
|
-
const
|
|
19524
|
+
const target4 = requireTarget(args5, "asset group");
|
|
19494
19525
|
const payload = mergePayload(loadJsonFileArg(args5.file), {
|
|
19495
19526
|
name: args5.name,
|
|
19496
19527
|
finalUrls: listFlag(args5["final-urls"]),
|
|
19497
19528
|
status: args5.status
|
|
19498
19529
|
});
|
|
19499
|
-
await stageUpdate("google.assetGroup.update", customerId,
|
|
19530
|
+
await stageUpdate("google.assetGroup.update", customerId, target4, payload);
|
|
19500
19531
|
}
|
|
19501
19532
|
}),
|
|
19502
19533
|
attach: defineCommand31({
|
|
@@ -19534,9 +19565,9 @@ var assetGroupsCommand = defineCommand31({
|
|
|
19534
19565
|
])
|
|
19535
19566
|
}
|
|
19536
19567
|
});
|
|
19537
|
-
function fileCreateCommand(name, kind,
|
|
19568
|
+
function fileCreateCommand(name, kind, description2, hints2) {
|
|
19538
19569
|
return defineCommand31({
|
|
19539
|
-
meta: { name, description },
|
|
19570
|
+
meta: { name, description: description2 },
|
|
19540
19571
|
args: { ...customerIdArg, ...fileArg },
|
|
19541
19572
|
run: async ({ args: args5 }) => {
|
|
19542
19573
|
const customerId = requireCustomerId(args5);
|
|
@@ -19578,7 +19609,7 @@ var assetsUpdateCommand = defineCommand31({
|
|
|
19578
19609
|
},
|
|
19579
19610
|
run: async ({ args: args5 }) => {
|
|
19580
19611
|
const customerId = requireCustomerId(args5);
|
|
19581
|
-
const
|
|
19612
|
+
const target4 = requireTarget(args5, "asset");
|
|
19582
19613
|
const payload = mergePayload(loadJsonFileArg(args5.file), {
|
|
19583
19614
|
name: args5.name,
|
|
19584
19615
|
linkText: args5["link-text"],
|
|
@@ -19591,7 +19622,7 @@ var assetsUpdateCommand = defineCommand31({
|
|
|
19591
19622
|
callToAction: args5.cta,
|
|
19592
19623
|
text: args5.text
|
|
19593
19624
|
});
|
|
19594
|
-
await stageUpdate("google.asset.update", customerId,
|
|
19625
|
+
await stageUpdate("google.asset.update", customerId, target4, payload);
|
|
19595
19626
|
}
|
|
19596
19627
|
});
|
|
19597
19628
|
var assetsCommand = defineCommand31({
|
|
@@ -19904,8 +19935,8 @@ var campaignCriteriaCommand = defineCommand31({
|
|
|
19904
19935
|
},
|
|
19905
19936
|
run: async ({ args: args5 }) => {
|
|
19906
19937
|
const customerId = requireCustomerId(args5);
|
|
19907
|
-
const
|
|
19908
|
-
await stageUpdate("google.campaignCriterion.update", customerId,
|
|
19938
|
+
const target4 = requireTarget(args5, "campaign criterion");
|
|
19939
|
+
await stageUpdate("google.campaignCriterion.update", customerId, target4, {
|
|
19909
19940
|
bidModifier: bidModifierFlag(args5["bid-modifier"])
|
|
19910
19941
|
});
|
|
19911
19942
|
}
|
|
@@ -20335,7 +20366,7 @@ async function stageLinkedinOps(rawOps, hints2) {
|
|
|
20335
20366
|
async function stageStatusChange(input) {
|
|
20336
20367
|
const payload = input.kind === "creative.update" ? { intendedStatus: input.status } : { status: input.status };
|
|
20337
20368
|
await stageLinkedinOps(
|
|
20338
|
-
input.targets.map((
|
|
20369
|
+
input.targets.map((target4) => ({ kind: input.kind, accountId: input.accountId, target: target4, payload }))
|
|
20339
20370
|
);
|
|
20340
20371
|
}
|
|
20341
20372
|
function requireTarget2(args5, entity) {
|
|
@@ -20343,17 +20374,17 @@ function requireTarget2(args5, entity) {
|
|
|
20343
20374
|
if (inverted !== void 0) {
|
|
20344
20375
|
failWriteValidation2(inverted);
|
|
20345
20376
|
}
|
|
20346
|
-
const
|
|
20347
|
-
const
|
|
20348
|
-
if (typeof
|
|
20377
|
+
const positionals2 = readPositionals(args5);
|
|
20378
|
+
const target4 = args5.id ?? args5.target ?? args5.ref ?? positionals2[0];
|
|
20379
|
+
if (typeof target4 !== "string" || target4.length === 0) {
|
|
20349
20380
|
failWriteValidation2(`pass the ${entity} id or URN as the positional argument`);
|
|
20350
20381
|
}
|
|
20351
|
-
if (
|
|
20382
|
+
if (positionals2.length > 1 || splitIdList(target4).length > 1) {
|
|
20352
20383
|
failWriteValidation2(
|
|
20353
20384
|
`this command takes one ${entity} id \u2014 run it once per id. Only pause/resume/archive and 'creatives update' accept several ids in one call.`
|
|
20354
20385
|
);
|
|
20355
20386
|
}
|
|
20356
|
-
return
|
|
20387
|
+
return target4;
|
|
20357
20388
|
}
|
|
20358
20389
|
function readPositionals(args5) {
|
|
20359
20390
|
if (!Array.isArray(args5._)) {
|
|
@@ -20371,11 +20402,11 @@ function idsFileEntries(path49) {
|
|
|
20371
20402
|
return readFileSync5(path49, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#")).flatMap(splitIdList);
|
|
20372
20403
|
}
|
|
20373
20404
|
function requireTargets(args5, entity) {
|
|
20374
|
-
const
|
|
20405
|
+
const positionals2 = readPositionals(args5);
|
|
20375
20406
|
const named = [args5.id, args5.target, args5.ref].filter(
|
|
20376
20407
|
(value) => typeof value === "string" && value.length > 0
|
|
20377
20408
|
);
|
|
20378
|
-
const fromArgs =
|
|
20409
|
+
const fromArgs = positionals2.length > 0 ? positionals2 : named;
|
|
20379
20410
|
const targets = [.../* @__PURE__ */ new Set([...fromArgs.flatMap(splitIdList), ...idsFileEntries(args5["ids-file"])])];
|
|
20380
20411
|
if (targets.length === 0) {
|
|
20381
20412
|
failWriteValidation2(
|
|
@@ -21319,9 +21350,9 @@ Example: baker ads linkedin campaigns create --name "ABM Tier-1" --group li_temp
|
|
|
21319
21350
|
await stageOp({ kind: "campaign.create", accountId, payload });
|
|
21320
21351
|
}
|
|
21321
21352
|
});
|
|
21322
|
-
function campaignUpdatePayloadFromFlags(args5,
|
|
21353
|
+
function campaignUpdatePayloadFromFlags(args5, target4, file) {
|
|
21323
21354
|
const flags = campaignPayloadFromFlags(args5);
|
|
21324
|
-
if (!
|
|
21355
|
+
if (!target4.startsWith(TEMP_REF_PREFIX)) {
|
|
21325
21356
|
flags.campaignGroup = void 0;
|
|
21326
21357
|
flags.type = void 0;
|
|
21327
21358
|
flags.locale = void 0;
|
|
@@ -21342,9 +21373,9 @@ Example: baker ads linkedin campaigns update 123456 --daily-budget 100 --currenc
|
|
|
21342
21373
|
},
|
|
21343
21374
|
run: async ({ args: args5 }) => {
|
|
21344
21375
|
const accountId = bareAccountId(args5);
|
|
21345
|
-
const
|
|
21346
|
-
const payload = campaignUpdatePayloadFromFlags(args5,
|
|
21347
|
-
await stageOp({ kind: "campaign.update", accountId, target:
|
|
21376
|
+
const target4 = requireTarget2(args5, "campaign");
|
|
21377
|
+
const payload = campaignUpdatePayloadFromFlags(args5, target4, loadJsonFileArg2(args5.file));
|
|
21378
|
+
await stageOp({ kind: "campaign.update", accountId, target: target4, payload });
|
|
21348
21379
|
}
|
|
21349
21380
|
});
|
|
21350
21381
|
function statusSugarCommand(entity, kind, name) {
|
|
@@ -21491,11 +21522,11 @@ Examples:
|
|
|
21491
21522
|
file: { type: "string", description: "JSON file with the full payload; flags override file keys" }
|
|
21492
21523
|
},
|
|
21493
21524
|
run: async ({ args: args5, rawArgs }) => {
|
|
21494
|
-
const { accountId, target:
|
|
21525
|
+
const { accountId, target: target4 } = accountTrackingTarget(args5);
|
|
21495
21526
|
await stageOp({
|
|
21496
21527
|
kind: "trackingParams.set",
|
|
21497
21528
|
accountId,
|
|
21498
|
-
target:
|
|
21529
|
+
target: target4,
|
|
21499
21530
|
payload: trackingParamsPayload(args5, rawArgs)
|
|
21500
21531
|
});
|
|
21501
21532
|
}
|
|
@@ -21783,7 +21814,7 @@ Examples:
|
|
|
21783
21814
|
campaign: args5.campaign,
|
|
21784
21815
|
content: Object.keys(content).length > 0 ? content : void 0
|
|
21785
21816
|
});
|
|
21786
|
-
await stageLinkedinOps(targets.map((
|
|
21817
|
+
await stageLinkedinOps(targets.map((target4) => ({ kind: "creative.update", accountId, target: target4, payload })));
|
|
21787
21818
|
}
|
|
21788
21819
|
});
|
|
21789
21820
|
var AUDIENCE_TYPES2 = ["company-list", "user-list", "retargeting", "engagement"];
|
|
@@ -21807,7 +21838,7 @@ Example: baker ads linkedin audiences create --name "ABM Tier-1" --type company-
|
|
|
21807
21838
|
},
|
|
21808
21839
|
run: async ({ args: args5 }) => {
|
|
21809
21840
|
const accountId = bareAccountId(args5);
|
|
21810
|
-
const
|
|
21841
|
+
const list3 = parseListFileArg(args5["list-file"], INLINE_ROWS_MAX);
|
|
21811
21842
|
const raw = args5.file ? loadJsonFileArg2(args5.file) : void 0;
|
|
21812
21843
|
await stageOp({
|
|
21813
21844
|
kind: "audience.create",
|
|
@@ -21815,7 +21846,7 @@ Example: baker ads linkedin audiences create --name "ABM Tier-1" --type company-
|
|
|
21815
21846
|
payload: {
|
|
21816
21847
|
name: args5.name,
|
|
21817
21848
|
audienceType: args5.type,
|
|
21818
|
-
...
|
|
21849
|
+
...list3 ? { listColumns: list3.columns, listRows: list3.rows } : {},
|
|
21819
21850
|
...raw ? { raw } : {}
|
|
21820
21851
|
}
|
|
21821
21852
|
});
|
|
@@ -21834,8 +21865,8 @@ Example: baker ads linkedin audiences upload urn:li:dmpSegment:42 --list-file mo
|
|
|
21834
21865
|
},
|
|
21835
21866
|
run: async ({ args: args5 }) => {
|
|
21836
21867
|
const accountId = bareAccountId(args5);
|
|
21837
|
-
const
|
|
21838
|
-
if (!
|
|
21868
|
+
const list3 = parseListFileArg(args5["list-file"], INLINE_ROWS_MAX);
|
|
21869
|
+
if (!list3) {
|
|
21839
21870
|
failWriteValidation2("--list-file is required");
|
|
21840
21871
|
}
|
|
21841
21872
|
await stageOp({
|
|
@@ -21843,8 +21874,8 @@ Example: baker ads linkedin audiences upload urn:li:dmpSegment:42 --list-file mo
|
|
|
21843
21874
|
accountId,
|
|
21844
21875
|
payload: {
|
|
21845
21876
|
segment: requireTarget2(args5, "audience segment"),
|
|
21846
|
-
listColumns:
|
|
21847
|
-
listRows:
|
|
21877
|
+
listColumns: list3.columns,
|
|
21878
|
+
listRows: list3.rows
|
|
21848
21879
|
}
|
|
21849
21880
|
});
|
|
21850
21881
|
}
|
|
@@ -26040,11 +26071,11 @@ function requireTarget3(args5, entity) {
|
|
|
26040
26071
|
failWriteValidation3(inverted);
|
|
26041
26072
|
}
|
|
26042
26073
|
const positional = Array.isArray(args5._) ? args5._[0] : void 0;
|
|
26043
|
-
const
|
|
26044
|
-
if (typeof
|
|
26074
|
+
const target4 = args5.id ?? args5.target ?? args5.ref ?? positional;
|
|
26075
|
+
if (typeof target4 !== "string" || target4.length === 0) {
|
|
26045
26076
|
failWriteValidation3(`pass the ${entity} id as the positional argument`);
|
|
26046
26077
|
}
|
|
26047
|
-
return
|
|
26078
|
+
return target4;
|
|
26048
26079
|
}
|
|
26049
26080
|
async function stageOp2(raw) {
|
|
26050
26081
|
const preflight = metaDraftOpInputSchema.safeParse(raw);
|
|
@@ -28233,13 +28264,13 @@ async function resolveXAccountId(args5) {
|
|
|
28233
28264
|
});
|
|
28234
28265
|
process.exit(1);
|
|
28235
28266
|
}
|
|
28236
|
-
const
|
|
28267
|
+
const list3 = accounts.map((a) => ` ${a.id} ${a.name}`).join("\n");
|
|
28237
28268
|
writeAdsJson({
|
|
28238
28269
|
ok: false,
|
|
28239
28270
|
error: {
|
|
28240
28271
|
code: "MULTIPLE_ACCOUNTS",
|
|
28241
28272
|
message: `Multiple X Ads accounts found. Pass --account-id or set BAKER_X_ADS_ACCOUNT_ID:
|
|
28242
|
-
${
|
|
28273
|
+
${list3}`
|
|
28243
28274
|
}
|
|
28244
28275
|
});
|
|
28245
28276
|
process.exit(1);
|
|
@@ -30326,10 +30357,10 @@ function wallClockAsUtc(instant, timeZone) {
|
|
|
30326
30357
|
}
|
|
30327
30358
|
function accountMidnightIso(daysAgo, timeZone, now = /* @__PURE__ */ new Date()) {
|
|
30328
30359
|
const localNow = new Date(wallClockAsUtc(now, timeZone));
|
|
30329
|
-
const
|
|
30330
|
-
let instant =
|
|
30360
|
+
const target4 = Date.UTC(localNow.getUTCFullYear(), localNow.getUTCMonth(), localNow.getUTCDate() - daysAgo, 0, 0, 0);
|
|
30361
|
+
let instant = target4;
|
|
30331
30362
|
for (let i = 0; i < 2; i++) {
|
|
30332
|
-
instant =
|
|
30363
|
+
instant = target4 - (wallClockAsUtc(new Date(instant), timeZone) - instant);
|
|
30333
30364
|
}
|
|
30334
30365
|
return isoSeconds(new Date(instant));
|
|
30335
30366
|
}
|
|
@@ -31839,12 +31870,12 @@ function setFromRename(args5, existing) {
|
|
|
31839
31870
|
}
|
|
31840
31871
|
function conversionsWrite(args5, existing) {
|
|
31841
31872
|
const set = [...setFromEvents(args5, existing), ...setFromRename(args5, existing)];
|
|
31842
|
-
const
|
|
31873
|
+
const remove2 = [
|
|
31843
31874
|
...args5.removeKeys,
|
|
31844
31875
|
...args5.removeName === void 0 ? [] : under(args5.removeName, existing).map((row) => row.eventKey)
|
|
31845
31876
|
];
|
|
31846
|
-
if (set.length === 0 &&
|
|
31847
|
-
return { ...set.length > 0 ? { set } : {}, ...
|
|
31877
|
+
if (set.length === 0 && remove2.length === 0) return null;
|
|
31878
|
+
return { ...set.length > 0 ? { set } : {}, ...remove2.length > 0 ? { remove: remove2 } : {} };
|
|
31848
31879
|
}
|
|
31849
31880
|
|
|
31850
31881
|
// src/commands/analytics/conversionHints.ts
|
|
@@ -31907,9 +31938,9 @@ function mergeStoredWithCounts(stored, reported) {
|
|
|
31907
31938
|
const counts = new Map(reported.map((row) => [row.name, row]));
|
|
31908
31939
|
const groups = /* @__PURE__ */ new Map();
|
|
31909
31940
|
for (const row of stored) {
|
|
31910
|
-
const
|
|
31911
|
-
|
|
31912
|
-
groups.set(row.name,
|
|
31941
|
+
const group2 = groups.get(row.name) ?? [];
|
|
31942
|
+
group2.push(row);
|
|
31943
|
+
groups.set(row.name, group2);
|
|
31913
31944
|
}
|
|
31914
31945
|
return [...groups.entries()].map(([name, rows]) => {
|
|
31915
31946
|
const modes = new Set(rows.map((row) => row.countMode));
|
|
@@ -32709,7 +32740,7 @@ import { defineCommand as defineCommand97 } from "citty";
|
|
|
32709
32740
|
var SET_FLAGS = ["--source-event", "--platform", "--tag", "--event"];
|
|
32710
32741
|
function parseSendsArgs(raw, argv, now = Date.now()) {
|
|
32711
32742
|
const set = parseSetFlags(raw, argv, now);
|
|
32712
|
-
const
|
|
32743
|
+
const remove2 = onlyOnce(raw.remove, "--remove", argv);
|
|
32713
32744
|
const days2 = raw.days === void 0 ? void 0 : Number(raw.days);
|
|
32714
32745
|
if (days2 !== void 0 && (!Number.isInteger(days2) || days2 <= 0 || days2 > 400)) {
|
|
32715
32746
|
throw new ConversionArgsError("--days must be a whole number of days, from 1 to 400.");
|
|
@@ -32725,7 +32756,7 @@ function parseSendsArgs(raw, argv, now = Date.now()) {
|
|
|
32725
32756
|
}
|
|
32726
32757
|
return {
|
|
32727
32758
|
...set === void 0 ? {} : { set },
|
|
32728
|
-
...
|
|
32759
|
+
...remove2 === void 0 ? {} : { remove: remove2 },
|
|
32729
32760
|
...days2 === void 0 ? {} : { days: days2 },
|
|
32730
32761
|
...uploads === void 0 ? {} : { uploads },
|
|
32731
32762
|
...raw.failed === true ? { failedOnly: true } : {},
|
|
@@ -35874,14 +35905,14 @@ function undisambiguatedSubsets(faces) {
|
|
|
35874
35905
|
}
|
|
35875
35906
|
function suggestFamilies(wanted, catalogue, limit2 = 5) {
|
|
35876
35907
|
const key = (s) => s.toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
35877
|
-
const
|
|
35878
|
-
if (!
|
|
35879
|
-
const words2 =
|
|
35908
|
+
const target4 = key(wanted);
|
|
35909
|
+
if (!target4) return [];
|
|
35910
|
+
const words2 = target4.split(" ");
|
|
35880
35911
|
const scored = catalogue.map((family) => {
|
|
35881
35912
|
const candidate = key(family);
|
|
35882
|
-
if (candidate ===
|
|
35883
|
-
if (candidate.startsWith(
|
|
35884
|
-
if (candidate.includes(
|
|
35913
|
+
if (candidate === target4) return { family, score: 0 };
|
|
35914
|
+
if (candidate.startsWith(target4)) return { family, score: 1 };
|
|
35915
|
+
if (candidate.includes(target4)) return { family, score: 2 };
|
|
35885
35916
|
const shared = words2.filter((w) => w.length > 2 && candidate.includes(w)).length;
|
|
35886
35917
|
return { family, score: shared > 0 ? 3 + (words2.length - shared) : Number.POSITIVE_INFINITY };
|
|
35887
35918
|
}).filter((c) => Number.isFinite(c.score)).sort((a, b) => a.score - b.score || a.family.localeCompare(b.family));
|
|
@@ -35942,8 +35973,8 @@ async function resolveBrandFaces(family, weights, subsets, deps, styles = ["norm
|
|
|
35942
35973
|
function rebaseFontFaceSrc(block2, fromDir, toDir = "src/styles") {
|
|
35943
35974
|
return block2.replace(/url\(\s*(['"]?)([^'")]+)\1\s*\)/gi, (whole, _quote, href) => {
|
|
35944
35975
|
if (!isRepoFontHref(href)) return whole;
|
|
35945
|
-
const
|
|
35946
|
-
return `url('${posix.relative(toDir,
|
|
35976
|
+
const target4 = posix.normalize(posix.join(fromDir, href));
|
|
35977
|
+
return `url('${posix.relative(toDir, target4)}')`;
|
|
35947
35978
|
});
|
|
35948
35979
|
}
|
|
35949
35980
|
function declaredFontFaces(source) {
|
|
@@ -36121,12 +36152,12 @@ function declaredWeights(body) {
|
|
|
36121
36152
|
return range;
|
|
36122
36153
|
}
|
|
36123
36154
|
function selfHostedWeights(css, family) {
|
|
36124
|
-
const
|
|
36155
|
+
const target4 = normalizeFamily(family);
|
|
36125
36156
|
const weights = /* @__PURE__ */ new Set();
|
|
36126
36157
|
for (const rule of stripCssComments(css).matchAll(/@font-face\s*\{([^}]*)\}/gi)) {
|
|
36127
36158
|
const body = rule[1] ?? "";
|
|
36128
36159
|
const declared = body.match(/font-family\s*:\s*([^;]+)/i)?.[1];
|
|
36129
|
-
if (!declared || normalizeFamily(declared) !==
|
|
36160
|
+
if (!declared || normalizeFamily(declared) !== target4) continue;
|
|
36130
36161
|
for (const weight of declaredWeights(body)) weights.add(weight);
|
|
36131
36162
|
}
|
|
36132
36163
|
return [...weights].sort((a, b) => a - b);
|
|
@@ -36136,12 +36167,12 @@ function fontFileSubset(href) {
|
|
|
36136
36167
|
return /^[A-Za-z0-9]+-[1-9]00(?:italic)?-([\w-]+)\.woff2$/.exec(file)?.[1] ?? null;
|
|
36137
36168
|
}
|
|
36138
36169
|
function selfHostedSubsets(css, family) {
|
|
36139
|
-
const
|
|
36170
|
+
const target4 = normalizeFamily(family);
|
|
36140
36171
|
const subsets = /* @__PURE__ */ new Set();
|
|
36141
36172
|
for (const rule of stripCssComments(css).matchAll(/@font-face\s*\{([^}]*)\}/gi)) {
|
|
36142
36173
|
const body = rule[1] ?? "";
|
|
36143
36174
|
const declared = body.match(/font-family\s*:\s*([^;]+)/i)?.[1];
|
|
36144
|
-
if (!declared || normalizeFamily(declared) !==
|
|
36175
|
+
if (!declared || normalizeFamily(declared) !== target4) continue;
|
|
36145
36176
|
for (const url of body.matchAll(/url\(\s*['"]?([^'")]+)['"]?\s*\)/gi)) {
|
|
36146
36177
|
const subset = fontFileSubset(url[1] ?? "");
|
|
36147
36178
|
if (subset) subsets.add(subset);
|
|
@@ -36150,12 +36181,12 @@ function selfHostedSubsets(css, family) {
|
|
|
36150
36181
|
return [...subsets].sort();
|
|
36151
36182
|
}
|
|
36152
36183
|
function selfHostedStyles(css, family) {
|
|
36153
|
-
const
|
|
36184
|
+
const target4 = normalizeFamily(family);
|
|
36154
36185
|
const styles = /* @__PURE__ */ new Set();
|
|
36155
36186
|
for (const rule of stripCssComments(css).matchAll(/@font-face\s*\{([^}]*)\}/gi)) {
|
|
36156
36187
|
const body = rule[1] ?? "";
|
|
36157
36188
|
const declared = body.match(/font-family\s*:\s*([^;]+)/i)?.[1];
|
|
36158
|
-
if (declared && normalizeFamily(declared) ===
|
|
36189
|
+
if (declared && normalizeFamily(declared) === target4) styles.add(faceStyle(body));
|
|
36159
36190
|
}
|
|
36160
36191
|
return [...styles].sort();
|
|
36161
36192
|
}
|
|
@@ -36179,12 +36210,12 @@ function unresolvedBrandFamilies(css) {
|
|
|
36179
36210
|
return declared.filter((family) => !loaded.has(family)).map((family) => asAuthored(css, family));
|
|
36180
36211
|
}
|
|
36181
36212
|
function stripFontFaceBlocks(source, family, styles) {
|
|
36182
|
-
const
|
|
36213
|
+
const target4 = normalizeFamily(family);
|
|
36183
36214
|
const ranges = [];
|
|
36184
36215
|
for (const rule of stripCssComments(source).matchAll(/@font-face\s*\{([^}]*)\}/gi)) {
|
|
36185
36216
|
const body = rule[1] ?? "";
|
|
36186
36217
|
const declared = body.match(/font-family\s*:\s*([^;]+)/i)?.[1];
|
|
36187
|
-
if (!declared || normalizeFamily(declared) !==
|
|
36218
|
+
if (!declared || normalizeFamily(declared) !== target4 || rule.index === void 0) continue;
|
|
36188
36219
|
if (styles && !styles.has(faceStyle(body))) continue;
|
|
36189
36220
|
ranges.push([rule.index, rule.index + rule[0].length]);
|
|
36190
36221
|
}
|
|
@@ -36778,11 +36809,11 @@ function scoreRetentionRisk(videoNodes) {
|
|
|
36778
36809
|
};
|
|
36779
36810
|
}
|
|
36780
36811
|
function scoreMechanismClarity(allPromptText) {
|
|
36781
|
-
const
|
|
36812
|
+
const clear2 = MECHANISM_CUE.test(allPromptText);
|
|
36782
36813
|
return {
|
|
36783
36814
|
dimension: "mechanism_clarity",
|
|
36784
|
-
score:
|
|
36785
|
-
note:
|
|
36815
|
+
score: clear2 ? 1 : 0.4,
|
|
36816
|
+
note: clear2 ? "states a mechanism/proof" : "no clear 'why it works' \u2014 add the mechanism"
|
|
36786
36817
|
};
|
|
36787
36818
|
}
|
|
36788
36819
|
function clamp01(n) {
|
|
@@ -36795,8 +36826,8 @@ function staticCopyLines(nodes) {
|
|
|
36795
36826
|
return specimenText2.split("\n").map((s) => s.trim()).filter(Boolean);
|
|
36796
36827
|
}
|
|
36797
36828
|
const gen = nodes.find((n) => n.type === "image_generate");
|
|
36798
|
-
const
|
|
36799
|
-
return
|
|
36829
|
+
const quoted2 = promptOf(gen).match(/"[^"]+"/g);
|
|
36830
|
+
return quoted2 ? quoted2.map((s) => s.slice(1, -1)) : [];
|
|
36800
36831
|
}
|
|
36801
36832
|
function scoreTextLegibilityRisk(nodes) {
|
|
36802
36833
|
const lines = staticCopyLines(nodes).length;
|
|
@@ -36945,8 +36976,8 @@ function canvasOutputsRoot(canvasFile, cwd = process.cwd(), exists2 = existsSync
|
|
|
36945
36976
|
const root = findWorkspaceRoot(path5.dirname(path5.resolve(cwd, canvasFile)), exists2) ?? cwd;
|
|
36946
36977
|
return path5.join(root, "canvas");
|
|
36947
36978
|
}
|
|
36948
|
-
function canonicalize(
|
|
36949
|
-
const abs = path5.resolve(
|
|
36979
|
+
function canonicalize(target4) {
|
|
36980
|
+
const abs = path5.resolve(target4);
|
|
36950
36981
|
let dir = abs;
|
|
36951
36982
|
for (; ; ) {
|
|
36952
36983
|
try {
|
|
@@ -36959,19 +36990,19 @@ function canonicalize(target2) {
|
|
|
36959
36990
|
}
|
|
36960
36991
|
}
|
|
36961
36992
|
}
|
|
36962
|
-
function isInside(root,
|
|
36963
|
-
const rel = path5.relative(root,
|
|
36993
|
+
function isInside(root, target4) {
|
|
36994
|
+
const rel = path5.relative(root, target4);
|
|
36964
36995
|
return rel !== "" && !rel.startsWith("..") && !path5.isAbsolute(rel);
|
|
36965
36996
|
}
|
|
36966
|
-
function toCanvasRelative(canvasDir,
|
|
36967
|
-
return path5.relative(canvasDir,
|
|
36997
|
+
function toCanvasRelative(canvasDir, target4) {
|
|
36998
|
+
return path5.relative(canvasDir, target4).split(path5.sep).join("/");
|
|
36968
36999
|
}
|
|
36969
37000
|
var RUNTIME_WORKSPACE_ROOT = "/home/user/repo";
|
|
36970
37001
|
function rewriteTarget(value, workspaceRoot) {
|
|
36971
37002
|
if (typeof value !== "string" || value.length === 0) return null;
|
|
36972
37003
|
if (value.includes("[TODO") || looksLikeHttpUrl(value) || !path5.isAbsolute(value)) return null;
|
|
36973
|
-
const
|
|
36974
|
-
if (isInside(workspaceRoot,
|
|
37004
|
+
const target4 = canonicalize(value);
|
|
37005
|
+
if (isInside(workspaceRoot, target4)) return target4;
|
|
36975
37006
|
const fromRuntime = path5.relative(RUNTIME_WORKSPACE_ROOT, path5.normalize(value));
|
|
36976
37007
|
if (fromRuntime !== "" && !fromRuntime.startsWith("..") && !path5.isAbsolute(fromRuntime)) {
|
|
36977
37008
|
return path5.join(workspaceRoot, fromRuntime);
|
|
@@ -36997,9 +37028,9 @@ function normalizeCanvasPaths(canvas, canvasDir, workspaceRoot) {
|
|
|
36997
37028
|
}
|
|
36998
37029
|
const key = localPathParam(n);
|
|
36999
37030
|
if (!key) return node;
|
|
37000
|
-
const
|
|
37001
|
-
if (
|
|
37002
|
-
const to = toCanvasRelative(dir,
|
|
37031
|
+
const target4 = rewriteTarget(params2[key], root);
|
|
37032
|
+
if (target4 === null) return node;
|
|
37033
|
+
const to = toCanvasRelative(dir, target4);
|
|
37003
37034
|
rewrites.push({ nodeId, param: key, from: params2[key], to });
|
|
37004
37035
|
return { ...node, params: { ...params2, [key]: to } };
|
|
37005
37036
|
});
|
|
@@ -37854,8 +37885,8 @@ function castAvatarsOnElements(elementsInput, avatars) {
|
|
|
37854
37885
|
if (!avatar) return el;
|
|
37855
37886
|
next += 1;
|
|
37856
37887
|
if (el.label) byLabel.set(el.label, avatar);
|
|
37857
|
-
const
|
|
37858
|
-
return { ...el, ...
|
|
37888
|
+
const description2 = avatar.subjectDescription?.trim();
|
|
37889
|
+
return { ...el, ...description2 ? { description: description2 } : {}, source_image: avatar.sheetUrl, source_is_sheet: true };
|
|
37859
37890
|
});
|
|
37860
37891
|
return cast.map((el) => {
|
|
37861
37892
|
const avatar = el.same_as ? byLabel.get(el.same_as) : void 0;
|
|
@@ -38150,7 +38181,7 @@ function buildFramePrompt(edge, sceneIndex, framePrompt, present2, hasAnchor, mo
|
|
|
38150
38181
|
"- ORIGINAL_FRAME \u2014 use ONLY for composition: framing, camera angle, shot size, subject placement, pose, and proportions. IGNORE its text, logo, brand name, colors, AND the identity of every person/animal/object in it \u2014 those come from the labeled reference images above, never from this frame. It is a DIFFERENT brand's footage with DIFFERENT actors, here ONLY to anchor where things sit and how the shot is framed (e.g. a profile/side angle stays a profile/side angle), never who they are or what palette to use."
|
|
38151
38182
|
] : []
|
|
38152
38183
|
].join("\n");
|
|
38153
|
-
const
|
|
38184
|
+
const description2 = framePrompt?.trim() || `the ${edge} frame of scene ${sceneIndex + 1} \u2014 describe the full composition, subjects, setting, action, lighting, and palette here. (Edit this line to change ONLY this frame.)`;
|
|
38154
38185
|
const isHookFrame = sceneIndex === 0 && edge === "start";
|
|
38155
38186
|
const cleanPlateBlock = [
|
|
38156
38187
|
"CRITICAL \u2014 RENDER A CLEAN PLATE WITH ZERO TEXT OR GRAPHIC OVERLAYS:",
|
|
@@ -38228,7 +38259,7 @@ function buildFramePrompt(edge, sceneIndex, framePrompt, present2, hasAnchor, mo
|
|
|
38228
38259
|
""
|
|
38229
38260
|
] : [],
|
|
38230
38261
|
"FRAME DESCRIPTION (this frame's editable prompt):",
|
|
38231
|
-
|
|
38262
|
+
description2,
|
|
38232
38263
|
"",
|
|
38233
38264
|
"Render exactly what the FRAME DESCRIPTION and the SHARED AD SPEC specify \u2014 this is the authoritative ad: its cast identity (via the reference images), palette, brand, and intent are law. Keep every recurring element identical to its reference image across all frames. Again: clean plate only \u2014 no rendered text, and no icons/stickers/emojis/badges (those live on the overlay layer).",
|
|
38234
38265
|
"",
|
|
@@ -39084,8 +39115,8 @@ function localizeNumeralsForNative(text2, langCode) {
|
|
|
39084
39115
|
if (!langCode || !INTEGER_SPELLERS[langCode]) return text2;
|
|
39085
39116
|
return text2.replace(/(?<![\w.,-])\d{1,9}(?![\w.,-])/g, (m) => spellNumber(langCode, Number.parseInt(m, 10)));
|
|
39086
39117
|
}
|
|
39087
|
-
function parseVoiceTraits(
|
|
39088
|
-
const d =
|
|
39118
|
+
function parseVoiceTraits(description2) {
|
|
39119
|
+
const d = description2.toLowerCase();
|
|
39089
39120
|
const out = {};
|
|
39090
39121
|
if (/\b(female|woman|women|girl|lady)\b/.test(d)) out.gender = "female";
|
|
39091
39122
|
else if (/\b(male|man|men|guy|boy)\b/.test(d)) out.gender = "male";
|
|
@@ -39296,11 +39327,11 @@ function makeVoiceFactory(blueprint, canonical2, nodes, voiceLanguage, pinnedVoi
|
|
|
39296
39327
|
const existing = bySpeaker.get(speaker);
|
|
39297
39328
|
if (existing) return existing;
|
|
39298
39329
|
const id = sanitizeId(`voice_${speaker}`, `voice_${bySpeaker.size}`);
|
|
39299
|
-
const
|
|
39300
|
-
const traits = parseVoiceTraits(
|
|
39330
|
+
const description2 = describe(speaker);
|
|
39331
|
+
const traits = parseVoiceTraits(description2);
|
|
39301
39332
|
if (voiceLanguage) traits.language = voiceLanguage;
|
|
39302
39333
|
nodes.push(
|
|
39303
|
-
pinnedVoiceId ? { id, type: "voice_select", params: { description, voice_id: pinnedVoiceId, ...traits } } : { id, type: "voice_select", params: { description, ...traits } }
|
|
39334
|
+
pinnedVoiceId ? { id, type: "voice_select", params: { description: description2, voice_id: pinnedVoiceId, ...traits } } : { id, type: "voice_select", params: { description: description2, ...traits } }
|
|
39304
39335
|
);
|
|
39305
39336
|
bySpeaker.set(speaker, id);
|
|
39306
39337
|
return id;
|
|
@@ -41302,8 +41333,8 @@ function mimeForFile(filePath) {
|
|
|
41302
41333
|
function toPosix(p) {
|
|
41303
41334
|
return p.split(path14.sep).join("/");
|
|
41304
41335
|
}
|
|
41305
|
-
function isInside2(dir,
|
|
41306
|
-
const rel = path14.relative(dir,
|
|
41336
|
+
function isInside2(dir, target4) {
|
|
41337
|
+
const rel = path14.relative(dir, target4);
|
|
41307
41338
|
return !rel.startsWith("..") && !path14.isAbsolute(rel);
|
|
41308
41339
|
}
|
|
41309
41340
|
function localPathRefsFromCanvas(parsed) {
|
|
@@ -41419,11 +41450,11 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
|
41419
41450
|
if (path14.isAbsolute(entry.path) || entry.path.split("/").includes("..")) {
|
|
41420
41451
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
41421
41452
|
}
|
|
41422
|
-
const
|
|
41423
|
-
if (
|
|
41453
|
+
const target4 = path14.resolve(resolvedTarget, entry.path);
|
|
41454
|
+
if (target4 !== resolvedTarget && !target4.startsWith(resolvedTarget + path14.sep)) {
|
|
41424
41455
|
throw new Error(`snapshot entry escapes the creative directory: ${entry.path}`);
|
|
41425
41456
|
}
|
|
41426
|
-
const existing = await readFile10(
|
|
41457
|
+
const existing = await readFile10(target4).catch(() => null);
|
|
41427
41458
|
if (existing) {
|
|
41428
41459
|
if (sha256Hex(existing) === entry.sha256) {
|
|
41429
41460
|
upToDate.push(entry.path);
|
|
@@ -41434,19 +41465,19 @@ async function restoreRunSnapshot(manifest, targetDir, opts = {}) {
|
|
|
41434
41465
|
continue;
|
|
41435
41466
|
}
|
|
41436
41467
|
}
|
|
41437
|
-
planned.push({ entry, target:
|
|
41468
|
+
planned.push({ entry, target: target4 });
|
|
41438
41469
|
}
|
|
41439
41470
|
if (conflicts.length > 0) throw new SnapshotConflictError(conflicts);
|
|
41440
41471
|
const restored = [];
|
|
41441
|
-
for (const { entry, target:
|
|
41472
|
+
for (const { entry, target: target4 } of planned) {
|
|
41442
41473
|
const res = await fetch(entry.url);
|
|
41443
41474
|
if (!res.ok) throw new Error(`snapshot download failed for ${entry.path}: ${res.status} ${res.statusText}`);
|
|
41444
41475
|
const bytes = Buffer.from(await res.arrayBuffer());
|
|
41445
41476
|
if (sha256Hex(bytes) !== entry.sha256) {
|
|
41446
41477
|
throw new Error(`snapshot download for ${entry.path} does not match its recorded sha256`);
|
|
41447
41478
|
}
|
|
41448
|
-
await mkdir5(path14.dirname(
|
|
41449
|
-
await writeFile7(
|
|
41479
|
+
await mkdir5(path14.dirname(target4), { recursive: true });
|
|
41480
|
+
await writeFile7(target4, bytes);
|
|
41450
41481
|
restored.push(entry.path);
|
|
41451
41482
|
}
|
|
41452
41483
|
return { canvasPath: path14.resolve(resolvedTarget, manifest.canvas.path), restored, upToDate };
|
|
@@ -42592,8 +42623,8 @@ function castAvatarsOnStaticElements(elementsInput, avatars) {
|
|
|
42592
42623
|
const avatar = isPerson(el) ? avatars[next] : void 0;
|
|
42593
42624
|
if (!avatar) return el;
|
|
42594
42625
|
next += 1;
|
|
42595
|
-
const
|
|
42596
|
-
return { ...el, ...
|
|
42626
|
+
const description2 = avatar.subjectDescription?.trim();
|
|
42627
|
+
return { ...el, ...description2 ? { description: description2 } : {}, source_image: avatar.sheetUrl };
|
|
42597
42628
|
});
|
|
42598
42629
|
}
|
|
42599
42630
|
function staticAvatarsNotCast(elementsInput, handles) {
|
|
@@ -43116,9 +43147,9 @@ async function resolveCastAvatars(flag3) {
|
|
|
43116
43147
|
}
|
|
43117
43148
|
|
|
43118
43149
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
43119
|
-
async function fileExists(
|
|
43150
|
+
async function fileExists(target4) {
|
|
43120
43151
|
try {
|
|
43121
|
-
await access(
|
|
43152
|
+
await access(target4);
|
|
43122
43153
|
return true;
|
|
43123
43154
|
} catch {
|
|
43124
43155
|
return false;
|
|
@@ -43844,8 +43875,8 @@ function beatSeconds(line, videoModel) {
|
|
|
43844
43875
|
}
|
|
43845
43876
|
var BRAND_PLATE = "A solid flat brand colour plate \u2014 an end card. No people, no objects, no scenery, no text of any kind.";
|
|
43846
43877
|
var ASKS_FOR_LEGIBLE = /\b(focus on the (number|amount|total|price|figure|text)|readable|legible|clearly (shows?|visible)|highlighted (number|amount|total)|showing the (number|amount|total|price))\b/i;
|
|
43847
|
-
function briefWantsLegibleText(
|
|
43848
|
-
return ASKS_FOR_LEGIBLE.test(
|
|
43878
|
+
function briefWantsLegibleText(show2) {
|
|
43879
|
+
return ASKS_FOR_LEGIBLE.test(show2);
|
|
43849
43880
|
}
|
|
43850
43881
|
var SELF_INTRODUCTION = /\b([Ss]oy|[Mm]e llamo|[Mm]i nombre es|[Ss]ou|[Jj]e m'appelle|[Jj]e suis|[Mm]i chiamo|[Ss]ono|[Ii]ch bin|[Ii]'m|[Ii] am|[Mm]y name is)\s+[A-ZÁÉÍÓÚÑ]/;
|
|
43851
43882
|
function lineIntroducesSpeaker(line) {
|
|
@@ -43899,8 +43930,8 @@ function adSpecCastElements(spec, avatar) {
|
|
|
43899
43930
|
...spec.set?.image?.trim() ? { source_image: spec.set.image.trim() } : {}
|
|
43900
43931
|
});
|
|
43901
43932
|
}
|
|
43902
|
-
const
|
|
43903
|
-
if (!
|
|
43933
|
+
const description2 = (avatar?.subjectDescription ?? spec.cast?.description)?.trim();
|
|
43934
|
+
if (!description2) return elements;
|
|
43904
43935
|
const scenes = spec.beats.map((beat, i) => ({ beat, i })).filter(({ beat, i }) => beat.cast !== false && !(endCardWanted(spec) && i === lastIndex)).map(({ i }) => i);
|
|
43905
43936
|
if (scenes.length === 0) return elements;
|
|
43906
43937
|
const sheet = avatar?.sheetUrl?.trim();
|
|
@@ -43910,7 +43941,7 @@ function adSpecCastElements(spec, avatar) {
|
|
|
43910
43941
|
// Same id the dialogue names as speaker: that pairing is what lets the engine see
|
|
43911
43942
|
// the presenter as present in a scene and route it to native audio.
|
|
43912
43943
|
cast_id: AD_CAST_ID,
|
|
43913
|
-
description,
|
|
43944
|
+
description: description2,
|
|
43914
43945
|
scenes,
|
|
43915
43946
|
// An avatar's identity sheet IS the turnaround the engine would otherwise render,
|
|
43916
43947
|
// so it is handed over as one. Left unmarked, the run paid to generate a sheet OF
|
|
@@ -44520,7 +44551,7 @@ var scaffoldAdCommand = defineCommand115({
|
|
|
44520
44551
|
if (flagAvatar) {
|
|
44521
44552
|
spec.data.cast = { ...spec.data.cast, avatar: flagAvatar };
|
|
44522
44553
|
}
|
|
44523
|
-
const contradicts = spec.data.beats.map((beat, i) => ({ i, show: beat.show })).filter(({ show }) => briefWantsLegibleText(
|
|
44554
|
+
const contradicts = spec.data.beats.map((beat, i) => ({ i, show: beat.show })).filter(({ show: show2 }) => briefWantsLegibleText(show2));
|
|
44524
44555
|
if (contradicts.length > 0) {
|
|
44525
44556
|
writeJson({
|
|
44526
44557
|
ok: false,
|
|
@@ -45204,8 +45235,8 @@ function referenceVideoExt(url, contentType) {
|
|
|
45204
45235
|
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
45205
45236
|
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
45206
45237
|
}
|
|
45207
|
-
async function fileExists2(
|
|
45208
|
-
return access2(
|
|
45238
|
+
async function fileExists2(target4) {
|
|
45239
|
+
return access2(target4).then(
|
|
45209
45240
|
() => true,
|
|
45210
45241
|
() => false
|
|
45211
45242
|
);
|
|
@@ -46523,8 +46554,8 @@ async function importedFiles(text2, file, root) {
|
|
|
46523
46554
|
for (const match of text2.matchAll(re2)) {
|
|
46524
46555
|
const spec = match[1];
|
|
46525
46556
|
if (spec === void 0) continue;
|
|
46526
|
-
const
|
|
46527
|
-
if (
|
|
46557
|
+
const target4 = await resolveSpecifier(spec, file, root);
|
|
46558
|
+
if (target4 !== null) out.push(target4);
|
|
46528
46559
|
}
|
|
46529
46560
|
}
|
|
46530
46561
|
return out;
|
|
@@ -46542,8 +46573,8 @@ async function landingGraphFiles(root, slug) {
|
|
|
46542
46573
|
continue;
|
|
46543
46574
|
}
|
|
46544
46575
|
seen.add(file);
|
|
46545
|
-
for (const
|
|
46546
|
-
if (!seen.has(
|
|
46576
|
+
for (const target4 of await importedFiles(text2, file, root)) {
|
|
46577
|
+
if (!seen.has(target4)) queue.push(target4);
|
|
46547
46578
|
}
|
|
46548
46579
|
}
|
|
46549
46580
|
return new Set([...seen].map((abs) => path29.relative(root, abs).split(path29.sep).join("/")));
|
|
@@ -46666,8 +46697,8 @@ function repointMoved(text2, opts) {
|
|
|
46666
46697
|
return text2.replace(QUOTED_RELATIVE, (_match, quote, spec) => {
|
|
46667
46698
|
const abs = path31.resolve(opts.fromDir, spec);
|
|
46668
46699
|
const within = path31.relative(opts.movedFrom, abs);
|
|
46669
|
-
const
|
|
46670
|
-
return `${quote}${toSpecifier(opts.toDir,
|
|
46700
|
+
const target4 = within.startsWith("..") || path31.isAbsolute(within) ? abs : path31.resolve(opts.movedTo, within);
|
|
46701
|
+
return `${quote}${toSpecifier(opts.toDir, target4)}${quote}`;
|
|
46671
46702
|
});
|
|
46672
46703
|
}
|
|
46673
46704
|
|
|
@@ -46743,8 +46774,8 @@ function repointIntoArchive(text2, sourceAbs, destAbs, archivedBySource) {
|
|
|
46743
46774
|
const toDir = path32.dirname(destAbs);
|
|
46744
46775
|
return text2.replace(QUOTED_RELATIVE2, (match, quote, spec, offset) => {
|
|
46745
46776
|
const [specPath, suffix] = splitSuffix(spec);
|
|
46746
|
-
const
|
|
46747
|
-
const resolved = resolveInArchive(
|
|
46777
|
+
const target4 = path32.resolve(fromDir, specPath);
|
|
46778
|
+
const resolved = resolveInArchive(target4, archivedBySource) ?? resolveOnDisk(target4, isImportPosition(text2, offset));
|
|
46748
46779
|
if (resolved === null) return match;
|
|
46749
46780
|
const rel = posix2(path32.relative(toDir, resolved));
|
|
46750
46781
|
return `${quote}${rel.startsWith(".") ? rel : `./${rel}`}${suffix}${quote}`;
|
|
@@ -46802,63 +46833,63 @@ async function promoteVariantFiles(opts) {
|
|
|
46802
46833
|
}
|
|
46803
46834
|
return written;
|
|
46804
46835
|
}
|
|
46805
|
-
function archiveRecord(
|
|
46836
|
+
function archiveRecord(target4, variantNumber) {
|
|
46806
46837
|
return `${JSON.stringify(
|
|
46807
46838
|
{
|
|
46808
|
-
experimentId:
|
|
46809
|
-
page:
|
|
46810
|
-
pageInternalId:
|
|
46811
|
-
variant:
|
|
46839
|
+
experimentId: target4.experimentId,
|
|
46840
|
+
page: target4.landingSlug,
|
|
46841
|
+
pageInternalId: target4.landingInternalId,
|
|
46842
|
+
variant: target4.variantSlug,
|
|
46812
46843
|
variantNumber,
|
|
46813
46844
|
// Which arm this snapshot is. On a control win the variant lost; on a
|
|
46814
46845
|
// variant win what lost is the page's own previous content, which never
|
|
46815
46846
|
// had a number of its own — so it is filed under the test that rejected
|
|
46816
46847
|
// it, which is the only number that means anything about it.
|
|
46817
|
-
arm:
|
|
46848
|
+
arm: target4.survivor === "variant" ? "page-before-this-test" : "variant"
|
|
46818
46849
|
},
|
|
46819
46850
|
null,
|
|
46820
46851
|
2
|
|
46821
46852
|
)}
|
|
46822
46853
|
`;
|
|
46823
46854
|
}
|
|
46824
|
-
async function foldExperiment(root,
|
|
46825
|
-
const pageDir = path32.resolve(root, "src/pages",
|
|
46826
|
-
const variantDir = path32.resolve(root, "src/pages",
|
|
46827
|
-
const variantNumber = variantNumberOf(
|
|
46855
|
+
async function foldExperiment(root, target4) {
|
|
46856
|
+
const pageDir = path32.resolve(root, "src/pages", target4.landingSlug);
|
|
46857
|
+
const variantDir = path32.resolve(root, "src/pages", target4.variantSlug);
|
|
46858
|
+
const variantNumber = variantNumberOf(target4.variantSlug);
|
|
46828
46859
|
if (variantNumber === null) {
|
|
46829
46860
|
return {
|
|
46830
|
-
error: `\u201C${
|
|
46861
|
+
error: `\u201C${target4.variantSlug}\u201D is not a variant of \u201C${target4.landingSlug}\u201D, so there is nothing to fold`
|
|
46831
46862
|
};
|
|
46832
46863
|
}
|
|
46833
46864
|
if (!await isDir(pageDir)) {
|
|
46834
|
-
return { error: `There is no page at src/pages/${
|
|
46865
|
+
return { error: `There is no page at src/pages/${target4.landingSlug}/` };
|
|
46835
46866
|
}
|
|
46836
46867
|
if (!await isDir(variantDir)) {
|
|
46837
|
-
return { error: `src/pages/${
|
|
46868
|
+
return { error: `src/pages/${target4.variantSlug}/ is already gone \u2014 this test looks folded in already` };
|
|
46838
46869
|
}
|
|
46839
|
-
const archiveKey =
|
|
46870
|
+
const archiveKey = target4.landingInternalId ?? target4.landingSlug;
|
|
46840
46871
|
const archiveDir = path32.resolve(root, ARCHIVE_ROOT, archiveKey, String(variantNumber));
|
|
46841
46872
|
await rm8(archiveDir, { recursive: true, force: true });
|
|
46842
46873
|
await mkdir9(archiveDir, { recursive: true });
|
|
46843
46874
|
const written = await archiveArm({
|
|
46844
46875
|
root,
|
|
46845
|
-
armDir:
|
|
46876
|
+
armDir: target4.survivor === "variant" ? pageDir : variantDir,
|
|
46846
46877
|
pageDir,
|
|
46847
46878
|
archiveDir,
|
|
46848
|
-
armSlug:
|
|
46879
|
+
armSlug: target4.survivor === "variant" ? target4.landingSlug : target4.variantSlug
|
|
46849
46880
|
});
|
|
46850
|
-
await writeFile12(path32.join(archiveDir, "_variant.json"), archiveRecord(
|
|
46881
|
+
await writeFile12(path32.join(archiveDir, "_variant.json"), archiveRecord(target4, variantNumber), "utf8");
|
|
46851
46882
|
written.push(posix2(path32.join(ARCHIVE_ROOT, archiveKey, String(variantNumber), "_variant.json")));
|
|
46852
|
-
if (
|
|
46883
|
+
if (target4.survivor === "variant") {
|
|
46853
46884
|
written.push(...await promoteVariantFiles({ root, pageDir, variantDir }));
|
|
46854
46885
|
}
|
|
46855
46886
|
await rm8(variantDir, { recursive: true, force: true });
|
|
46856
46887
|
return {
|
|
46857
|
-
experimentId:
|
|
46858
|
-
page:
|
|
46888
|
+
experimentId: target4.experimentId,
|
|
46889
|
+
page: target4.landingSlug,
|
|
46859
46890
|
archivedTo: posix2(path32.join(ARCHIVE_ROOT, archiveKey, String(variantNumber))),
|
|
46860
46891
|
written,
|
|
46861
|
-
removed: [`src/pages/${
|
|
46892
|
+
removed: [`src/pages/${target4.variantSlug}`]
|
|
46862
46893
|
};
|
|
46863
46894
|
}
|
|
46864
46895
|
|
|
@@ -48362,9 +48393,9 @@ var foldCommand = defineCommand124({
|
|
|
48362
48393
|
}
|
|
48363
48394
|
const folded = [];
|
|
48364
48395
|
const failures = [];
|
|
48365
|
-
for (const
|
|
48366
|
-
const result = await foldExperiment(process.cwd(),
|
|
48367
|
-
if ("error" in result) failures.push(`${
|
|
48396
|
+
for (const target4 of owed) {
|
|
48397
|
+
const result = await foldExperiment(process.cwd(), target4);
|
|
48398
|
+
if ("error" in result) failures.push(`${target4.landingSlug}: ${result.error}`);
|
|
48368
48399
|
else folded.push(result);
|
|
48369
48400
|
}
|
|
48370
48401
|
if (folded.length > 0) {
|
|
@@ -48906,6 +48937,9 @@ var ADS_MATCHING = [
|
|
|
48906
48937
|
"country"
|
|
48907
48938
|
];
|
|
48908
48939
|
var MAPPING_SLOTS = {
|
|
48940
|
+
shopifyCustomer: [
|
|
48941
|
+
{ kind: "record", key: "customer", allowed: ["email", "phone", "acceptsEmailMarketing", "acceptsSmsMarketing"] }
|
|
48942
|
+
],
|
|
48909
48943
|
googleSpreadsheet: [{ kind: "list", key: "columnMapping", addressedBy: "column" }],
|
|
48910
48944
|
hubspotForm: [
|
|
48911
48945
|
{
|
|
@@ -49147,9 +49181,9 @@ function listPatch(shape, sideEffect, entries) {
|
|
|
49147
49181
|
function existingEntries(shape, sideEffect) {
|
|
49148
49182
|
const owner = shape.under ? sideEffect[shape.under] : sideEffect;
|
|
49149
49183
|
if (!owner || typeof owner !== "object") return [];
|
|
49150
|
-
const
|
|
49151
|
-
if (!Array.isArray(
|
|
49152
|
-
return
|
|
49184
|
+
const list3 = owner[shape.key];
|
|
49185
|
+
if (!Array.isArray(list3)) return [];
|
|
49186
|
+
return list3.filter((entry) => Boolean(entry) && typeof entry === "object");
|
|
49153
49187
|
}
|
|
49154
49188
|
function mergeEntries(shape, existing, incoming) {
|
|
49155
49189
|
const address = (entry) => {
|
|
@@ -49650,10 +49684,10 @@ function dropUnsetOptionals(sideEffect, path49) {
|
|
|
49650
49684
|
return [{ path: path49, change: `dropped \`${key}\` (an optional string is absent, never null)` }];
|
|
49651
49685
|
});
|
|
49652
49686
|
}
|
|
49653
|
-
function fillNulledArrays(
|
|
49687
|
+
function fillNulledArrays(target4, prefix, path49) {
|
|
49654
49688
|
return ARRAY_FIELDS.flatMap((key) => {
|
|
49655
|
-
if (!(key in
|
|
49656
|
-
|
|
49689
|
+
if (!(key in target4) || target4[key] !== null) return [];
|
|
49690
|
+
target4[key] = [];
|
|
49657
49691
|
return [{ path: path49, change: `\`${prefix}${key}: null\` \u2192 \`[]\`` }];
|
|
49658
49692
|
});
|
|
49659
49693
|
}
|
|
@@ -49679,9 +49713,9 @@ function sideEffectsOf(node) {
|
|
|
49679
49713
|
function normalizeSideEffect(sideEffect, where) {
|
|
49680
49714
|
const path49 = `${where} \u2192 ${String(sideEffect.id ?? "side effect")}`;
|
|
49681
49715
|
const arrays = ARRAY_OWNERS.flatMap((owner) => {
|
|
49682
|
-
const
|
|
49683
|
-
if (!
|
|
49684
|
-
return fillNulledArrays(
|
|
49716
|
+
const target4 = owner ? sideEffect[owner] : sideEffect;
|
|
49717
|
+
if (!target4 || typeof target4 !== "object") return [];
|
|
49718
|
+
return fillNulledArrays(target4, owner ? `${owner}.` : "", path49);
|
|
49685
49719
|
});
|
|
49686
49720
|
return [...dropUnsetOptionals(sideEffect, path49), ...arrays, ...tidyEmailRecipients(sideEffect, path49)];
|
|
49687
49721
|
}
|
|
@@ -50871,13 +50905,13 @@ async function resolvePropertyId(args5) {
|
|
|
50871
50905
|
if (properties.length === 0) {
|
|
50872
50906
|
handleConnectionError("ga4");
|
|
50873
50907
|
}
|
|
50874
|
-
const
|
|
50908
|
+
const list3 = properties.map((p) => ` ${p.externalId} ${p.name}`).join("\n");
|
|
50875
50909
|
writeJsonEnvelope({
|
|
50876
50910
|
ok: false,
|
|
50877
50911
|
error: {
|
|
50878
50912
|
code: "MULTIPLE_PROPERTIES",
|
|
50879
50913
|
message: `Multiple properties found. Pass --property-id or set BAKER_GA4_PROPERTY_ID:
|
|
50880
|
-
${
|
|
50914
|
+
${list3}`
|
|
50881
50915
|
}
|
|
50882
50916
|
});
|
|
50883
50917
|
process.exit(1);
|
|
@@ -50970,11 +51004,11 @@ function failValidation3(message) {
|
|
|
50970
51004
|
}
|
|
50971
51005
|
function requireTarget5(args5, entity) {
|
|
50972
51006
|
const positional = Array.isArray(args5._) ? args5._[0] : void 0;
|
|
50973
|
-
const
|
|
50974
|
-
if (typeof
|
|
51007
|
+
const target4 = args5.id ?? args5.target ?? positional;
|
|
51008
|
+
if (typeof target4 !== "string" || target4.length === 0) {
|
|
50975
51009
|
failValidation3(`pass the ${entity} id as the positional argument`);
|
|
50976
51010
|
}
|
|
50977
|
-
return
|
|
51011
|
+
return target4;
|
|
50978
51012
|
}
|
|
50979
51013
|
function readJsonSource(args5, flag3) {
|
|
50980
51014
|
const inline = args5[flag3];
|
|
@@ -51968,11 +52002,11 @@ Examples:
|
|
|
51968
52002
|
"property-id": propertyArg
|
|
51969
52003
|
},
|
|
51970
52004
|
run: async ({ args: args5 }) => {
|
|
51971
|
-
const
|
|
52005
|
+
const target4 = requireTarget5(args5, "audience id");
|
|
51972
52006
|
const payload = loadJsonArg(args5);
|
|
51973
52007
|
await stageOp3({
|
|
51974
52008
|
kind: "ga4.audience.update",
|
|
51975
|
-
target:
|
|
52009
|
+
target: target4,
|
|
51976
52010
|
payload,
|
|
51977
52011
|
...withProperty(args5)
|
|
51978
52012
|
});
|
|
@@ -52101,13 +52135,13 @@ Examples:
|
|
|
52101
52135
|
"property-id": propertyArg
|
|
52102
52136
|
},
|
|
52103
52137
|
run: async ({ args: args5 }) => {
|
|
52104
|
-
const
|
|
52138
|
+
const target4 = requireTarget5(args5, "Google Ads link id");
|
|
52105
52139
|
if (typeof args5["ads-personalization"] !== "string") {
|
|
52106
52140
|
failValidation3("pass --ads-personalization true|false \u2014 it is the only thing a link update can change");
|
|
52107
52141
|
}
|
|
52108
52142
|
await stageOp3({
|
|
52109
52143
|
kind: "ga4.googleAdsLink.update",
|
|
52110
|
-
target:
|
|
52144
|
+
target: target4,
|
|
52111
52145
|
payload: { adsPersonalizationEnabled: booleanArg(args5["ads-personalization"], "ads-personalization") },
|
|
52112
52146
|
...withProperty(args5)
|
|
52113
52147
|
});
|
|
@@ -52342,13 +52376,13 @@ async function resolveSiteUrl(args5) {
|
|
|
52342
52376
|
if (sites.length === 0) {
|
|
52343
52377
|
handleConnectionError("gsc");
|
|
52344
52378
|
}
|
|
52345
|
-
const
|
|
52379
|
+
const list3 = sites.map((s) => ` ${s.siteUrl} (${s.permissionLevel})`).join("\n");
|
|
52346
52380
|
writeJsonEnvelope({
|
|
52347
52381
|
ok: false,
|
|
52348
52382
|
error: {
|
|
52349
52383
|
code: "MULTIPLE_SITES",
|
|
52350
52384
|
message: `Multiple sites found. Pass --site-url or set BAKER_GSC_SITE_URL:
|
|
52351
|
-
${
|
|
52385
|
+
${list3}`
|
|
52352
52386
|
}
|
|
52353
52387
|
});
|
|
52354
52388
|
process.exit(1);
|
|
@@ -53338,7 +53372,7 @@ function propertiesCommand2(object) {
|
|
|
53338
53372
|
object
|
|
53339
53373
|
});
|
|
53340
53374
|
const { properties } = response.data;
|
|
53341
|
-
const operators = Object.entries(HUBSPOT_OPERATORS).map(([kind,
|
|
53375
|
+
const operators = Object.entries(HUBSPOT_OPERATORS).map(([kind, list3]) => `${kind}: ${list3.map((operator) => operator.value).join(", ")}`).join(" \xB7 ");
|
|
53342
53376
|
const objectFlag = object === "contact" ? "" : ` --object ${object}`;
|
|
53343
53377
|
const hints2 = properties.length === 0 ? [`This account has no visible ${object} property, so there is nothing an event could watch.`] : [
|
|
53344
53378
|
`Pick the property and the condition that mean the outcome, then \`baker analytics sources --create-hubspot${objectFlag} --property <name> [--operator <op>] --value <value>\u2026\` (\`--value\` repeats for a list). Operators by kind \u2014 ${operators}. Counting starts from creation: a ${object} already satisfying it is not counted until it leaves and comes back.`
|
|
@@ -53922,17 +53956,17 @@ var dimensionsCommand = defineCommand147({
|
|
|
53922
53956
|
},
|
|
53923
53957
|
run: async ({ args: args5 }) => {
|
|
53924
53958
|
try {
|
|
53925
|
-
const
|
|
53926
|
-
if (!
|
|
53959
|
+
const target4 = args5.target;
|
|
53960
|
+
if (!target4) {
|
|
53927
53961
|
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message: "target argument is required" } });
|
|
53928
53962
|
process.exit(1);
|
|
53929
53963
|
}
|
|
53930
|
-
const buffer = await readImageBuffer(
|
|
53964
|
+
const buffer = await readImageBuffer(target4);
|
|
53931
53965
|
const dims = getDimensionsFromBuffer(buffer);
|
|
53932
53966
|
if (!dims) {
|
|
53933
53967
|
writeJson({
|
|
53934
53968
|
ok: false,
|
|
53935
|
-
error: { code: "IMAGE_PROCESSING_ERROR", message: `Could not extract dimensions from ${
|
|
53969
|
+
error: { code: "IMAGE_PROCESSING_ERROR", message: `Could not extract dimensions from ${target4}` }
|
|
53936
53970
|
});
|
|
53937
53971
|
process.exit(1);
|
|
53938
53972
|
}
|
|
@@ -53940,8 +53974,8 @@ var dimensionsCommand = defineCommand147({
|
|
|
53940
53974
|
{
|
|
53941
53975
|
ok: true,
|
|
53942
53976
|
data: {
|
|
53943
|
-
target:
|
|
53944
|
-
source: isRemoteUrl(
|
|
53977
|
+
target: target4,
|
|
53978
|
+
source: isRemoteUrl(target4) ? "url" : "file",
|
|
53945
53979
|
width: dims.width,
|
|
53946
53980
|
height: dims.height,
|
|
53947
53981
|
aspectRatio: Math.round(dims.aspectRatio * 1e4) / 1e4,
|
|
@@ -54070,15 +54104,15 @@ registerSchema({
|
|
|
54070
54104
|
var ValidationError3 = class extends Error {
|
|
54071
54105
|
};
|
|
54072
54106
|
var UnusableTargetError = class extends Error {
|
|
54073
|
-
constructor(
|
|
54074
|
-
super(`"${
|
|
54075
|
-
this.target =
|
|
54107
|
+
constructor(target4) {
|
|
54108
|
+
super(`"${target4}" is neither an image URL nor a library image id.`);
|
|
54109
|
+
this.target = target4;
|
|
54076
54110
|
}
|
|
54077
54111
|
};
|
|
54078
54112
|
function readTargets(args5) {
|
|
54079
|
-
const
|
|
54113
|
+
const positionals2 = Array.isArray(args5._) ? args5._ : [];
|
|
54080
54114
|
const named = typeof args5.targets === "string" ? [args5.targets] : [];
|
|
54081
|
-
const all =
|
|
54115
|
+
const all = positionals2.length > 0 ? positionals2 : named;
|
|
54082
54116
|
const seen = /* @__PURE__ */ new Set();
|
|
54083
54117
|
const out = [];
|
|
54084
54118
|
for (const value of all) {
|
|
@@ -54103,13 +54137,13 @@ function classify(raw) {
|
|
|
54103
54137
|
}
|
|
54104
54138
|
return { raw, kind: "imageId" };
|
|
54105
54139
|
}
|
|
54106
|
-
async function resolveTarget(
|
|
54107
|
-
if (
|
|
54108
|
-
return { input:
|
|
54140
|
+
async function resolveTarget(target4) {
|
|
54141
|
+
if (target4.kind === "url") {
|
|
54142
|
+
return { input: target4.raw, url: target4.raw, baseName: baseNameFromUrl(target4.raw) };
|
|
54109
54143
|
}
|
|
54110
|
-
const doc = await apiGet("/api/images/get", { id:
|
|
54111
|
-
const stem = sanitizeBaseName(doc.name.length > 0 ? doc.name :
|
|
54112
|
-
return { input:
|
|
54144
|
+
const doc = await apiGet("/api/images/get", { id: target4.raw });
|
|
54145
|
+
const stem = sanitizeBaseName(doc.name.length > 0 ? doc.name : target4.raw);
|
|
54146
|
+
return { input: target4.raw, url: doc.imageUrl, baseName: stem };
|
|
54113
54147
|
}
|
|
54114
54148
|
async function fetchBytes(url) {
|
|
54115
54149
|
return await fetchExternalBytes(url, { maxBytes: MAX_REMOTE_IMAGE_BYTES });
|
|
@@ -54145,9 +54179,9 @@ function failureMessage(err, fallback) {
|
|
|
54145
54179
|
async function runDownloads(plan) {
|
|
54146
54180
|
const failed = [];
|
|
54147
54181
|
const fetched = [];
|
|
54148
|
-
for (const
|
|
54182
|
+
for (const target4 of plan.targets) {
|
|
54149
54183
|
try {
|
|
54150
|
-
const resolved = await resolveTarget(
|
|
54184
|
+
const resolved = await resolveTarget(target4);
|
|
54151
54185
|
const { buffer, contentType } = await fetchBytes(resolved.url);
|
|
54152
54186
|
fetched.push({
|
|
54153
54187
|
input: resolved.input,
|
|
@@ -54161,7 +54195,7 @@ async function runDownloads(plan) {
|
|
|
54161
54195
|
})
|
|
54162
54196
|
});
|
|
54163
54197
|
} catch (err) {
|
|
54164
|
-
failed.push({ input:
|
|
54198
|
+
failed.push({ input: target4.raw, error: failureMessage(err, "Download failed") });
|
|
54165
54199
|
}
|
|
54166
54200
|
}
|
|
54167
54201
|
const paths = disambiguate(fetched.map((item) => item.path));
|
|
@@ -54382,7 +54416,7 @@ function providerHitNormalizer(record, full) {
|
|
|
54382
54416
|
if (!full) {
|
|
54383
54417
|
return compact3;
|
|
54384
54418
|
}
|
|
54385
|
-
const
|
|
54419
|
+
const group2 = asRecord2(record.group);
|
|
54386
54420
|
return {
|
|
54387
54421
|
...compact3,
|
|
54388
54422
|
externalUrl: str(record.externalUrl),
|
|
@@ -54394,7 +54428,7 @@ function providerHitNormalizer(record, full) {
|
|
|
54394
54428
|
score: num(record.score),
|
|
54395
54429
|
deduped: typeof record.deduped === "boolean" ? record.deduped : void 0,
|
|
54396
54430
|
alsoInLibrary: str(record.alsoInLibrary),
|
|
54397
|
-
groupKey: str(
|
|
54431
|
+
groupKey: str(group2.key)
|
|
54398
54432
|
};
|
|
54399
54433
|
}
|
|
54400
54434
|
function readOutputFlags(args5) {
|
|
@@ -55732,18 +55766,18 @@ function findColorRangeInternal(data, ignoredColor = null, customThreshold) {
|
|
|
55732
55766
|
const color = { r: data[i], g: data[i + 1], b: data[i + 2] };
|
|
55733
55767
|
if (ignoredColor && colorDistance(color, ignoredColor) < threshold) continue;
|
|
55734
55768
|
let found = false;
|
|
55735
|
-
for (const
|
|
55736
|
-
const distance = colorDistance(
|
|
55737
|
-
const contrastRatio = getContrastRatio(
|
|
55769
|
+
for (const group2 of groups) {
|
|
55770
|
+
const distance = colorDistance(group2.color, color);
|
|
55771
|
+
const contrastRatio = getContrastRatio(group2.color, color);
|
|
55738
55772
|
let groupingThreshold = threshold;
|
|
55739
55773
|
if (contrastRatio > 3) groupingThreshold = Math.min(groupingThreshold, 8);
|
|
55740
|
-
const hueDistance = getHueDistance(
|
|
55774
|
+
const hueDistance = getHueDistance(group2.color, color);
|
|
55741
55775
|
if (hueDistance > 20) groupingThreshold = Math.min(groupingThreshold, 5);
|
|
55742
|
-
if (isHighlySaturated(color) && isHighlySaturated(
|
|
55776
|
+
if (isHighlySaturated(color) && isHighlySaturated(group2.color)) {
|
|
55743
55777
|
groupingThreshold = Math.min(groupingThreshold, 10);
|
|
55744
55778
|
}
|
|
55745
55779
|
if (distance < groupingThreshold) {
|
|
55746
|
-
|
|
55780
|
+
group2.count++;
|
|
55747
55781
|
found = true;
|
|
55748
55782
|
break;
|
|
55749
55783
|
}
|
|
@@ -55848,13 +55882,13 @@ function hasGradient(data) {
|
|
|
55848
55882
|
function perceivedLuminance(color) {
|
|
55849
55883
|
return (0.299 * color.r + 0.587 * color.g + 0.114 * color.b) / 255;
|
|
55850
55884
|
}
|
|
55851
|
-
function createSvgRecolorer(
|
|
55852
|
-
if (palette.length < 2) return () =>
|
|
55885
|
+
function createSvgRecolorer(target4, palette) {
|
|
55886
|
+
if (palette.length < 2) return () => target4;
|
|
55853
55887
|
const luminances = palette.map(perceivedLuminance);
|
|
55854
55888
|
const min = Math.min(...luminances);
|
|
55855
55889
|
const max = Math.max(...luminances);
|
|
55856
|
-
if (max - min < 0.05) return () =>
|
|
55857
|
-
const [h, s, targetLightness] = rgbToHsl(
|
|
55890
|
+
if (max - min < 0.05) return () => target4;
|
|
55891
|
+
const [h, s, targetLightness] = rgbToHsl(target4.r, target4.g, target4.b);
|
|
55858
55892
|
const lightnessRange = 0.75;
|
|
55859
55893
|
const minLightness = targetLightness >= 0.5 ? Math.max(0, targetLightness - lightnessRange) : targetLightness;
|
|
55860
55894
|
const maxLightness = targetLightness >= 0.5 ? targetLightness : Math.min(1, targetLightness + lightnessRange);
|
|
@@ -56848,18 +56882,18 @@ var uploadCommand = defineCommand167({
|
|
|
56848
56882
|
},
|
|
56849
56883
|
run: async ({ args: args5 }) => {
|
|
56850
56884
|
try {
|
|
56851
|
-
const
|
|
56852
|
-
if (!
|
|
56885
|
+
const target4 = args5.file;
|
|
56886
|
+
if (!target4) {
|
|
56853
56887
|
writeJson({
|
|
56854
56888
|
ok: false,
|
|
56855
56889
|
error: { code: "VALIDATION_ERROR", message: "File path or URL is required" }
|
|
56856
56890
|
});
|
|
56857
56891
|
process.exit(1);
|
|
56858
56892
|
}
|
|
56859
|
-
if (isRemoteUrl2(
|
|
56860
|
-
await uploadRemote(
|
|
56893
|
+
if (isRemoteUrl2(target4)) {
|
|
56894
|
+
await uploadRemote(target4, args5);
|
|
56861
56895
|
} else {
|
|
56862
|
-
await uploadLocal(
|
|
56896
|
+
await uploadLocal(target4, args5);
|
|
56863
56897
|
}
|
|
56864
56898
|
} catch (err) {
|
|
56865
56899
|
if (err instanceof ApiError) {
|
|
@@ -56871,7 +56905,7 @@ var uploadCommand = defineCommand167({
|
|
|
56871
56905
|
}
|
|
56872
56906
|
}
|
|
56873
56907
|
});
|
|
56874
|
-
async function uploadRemote(
|
|
56908
|
+
async function uploadRemote(target4, args5) {
|
|
56875
56909
|
const source = args5.source ?? "uploaded";
|
|
56876
56910
|
if (args5["dry-run"]) {
|
|
56877
56911
|
writeJson({
|
|
@@ -56879,7 +56913,7 @@ async function uploadRemote(target2, args5) {
|
|
|
56879
56913
|
dryRun: true,
|
|
56880
56914
|
operation: "images.upload (remote)",
|
|
56881
56915
|
params: {
|
|
56882
|
-
url:
|
|
56916
|
+
url: target4,
|
|
56883
56917
|
source,
|
|
56884
56918
|
externalId: args5["external-id"] ?? null,
|
|
56885
56919
|
externalUrl: args5["external-url"] ?? null,
|
|
@@ -56888,22 +56922,22 @@ async function uploadRemote(target2, args5) {
|
|
|
56888
56922
|
});
|
|
56889
56923
|
return;
|
|
56890
56924
|
}
|
|
56891
|
-
const body = { url:
|
|
56925
|
+
const body = { url: target4, source };
|
|
56892
56926
|
if (args5["external-id"]) body.externalId = args5["external-id"];
|
|
56893
56927
|
if (args5["external-url"]) body.externalUrl = args5["external-url"];
|
|
56894
56928
|
if (args5.context) body.descriptionContext = args5.context;
|
|
56895
56929
|
const data = await apiPost("/api/images/ingest", body);
|
|
56896
56930
|
writeJson({ ok: true, data });
|
|
56897
56931
|
}
|
|
56898
|
-
async function uploadLocal(
|
|
56899
|
-
const contentType = args5["content-type"] || detectImageContentType(
|
|
56932
|
+
async function uploadLocal(target4, args5) {
|
|
56933
|
+
const contentType = args5["content-type"] || detectImageContentType(target4);
|
|
56900
56934
|
if (args5["dry-run"]) {
|
|
56901
56935
|
writeJson({
|
|
56902
56936
|
ok: true,
|
|
56903
56937
|
dryRun: true,
|
|
56904
56938
|
operation: "images.upload (local)",
|
|
56905
56939
|
params: {
|
|
56906
|
-
file:
|
|
56940
|
+
file: target4,
|
|
56907
56941
|
contentType,
|
|
56908
56942
|
source: args5.source ?? null,
|
|
56909
56943
|
descriptionContext: args5.context ?? null
|
|
@@ -56912,7 +56946,7 @@ async function uploadLocal(target2, args5) {
|
|
|
56912
56946
|
return;
|
|
56913
56947
|
}
|
|
56914
56948
|
const data = await uploadLocalImage({
|
|
56915
|
-
file:
|
|
56949
|
+
file: target4,
|
|
56916
56950
|
contentType,
|
|
56917
56951
|
source: args5.source,
|
|
56918
56952
|
descriptionContext: args5.context
|
|
@@ -59201,7 +59235,7 @@ function classifyPreflightStatus(status) {
|
|
|
59201
59235
|
async function preflightProxyRoute(route, options = {}) {
|
|
59202
59236
|
if (route.kind !== "proxy") return { usable: true };
|
|
59203
59237
|
const timeoutMs = options.timeoutMs ?? 8e3;
|
|
59204
|
-
const
|
|
59238
|
+
const target4 = options.target ?? "example.com:443";
|
|
59205
59239
|
const endpoint = new URL(route.server);
|
|
59206
59240
|
const auth = Buffer.from(`${route.username}:${route.password}`).toString("base64");
|
|
59207
59241
|
return await new Promise((resolve5) => {
|
|
@@ -59213,8 +59247,8 @@ async function preflightProxyRoute(route, options = {}) {
|
|
|
59213
59247
|
host: endpoint.hostname,
|
|
59214
59248
|
port: Number(endpoint.port),
|
|
59215
59249
|
method: "CONNECT",
|
|
59216
|
-
path:
|
|
59217
|
-
headers: { "Proxy-Authorization": `Basic ${auth}`, Host:
|
|
59250
|
+
path: target4,
|
|
59251
|
+
headers: { "Proxy-Authorization": `Basic ${auth}`, Host: target4 },
|
|
59218
59252
|
timeout: timeoutMs
|
|
59219
59253
|
});
|
|
59220
59254
|
request.on("connect", (response, socket) => {
|
|
@@ -60140,9 +60174,9 @@ function bandsCollide(a, b, margin, maxGap) {
|
|
|
60140
60174
|
const gap = Math.min(Math.abs(shrunkA.bottom - shrunkB.top), Math.abs(shrunkB.bottom - shrunkA.top));
|
|
60141
60175
|
return gap <= maxGap;
|
|
60142
60176
|
}
|
|
60143
|
-
function shouldPromoteToParent(
|
|
60144
|
-
const isRunt =
|
|
60145
|
-
return isRunt &&
|
|
60177
|
+
function shouldPromoteToParent(group2) {
|
|
60178
|
+
const isRunt = group2.childCount < 3 || group2.height < 80;
|
|
60179
|
+
return isRunt && group2.parentHeight < 1600;
|
|
60146
60180
|
}
|
|
60147
60181
|
|
|
60148
60182
|
// src/engine/landing-library/segment.ts
|
|
@@ -60361,35 +60395,35 @@ var inPageSegment = (options) => {
|
|
|
60361
60395
|
};
|
|
60362
60396
|
const clusterOnce = (groups2) => {
|
|
60363
60397
|
const buckets = [];
|
|
60364
|
-
for (const
|
|
60365
|
-
if (
|
|
60366
|
-
buckets.push([
|
|
60398
|
+
for (const group2 of groups2) {
|
|
60399
|
+
if (group2.sealed) {
|
|
60400
|
+
buckets.push([group2]);
|
|
60367
60401
|
continue;
|
|
60368
60402
|
}
|
|
60369
|
-
const
|
|
60403
|
+
const target4 = buckets.find(
|
|
60370
60404
|
(bucket) => bucket.some(
|
|
60371
60405
|
(member) => !member.sealed && geom.bandsCollide(
|
|
60372
60406
|
{ top: member.top, bottom: member.bottom },
|
|
60373
|
-
{ top:
|
|
60407
|
+
{ top: group2.top, bottom: group2.bottom },
|
|
60374
60408
|
options.collisionMargin,
|
|
60375
60409
|
gapThreshold
|
|
60376
60410
|
)
|
|
60377
60411
|
)
|
|
60378
60412
|
);
|
|
60379
|
-
if (
|
|
60380
|
-
else buckets.push([
|
|
60413
|
+
if (target4) target4.push(group2);
|
|
60414
|
+
else buckets.push([group2]);
|
|
60381
60415
|
}
|
|
60382
60416
|
if (buckets.length === groups2.length) return groups2;
|
|
60383
60417
|
return buckets.flatMap((bucket) => bucket.length === 1 ? [bucket[0]] : mergeBucket(bucket));
|
|
60384
60418
|
};
|
|
60385
|
-
let groups = leaves.map((
|
|
60386
|
-
root:
|
|
60387
|
-
top:
|
|
60388
|
-
bottom:
|
|
60389
|
-
left:
|
|
60390
|
-
right:
|
|
60391
|
-
height:
|
|
60392
|
-
leaves: [
|
|
60419
|
+
let groups = leaves.map((leaf2) => ({
|
|
60420
|
+
root: leaf2.element,
|
|
60421
|
+
top: leaf2.top,
|
|
60422
|
+
bottom: leaf2.bottom,
|
|
60423
|
+
left: leaf2.left,
|
|
60424
|
+
right: leaf2.right,
|
|
60425
|
+
height: leaf2.height,
|
|
60426
|
+
leaves: [leaf2],
|
|
60393
60427
|
sealed: false
|
|
60394
60428
|
}));
|
|
60395
60429
|
for (let pass = 0; pass < 40; pass++) {
|
|
@@ -60399,21 +60433,21 @@ var inPageSegment = (options) => {
|
|
|
60399
60433
|
}
|
|
60400
60434
|
for (let pass = 0; pass < 10; pass++) {
|
|
60401
60435
|
let promoted = false;
|
|
60402
|
-
groups = groups.map((
|
|
60403
|
-
const parent =
|
|
60404
|
-
if (!parent || parent === document.documentElement || parent === document.body) return
|
|
60436
|
+
groups = groups.map((group2) => {
|
|
60437
|
+
const parent = group2.root.parentElement;
|
|
60438
|
+
if (!parent || parent === document.documentElement || parent === document.body) return group2;
|
|
60405
60439
|
const parentBox = boxOf(parent);
|
|
60406
|
-
if (!parentBox) return
|
|
60440
|
+
if (!parentBox) return group2;
|
|
60407
60441
|
if (!geom.shouldPromoteToParent({
|
|
60408
|
-
childCount:
|
|
60409
|
-
height:
|
|
60442
|
+
childCount: group2.leaves.length,
|
|
60443
|
+
height: group2.height,
|
|
60410
60444
|
parentHeight: parentBox.height
|
|
60411
60445
|
})) {
|
|
60412
|
-
return
|
|
60446
|
+
return group2;
|
|
60413
60447
|
}
|
|
60414
60448
|
promoted = true;
|
|
60415
60449
|
return {
|
|
60416
|
-
...
|
|
60450
|
+
...group2,
|
|
60417
60451
|
root: parent,
|
|
60418
60452
|
top: parentBox.top,
|
|
60419
60453
|
bottom: parentBox.bottom,
|
|
@@ -60429,23 +60463,23 @@ var inPageSegment = (options) => {
|
|
|
60429
60463
|
const containsBox = (outer, inner) => outer.top - 4 <= inner.top && outer.bottom + 4 >= inner.bottom && outer.left - 4 <= inner.left && outer.right + 4 >= inner.right;
|
|
60430
60464
|
const deduped = [];
|
|
60431
60465
|
const area = (g) => (g.right - g.left) * g.height;
|
|
60432
|
-
for (const
|
|
60466
|
+
for (const group2 of groups.slice().sort((a, b) => area(b) - area(a) || b.leaves.length - a.leaves.length)) {
|
|
60433
60467
|
const duplicate = deduped.some(
|
|
60434
|
-
(kept) => kept.root ===
|
|
60468
|
+
(kept) => kept.root === group2.root || kept.root.contains(group2.root) || sameBox(kept, group2) || containsBox(kept, group2)
|
|
60435
60469
|
);
|
|
60436
|
-
if (!duplicate) deduped.push(
|
|
60470
|
+
if (!duplicate) deduped.push(group2);
|
|
60437
60471
|
}
|
|
60438
|
-
return deduped.filter((
|
|
60472
|
+
return deduped.filter((group2) => group2.height > 0 && group2.right - group2.left > 0).sort((a, b) => a.top - b.top).map((group2, index) => ({
|
|
60439
60473
|
index,
|
|
60440
|
-
selector: selectorFor(
|
|
60474
|
+
selector: selectorFor(group2.root),
|
|
60441
60475
|
rect: {
|
|
60442
|
-
top:
|
|
60443
|
-
left:
|
|
60444
|
-
width:
|
|
60445
|
-
height:
|
|
60476
|
+
top: group2.top,
|
|
60477
|
+
left: group2.left,
|
|
60478
|
+
width: group2.right - group2.left,
|
|
60479
|
+
height: group2.height
|
|
60446
60480
|
},
|
|
60447
|
-
leafCount:
|
|
60448
|
-
textPreview: visibleText(
|
|
60481
|
+
leafCount: group2.leaves.length,
|
|
60482
|
+
textPreview: visibleText(group2.root).slice(0, 200)
|
|
60449
60483
|
}));
|
|
60450
60484
|
};
|
|
60451
60485
|
|
|
@@ -60472,8 +60506,8 @@ async function captureMotionTake(browser, url, selector, timeoutMs = 45e3) {
|
|
|
60472
60506
|
await settleNavigation(page);
|
|
60473
60507
|
await page.waitForLoadState("networkidle", { timeout: 8e3 }).catch(() => void 0);
|
|
60474
60508
|
await settleConsent(page);
|
|
60475
|
-
const
|
|
60476
|
-
if (!await
|
|
60509
|
+
const target4 = page.locator(selector).first();
|
|
60510
|
+
if (!await target4.count().catch(() => 0)) return null;
|
|
60477
60511
|
await page.evaluate((sectionSelector) => {
|
|
60478
60512
|
const element = document.querySelector(sectionSelector);
|
|
60479
60513
|
if (!element) return;
|
|
@@ -60527,12 +60561,12 @@ async function aimAtSection(page, selector) {
|
|
|
60527
60561
|
}
|
|
60528
60562
|
async function hideFloatingFurniture(page, selector) {
|
|
60529
60563
|
await page.evaluate((sectionSelector) => {
|
|
60530
|
-
const
|
|
60531
|
-
if (!
|
|
60564
|
+
const target4 = document.querySelector(sectionSelector);
|
|
60565
|
+
if (!target4) return;
|
|
60532
60566
|
for (const element of Array.from(document.querySelectorAll("*"))) {
|
|
60533
60567
|
const position = getComputedStyle(element).position;
|
|
60534
60568
|
if (position !== "fixed" && position !== "sticky") continue;
|
|
60535
|
-
if (element.contains(
|
|
60569
|
+
if (element.contains(target4) || target4.contains(element) || element === target4) continue;
|
|
60536
60570
|
element.style.setProperty("visibility", "hidden", "important");
|
|
60537
60571
|
}
|
|
60538
60572
|
}, selector);
|
|
@@ -60589,9 +60623,9 @@ async function renderBundleToPng(browser, html, viewportWidth, options = {}) {
|
|
|
60589
60623
|
await page.waitForTimeout(300);
|
|
60590
60624
|
if (!wholePage) {
|
|
60591
60625
|
for (const selector of [`[${SECTION_ROOT_ATTRIBUTE}]`, "body > *"]) {
|
|
60592
|
-
const
|
|
60593
|
-
if (!await
|
|
60594
|
-
const shot = await
|
|
60626
|
+
const target4 = page.locator(selector).first();
|
|
60627
|
+
if (!await target4.count()) continue;
|
|
60628
|
+
const shot = await target4.screenshot({ type: "png", timeout: timeoutMs }).catch(() => null);
|
|
60595
60629
|
if (shot) return shot;
|
|
60596
60630
|
}
|
|
60597
60631
|
}
|
|
@@ -61067,8 +61101,8 @@ async function scrapeLanding(options) {
|
|
|
61067
61101
|
import { existsSync as existsSync9 } from "fs";
|
|
61068
61102
|
import path41 from "path";
|
|
61069
61103
|
var SCRATCH_DIR = ".baker";
|
|
61070
|
-
function isWithin(parent,
|
|
61071
|
-
const relative = path41.relative(parent,
|
|
61104
|
+
function isWithin(parent, target4) {
|
|
61105
|
+
const relative = path41.relative(parent, target4);
|
|
61072
61106
|
return relative === "" || !relative.startsWith("..") && !path41.isAbsolute(relative);
|
|
61073
61107
|
}
|
|
61074
61108
|
function findRepoRoot(from) {
|
|
@@ -61359,8 +61393,8 @@ registerSchema({
|
|
|
61359
61393
|
function buildSearchBody(args5) {
|
|
61360
61394
|
const body = {};
|
|
61361
61395
|
const setList2 = (key, value) => {
|
|
61362
|
-
const
|
|
61363
|
-
if (
|
|
61396
|
+
const list3 = splitList2(value);
|
|
61397
|
+
if (list3) Object.assign(body, { [key]: list3 });
|
|
61364
61398
|
};
|
|
61365
61399
|
if (args5.query) body.query = String(args5.query);
|
|
61366
61400
|
if (args5["similar-to"]) body.similarToSectionId = String(args5["similar-to"]);
|
|
@@ -62135,9 +62169,9 @@ function customEntry(server) {
|
|
|
62135
62169
|
var BROKEN_SERVER_HINT = "A server marked usable=false will NOT answer: its tools are absent from this turn. Say plainly that the tool is not working and relay `problem`; never claim its tools are loading, coming next message, or temporarily unavailable. `problem` names what a person has to fix \u2014 the address, the sign-in, or the key the server was added with \u2014 so pass on that fix and no other: never tell the user to reconnect a server whose `problem` is its address or its key. The user makes the fix in the dashboard (Brain \u2192 Integrations \u2192 MCPs). If `suggestedAddress` is present, that is the address to use.";
|
|
62136
62170
|
var LAST_CHECK_FAILED_HINT = "A server with lastCheck=failed was unreachable or erroring when it was last checked \u2014 that may already have passed, and its tools ARE loaded this turn. Try the tool; if it works, say nothing about the check. If it fails, report `problem` as what happened rather than guessing.";
|
|
62137
62171
|
function parseHeaders(raw, rawArgs) {
|
|
62138
|
-
const
|
|
62172
|
+
const list3 = repeatedValues(rawArgs, "header", raw);
|
|
62139
62173
|
const headers = {};
|
|
62140
|
-
for (const entry of
|
|
62174
|
+
for (const entry of list3) {
|
|
62141
62175
|
const text2 = String(entry);
|
|
62142
62176
|
const sep = text2.indexOf(":") >= 0 && (text2.indexOf(":") < text2.indexOf("=") || text2.indexOf("=") < 0) ? ":" : "=";
|
|
62143
62177
|
const at = text2.indexOf(sep);
|
|
@@ -63306,7 +63340,7 @@ Examples:
|
|
|
63306
63340
|
"no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false }
|
|
63307
63341
|
},
|
|
63308
63342
|
run: async ({ args: args5 }) => {
|
|
63309
|
-
const
|
|
63343
|
+
const target4 = args5.target;
|
|
63310
63344
|
const location2 = args5.location || void 0;
|
|
63311
63345
|
const language = args5.language || void 0;
|
|
63312
63346
|
const sort = args5.sort || void 0;
|
|
@@ -63316,7 +63350,7 @@ Examples:
|
|
|
63316
63350
|
const queryContext = buildResearchQueryContext(location2, language);
|
|
63317
63351
|
try {
|
|
63318
63352
|
const data = await apiPost("/api/research/keywords-for-site", {
|
|
63319
|
-
target:
|
|
63353
|
+
target: target4,
|
|
63320
63354
|
location: location2,
|
|
63321
63355
|
language,
|
|
63322
63356
|
sort,
|
|
@@ -63489,7 +63523,7 @@ Examples:
|
|
|
63489
63523
|
"no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false }
|
|
63490
63524
|
},
|
|
63491
63525
|
run: async ({ args: args5 }) => {
|
|
63492
|
-
const
|
|
63526
|
+
const target4 = args5.target;
|
|
63493
63527
|
const location2 = args5.location || void 0;
|
|
63494
63528
|
const language = args5.language || void 0;
|
|
63495
63529
|
const limit2 = args5.limit ? Number(args5.limit) : void 0;
|
|
@@ -63497,7 +63531,7 @@ Examples:
|
|
|
63497
63531
|
const queryContext = buildResearchQueryContext(location2, language);
|
|
63498
63532
|
try {
|
|
63499
63533
|
const data = await apiPost("/api/research/relevant-pages", {
|
|
63500
|
-
target:
|
|
63534
|
+
target: target4,
|
|
63501
63535
|
location: location2,
|
|
63502
63536
|
language,
|
|
63503
63537
|
limit: limit2,
|
|
@@ -63844,7 +63878,7 @@ function refuseMisusedKinds(kind, args5) {
|
|
|
63844
63878
|
}
|
|
63845
63879
|
}
|
|
63846
63880
|
function refuseStrayFlags(kind, args5) {
|
|
63847
|
-
const allowed = new Set(TRIGGER_TARGETS[kind].map((
|
|
63881
|
+
const allowed = new Set(TRIGGER_TARGETS[kind].map((target4) => TRIGGER_TARGET_FIELDS[target4.field].flag));
|
|
63848
63882
|
const stray = TRIGGER_TARGET_FIELD_IDS.map((field) => TRIGGER_TARGET_FIELDS[field].flag).filter(
|
|
63849
63883
|
(flag3) => !allowed.has(flag3) && flagText(args5, flag3) !== void 0 && !(kind === "experiment.started" && flag3 === "experiment")
|
|
63850
63884
|
);
|
|
@@ -64018,8 +64052,16 @@ registerSchema({
|
|
|
64018
64052
|
command: "scheduled-agents.create",
|
|
64019
64053
|
description: "Stage a scheduled agent: a trigger and a prompt. Applies when the chat is published and returns a temp_agent_* ID. Start here: `baker scheduled-agents triggers` lists everything that can start one.",
|
|
64020
64054
|
args: {
|
|
64021
|
-
name: {
|
|
64022
|
-
|
|
64055
|
+
name: {
|
|
64056
|
+
type: "string",
|
|
64057
|
+
description: "What a person will call it in the list. With --template, leave it out to use the recipe's name.",
|
|
64058
|
+
required: false
|
|
64059
|
+
},
|
|
64060
|
+
description: {
|
|
64061
|
+
type: "string",
|
|
64062
|
+
description: "One line under the name. Not read by the run. With --template, leave it out: the recipe's own line is used, and it says what waits for the user's review.",
|
|
64063
|
+
required: false
|
|
64064
|
+
},
|
|
64023
64065
|
prompt: {
|
|
64024
64066
|
type: "string",
|
|
64025
64067
|
description: "What the run does, in full. This IS the job \u2014 nothing else is resolved when it fires. Write it as you would write a message to an agent.",
|
|
@@ -64072,14 +64114,14 @@ var createCommand3 = defineCommand197({
|
|
|
64072
64114
|
try {
|
|
64073
64115
|
refuseAutoPublish(args5);
|
|
64074
64116
|
const name = args5.name;
|
|
64075
|
-
const
|
|
64117
|
+
const description2 = args5.description;
|
|
64076
64118
|
const prompt = args5.prompt;
|
|
64077
64119
|
const template = typeof args5.template === "string" ? args5.template.trim() : "";
|
|
64078
|
-
if (!name || name.trim().length === 0) {
|
|
64079
|
-
failValidation4("--name is required.");
|
|
64120
|
+
if (template.length === 0 && (!name || name.trim().length === 0)) {
|
|
64121
|
+
failValidation4("--name is required. Or pass --template to use a recipe's name.");
|
|
64080
64122
|
}
|
|
64081
|
-
if (!
|
|
64082
|
-
failValidation4("--description is required.");
|
|
64123
|
+
if (template.length === 0 && (!description2 || description2.trim().length === 0)) {
|
|
64124
|
+
failValidation4("--description is required. Or pass --template to use a recipe's line.");
|
|
64083
64125
|
}
|
|
64084
64126
|
if (template.length === 0 && (!prompt || prompt.trim().length === 0)) {
|
|
64085
64127
|
failValidation4("--prompt is required: it is the whole of what a run does. Or pass --template to copy a brief.");
|
|
@@ -64088,8 +64130,8 @@ var createCommand3 = defineCommand197({
|
|
|
64088
64130
|
const reaction = reactionBody(args5);
|
|
64089
64131
|
const body = {
|
|
64090
64132
|
chatId: requireChatId(),
|
|
64091
|
-
name,
|
|
64092
|
-
description,
|
|
64133
|
+
...name?.trim() ? { name } : {},
|
|
64134
|
+
...description2?.trim() ? { description: description2 } : {},
|
|
64093
64135
|
enabled: !args5.disabled,
|
|
64094
64136
|
...trigger,
|
|
64095
64137
|
...prompt ? { prompt } : {},
|
|
@@ -64438,8 +64480,8 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
64438
64480
|
try {
|
|
64439
64481
|
const save = flag3("save");
|
|
64440
64482
|
const publish = flag3("publish");
|
|
64441
|
-
const
|
|
64442
|
-
if ([save, publish,
|
|
64483
|
+
const remove2 = flag3("delete");
|
|
64484
|
+
if ([save, publish, remove2].filter((id) => id.length > 0).length > 1) {
|
|
64443
64485
|
failValidation4("Pass one of --save, --publish or --delete at a time.");
|
|
64444
64486
|
}
|
|
64445
64487
|
if (save.length > 0) {
|
|
@@ -64468,10 +64510,10 @@ Full guide: __tooling__/docs/tools/baker/scheduled-agents.md`
|
|
|
64468
64510
|
writeJson(response2);
|
|
64469
64511
|
return;
|
|
64470
64512
|
}
|
|
64471
|
-
if (
|
|
64513
|
+
if (remove2.length > 0) {
|
|
64472
64514
|
const response2 = await apiPost("/api/marketplace/remove", {
|
|
64473
64515
|
chatId: requireChatId(),
|
|
64474
|
-
slug:
|
|
64516
|
+
slug: remove2
|
|
64475
64517
|
});
|
|
64476
64518
|
writeJson(response2);
|
|
64477
64519
|
return;
|
|
@@ -64769,7 +64811,7 @@ var schemaCommand2 = defineCommand208({
|
|
|
64769
64811
|
});
|
|
64770
64812
|
|
|
64771
64813
|
// src/commands/shopify/index.ts
|
|
64772
|
-
import { defineCommand as
|
|
64814
|
+
import { defineCommand as defineCommand216 } from "citty";
|
|
64773
64815
|
|
|
64774
64816
|
// src/commands/shopify/buyers.ts
|
|
64775
64817
|
import { defineCommand as defineCommand209 } from "citty";
|
|
@@ -65056,7 +65098,18 @@ var shopifyStoreSummarySchema = z50.object({
|
|
|
65056
65098
|
});
|
|
65057
65099
|
var shopifyStoreResponseSchema = z50.object({
|
|
65058
65100
|
store: shopifyStoreStateSchema,
|
|
65059
|
-
summary: shopifyStoreSummarySchema
|
|
65101
|
+
summary: shopifyStoreSummarySchema,
|
|
65102
|
+
/**
|
|
65103
|
+
* Which store changes Baker may make (S9), by what the store allowed: false
|
|
65104
|
+
* means `baker shopify change …` will ask the user to allow it first.
|
|
65105
|
+
*/
|
|
65106
|
+
canChange: z50.object({
|
|
65107
|
+
products: z50.boolean(),
|
|
65108
|
+
publishing: z50.boolean(),
|
|
65109
|
+
discounts: z50.boolean(),
|
|
65110
|
+
redirects: z50.boolean(),
|
|
65111
|
+
marketing: z50.boolean()
|
|
65112
|
+
}).optional()
|
|
65060
65113
|
});
|
|
65061
65114
|
var shopifyProductRowSchema = z50.object({
|
|
65062
65115
|
productId: z50.string(),
|
|
@@ -65094,7 +65147,7 @@ var shopifyVariantSchema = z50.object({
|
|
|
65094
65147
|
compareAtPrice: shopifyPriceSchema.optional(),
|
|
65095
65148
|
stock: shopifyOptionStockSchema,
|
|
65096
65149
|
quantity: z50.number().optional(),
|
|
65097
|
-
/** The link for
|
|
65150
|
+
/** The option's buy link for ads, emails and messages (a landing places `<ShopifyProduct>` instead): never build a Shopify link by hand. Absent when the option can't be bought. */
|
|
65098
65151
|
buyUrl: z50.string().optional(),
|
|
65099
65152
|
buyUrlKind: shopifyBuyDestinationSchema.optional(),
|
|
65100
65153
|
/** Why there is no buy link. */
|
|
@@ -65209,7 +65262,7 @@ var shopifyProductDetailSchema = z50.object({
|
|
|
65209
65262
|
handle: z50.string(),
|
|
65210
65263
|
/** The product's page in Baker — the link to share with the user. */
|
|
65211
65264
|
url: z50.string(),
|
|
65212
|
-
/** The product's page on the online store: to look at, never a buy button's link (that is each option's `buyUrl
|
|
65265
|
+
/** The product's page on the online store: to look at, never a buy button's link (that is each option's `buyUrl`, or `<ShopifyProduct>` on a landing). */
|
|
65213
65266
|
storeUrl: z50.string().optional(),
|
|
65214
65267
|
availability: shopifyAvailabilitySchema.optional(),
|
|
65215
65268
|
status: shopifyProductStatusSchema.optional(),
|
|
@@ -65773,6 +65826,359 @@ var shopifyCohortsResponseSchema = z51.object({
|
|
|
65773
65826
|
cohorts: z51.array(shopifyCohortSchema)
|
|
65774
65827
|
});
|
|
65775
65828
|
|
|
65829
|
+
// ../api/src/shopify/writes.ts
|
|
65830
|
+
import { z as z52 } from "zod";
|
|
65831
|
+
var SHOPIFY_TEMP_REF_PREFIX = "shopify_temp_";
|
|
65832
|
+
var SHOPIFY_PERMISSION_NEEDED_PREFIX = "Baker isn't allowed to change this Shopify store yet";
|
|
65833
|
+
var shopifyWriteEntitySchema = z52.enum([
|
|
65834
|
+
"product",
|
|
65835
|
+
"variant",
|
|
65836
|
+
"metafield",
|
|
65837
|
+
"collection",
|
|
65838
|
+
"discountCode",
|
|
65839
|
+
"urlRedirect",
|
|
65840
|
+
"marketingActivity",
|
|
65841
|
+
"webPixel",
|
|
65842
|
+
"signupOffer"
|
|
65843
|
+
]);
|
|
65844
|
+
var shopifyDraftOpKindSchema = z52.enum([
|
|
65845
|
+
"shopify.product.update",
|
|
65846
|
+
"shopify.variant.update",
|
|
65847
|
+
"shopify.metafield.set",
|
|
65848
|
+
"shopify.metafield.delete",
|
|
65849
|
+
"shopify.collection.create",
|
|
65850
|
+
"shopify.collection.publish",
|
|
65851
|
+
"shopify.collection.unpublish",
|
|
65852
|
+
"shopify.collection.update",
|
|
65853
|
+
"shopify.collection.delete",
|
|
65854
|
+
"shopify.collection.addProducts",
|
|
65855
|
+
"shopify.collection.removeProducts",
|
|
65856
|
+
"shopify.discountCode.create",
|
|
65857
|
+
"shopify.discountCode.deactivate",
|
|
65858
|
+
"shopify.discountCode.schedule",
|
|
65859
|
+
"shopify.urlRedirect.create",
|
|
65860
|
+
"shopify.urlRedirect.delete",
|
|
65861
|
+
"shopify.marketingActivity.upsert",
|
|
65862
|
+
"shopify.marketingActivity.delete",
|
|
65863
|
+
"shopify.webPixel.update",
|
|
65864
|
+
"shopify.signupOffer.set",
|
|
65865
|
+
"shopify.signupOffer.end"
|
|
65866
|
+
]);
|
|
65867
|
+
var shopifyDecimalSchema = z52.string().regex(/^\d{1,12}(\.\d{1,4})?$/, 'write amounts as a decimal string, e.g. "19.95"');
|
|
65868
|
+
var tagSchema = z52.string().trim().min(1).max(255);
|
|
65869
|
+
var seoSchema = z52.object({ title: z52.string().max(255).optional(), description: z52.string().max(320).optional() }).strict();
|
|
65870
|
+
var SHOPIFY_DESCRIPTION_MAX_CHARS = 2e5;
|
|
65871
|
+
var descriptionHtmlSchema = z52.string().max(SHOPIFY_DESCRIPTION_MAX_CHARS);
|
|
65872
|
+
var productUpdateSchema = z52.object({
|
|
65873
|
+
title: z52.string().trim().min(1).max(255).optional(),
|
|
65874
|
+
descriptionHtml: descriptionHtmlSchema.optional(),
|
|
65875
|
+
seo: seoSchema.optional(),
|
|
65876
|
+
/** The WHOLE tag list after the change (Shopify replaces it). `addTags`/`removeTags` are resolved against the live tags. */
|
|
65877
|
+
tags: z52.array(tagSchema).max(250).optional(),
|
|
65878
|
+
addTags: z52.array(tagSchema).max(250).optional(),
|
|
65879
|
+
removeTags: z52.array(tagSchema).max(250).optional(),
|
|
65880
|
+
productType: z52.string().max(255).optional()
|
|
65881
|
+
}).strict().refine((payload) => Object.keys(payload).length > 0, "name at least one thing to change").refine(
|
|
65882
|
+
(payload) => payload.tags === void 0 || payload.addTags === void 0 && payload.removeTags === void 0,
|
|
65883
|
+
"give either the whole `tags` list or `addTags`/`removeTags`, not both"
|
|
65884
|
+
);
|
|
65885
|
+
var shopifyWeightUnitSchema = z52.enum(["GRAMS", "KILOGRAMS", "OUNCES", "POUNDS"]);
|
|
65886
|
+
var variantUpdateSchema = z52.object({
|
|
65887
|
+
barcode: z52.string().trim().max(255).optional(),
|
|
65888
|
+
sku: z52.string().trim().max(255).optional(),
|
|
65889
|
+
weight: z52.object({ value: z52.number().nonnegative(), unit: shopifyWeightUnitSchema }).strict().optional(),
|
|
65890
|
+
price: shopifyDecimalSchema.optional(),
|
|
65891
|
+
/** `null` clears the sale ("compare at") price. */
|
|
65892
|
+
compareAtPrice: shopifyDecimalSchema.nullable().optional()
|
|
65893
|
+
}).strict().refine((payload) => Object.keys(payload).length > 0, "name at least one thing to change");
|
|
65894
|
+
var metafieldKeySchema = z52.string().min(2).max(64).regex(/^[A-Za-z0-9_-]+$/, "letters, digits, `_` and `-` only");
|
|
65895
|
+
var metafieldNamespaceSchema = z52.string().min(2).max(255).regex(/^[A-Za-z0-9_-]+$/, "letters, digits, `_` and `-` only");
|
|
65896
|
+
var metafieldSetSchema = z52.object({
|
|
65897
|
+
namespace: metafieldNamespaceSchema,
|
|
65898
|
+
key: metafieldKeySchema,
|
|
65899
|
+
/** Shopify's metafield type, e.g. `single_line_text_field`, `multi_line_text_field`, `rich_text_field`, `number_integer`, `boolean`, `json`. */
|
|
65900
|
+
type: z52.string().min(1).max(100),
|
|
65901
|
+
value: z52.string().max(1e5)
|
|
65902
|
+
}).strict();
|
|
65903
|
+
var metafieldDeleteSchema = z52.object({ namespace: metafieldNamespaceSchema, key: metafieldKeySchema }).strict();
|
|
65904
|
+
var shopifyCollectionSortOrderSchema = z52.enum([
|
|
65905
|
+
"MANUAL",
|
|
65906
|
+
"BEST_SELLING",
|
|
65907
|
+
"ALPHA_ASC",
|
|
65908
|
+
"ALPHA_DESC",
|
|
65909
|
+
"PRICE_DESC",
|
|
65910
|
+
"PRICE_ASC",
|
|
65911
|
+
"CREATED",
|
|
65912
|
+
"CREATED_DESC"
|
|
65913
|
+
]);
|
|
65914
|
+
var handleSchema = z52.string().min(1).max(255).regex(/^[a-z0-9]+(?:-[a-z0-9]+)*$/, "lowercase letters, digits and single dashes, e.g. summer-snowboards");
|
|
65915
|
+
var SHOPIFY_COLLECTION_PRODUCTS_MAX = 250;
|
|
65916
|
+
var collectionCreateSchema = z52.object({
|
|
65917
|
+
title: z52.string().trim().min(1).max(255),
|
|
65918
|
+
handle: handleSchema.optional(),
|
|
65919
|
+
descriptionHtml: descriptionHtmlSchema.optional(),
|
|
65920
|
+
seo: seoSchema.optional(),
|
|
65921
|
+
sortOrder: shopifyCollectionSortOrderSchema.optional(),
|
|
65922
|
+
/** The theme template the collection page uses (the part after `collection.`). Kept when a deleted collection is put back. */
|
|
65923
|
+
templateSuffix: z52.string().max(255).optional(),
|
|
65924
|
+
/** Product ids, handles or links, in the order shown with sort order MANUAL. */
|
|
65925
|
+
products: z52.array(z52.string().min(1)).max(SHOPIFY_COLLECTION_PRODUCTS_MAX).optional(),
|
|
65926
|
+
/** Show it on the online store (a second change, staged with it). Default true. */
|
|
65927
|
+
onlineStore: z52.boolean().optional()
|
|
65928
|
+
}).strict();
|
|
65929
|
+
var collectionUpdateSchema = z52.object({
|
|
65930
|
+
title: z52.string().trim().min(1).max(255).optional(),
|
|
65931
|
+
descriptionHtml: descriptionHtmlSchema.optional(),
|
|
65932
|
+
seo: seoSchema.optional(),
|
|
65933
|
+
sortOrder: shopifyCollectionSortOrderSchema.optional()
|
|
65934
|
+
}).strict().refine((payload) => Object.keys(payload).length > 0, "name at least one thing to change");
|
|
65935
|
+
var collectionProductsSchema = z52.object({ products: z52.array(z52.string().min(1)).min(1).max(SHOPIFY_COLLECTION_PRODUCTS_MAX) }).strict();
|
|
65936
|
+
var isoDateTimeSchema = z52.iso.datetime({ offset: true });
|
|
65937
|
+
var discountCodeSchema = z52.string().regex(/^[A-Za-z0-9_-]{3,64}$/, "3\u201364 letters, digits, `_` or `-`, e.g. SPRING10");
|
|
65938
|
+
var discountValueSchema = z52.union([
|
|
65939
|
+
z52.object({ percentage: z52.number().min(1).max(100) }).strict(),
|
|
65940
|
+
z52.object({ amount: shopifyDecimalSchema }).strict()
|
|
65941
|
+
]);
|
|
65942
|
+
var discountAppliesToSchema = z52.union([
|
|
65943
|
+
z52.object({ all: z52.literal(true) }).strict(),
|
|
65944
|
+
z52.object({ products: z52.array(z52.string().min(1)).min(1).max(100) }).strict(),
|
|
65945
|
+
z52.object({ collections: z52.array(z52.string().min(1)).min(1).max(100) }).strict()
|
|
65946
|
+
]);
|
|
65947
|
+
var discountCreateSchema = z52.object({
|
|
65948
|
+
code: discountCodeSchema,
|
|
65949
|
+
/** What the store's staff see; defaults to the code. */
|
|
65950
|
+
title: z52.string().trim().min(1).max(255).optional(),
|
|
65951
|
+
value: discountValueSchema,
|
|
65952
|
+
appliesTo: discountAppliesToSchema,
|
|
65953
|
+
minimumSubtotal: shopifyDecimalSchema.optional(),
|
|
65954
|
+
minimumQuantity: z52.number().int().min(1).max(1e4).optional(),
|
|
65955
|
+
/** Default: the moment it is published. */
|
|
65956
|
+
startsAt: isoDateTimeSchema.optional(),
|
|
65957
|
+
endsAt: isoDateTimeSchema.optional(),
|
|
65958
|
+
usageLimit: z52.number().int().min(1).max(1e6).optional(),
|
|
65959
|
+
/** Default true. */
|
|
65960
|
+
oncePerCustomer: z52.boolean().optional(),
|
|
65961
|
+
combinesWith: z52.object({
|
|
65962
|
+
productDiscounts: z52.boolean().optional(),
|
|
65963
|
+
orderDiscounts: z52.boolean().optional(),
|
|
65964
|
+
shippingDiscounts: z52.boolean().optional()
|
|
65965
|
+
}).strict().optional()
|
|
65966
|
+
}).strict().refine(
|
|
65967
|
+
(payload) => payload.minimumSubtotal === void 0 || payload.minimumQuantity === void 0,
|
|
65968
|
+
"a discount takes a minimum amount or a minimum quantity, not both"
|
|
65969
|
+
);
|
|
65970
|
+
var discountScheduleSchema = z52.object({ startsAt: isoDateTimeSchema, endsAt: isoDateTimeSchema.nullable() }).strict();
|
|
65971
|
+
var storePathSchema = z52.string().min(2).max(1024).regex(/^\/[^\s]*$/, 'a path on the store starting with "/", e.g. /collections/old');
|
|
65972
|
+
var urlRedirectCreateSchema = z52.object({
|
|
65973
|
+
from: storePathSchema,
|
|
65974
|
+
to: z52.union([storePathSchema, z52.url({ protocol: /^https$/ })])
|
|
65975
|
+
}).strict();
|
|
65976
|
+
var shopifyMarketingPlatformSchema = z52.enum(["google-ads", "meta"]);
|
|
65977
|
+
var marketingUpsertSchema = z52.object({
|
|
65978
|
+
platform: shopifyMarketingPlatformSchema,
|
|
65979
|
+
accountId: z52.string().min(1).max(64),
|
|
65980
|
+
campaignId: z52.string().regex(/^\d+$/, "the campaign's numeric id"),
|
|
65981
|
+
utm: z52.object({
|
|
65982
|
+
campaign: z52.string().trim().min(1).max(255),
|
|
65983
|
+
source: z52.string().trim().min(1).max(255),
|
|
65984
|
+
medium: z52.string().trim().min(1).max(255)
|
|
65985
|
+
}).strict()
|
|
65986
|
+
}).strict();
|
|
65987
|
+
var signupOfferHandleSchema = z52.string().regex(/^[a-z0-9][a-z0-9-]{1,39}$/, "2\u201340 lowercase letters, digits and dashes, e.g. welcome-10");
|
|
65988
|
+
var signupOfferSetSchema = z52.object({
|
|
65989
|
+
/** What the store's staff see in front of each code, and the Store page's row. */
|
|
65990
|
+
name: z52.string().trim().min(1).max(80),
|
|
65991
|
+
value: discountValueSchema,
|
|
65992
|
+
appliesTo: discountAppliesToSchema,
|
|
65993
|
+
minimumSubtotal: shopifyDecimalSchema.optional(),
|
|
65994
|
+
/** Each code's life, from the moment it is given. */
|
|
65995
|
+
validForDays: z52.number().int().min(1).max(365),
|
|
65996
|
+
/** The offer stops giving codes after this; no code outlives it. */
|
|
65997
|
+
endsAt: isoDateTimeSchema.optional(),
|
|
65998
|
+
/** How many codes the offer gives in all. */
|
|
65999
|
+
maxCodes: z52.number().int().min(1).max(1e5).optional(),
|
|
66000
|
+
/** The start of every code (WELCOME-7Q2M9XKD); default: the handle in capitals. */
|
|
66001
|
+
codePrefix: z52.string().regex(/^[A-Z0-9]{2,12}$/, "2\u201312 capital letters or digits, e.g. WELCOME").optional(),
|
|
66002
|
+
combinesWith: z52.object({
|
|
66003
|
+
productDiscounts: z52.boolean().optional(),
|
|
66004
|
+
orderDiscounts: z52.boolean().optional(),
|
|
66005
|
+
shippingDiscounts: z52.boolean().optional()
|
|
66006
|
+
}).strict().optional()
|
|
66007
|
+
}).strict();
|
|
66008
|
+
var emptySchema = z52.object({}).strict();
|
|
66009
|
+
var opFields = {
|
|
66010
|
+
/** Optional caller-chosen ref for a create, so later ops in the batch can name it (`shopify_temp_…`). */
|
|
66011
|
+
ref: z52.string().startsWith(SHOPIFY_TEMP_REF_PREFIX).max(80).optional()
|
|
66012
|
+
};
|
|
66013
|
+
var target2 = z52.string().trim().min(1).max(2048);
|
|
66014
|
+
var shopifyDraftOpInputSchema = z52.discriminatedUnion("kind", [
|
|
66015
|
+
z52.object({ kind: z52.literal("shopify.product.update"), target: target2, payload: productUpdateSchema, ...opFields }),
|
|
66016
|
+
z52.object({ kind: z52.literal("shopify.variant.update"), target: target2, payload: variantUpdateSchema, ...opFields }),
|
|
66017
|
+
z52.object({ kind: z52.literal("shopify.metafield.set"), target: target2, payload: metafieldSetSchema, ...opFields }),
|
|
66018
|
+
z52.object({ kind: z52.literal("shopify.metafield.delete"), target: target2, payload: metafieldDeleteSchema, ...opFields }),
|
|
66019
|
+
z52.object({ kind: z52.literal("shopify.collection.create"), payload: collectionCreateSchema, ...opFields }),
|
|
66020
|
+
z52.object({ kind: z52.literal("shopify.collection.publish"), target: target2, payload: emptySchema.optional(), ...opFields }),
|
|
66021
|
+
z52.object({ kind: z52.literal("shopify.collection.unpublish"), target: target2, payload: emptySchema.optional(), ...opFields }),
|
|
66022
|
+
z52.object({ kind: z52.literal("shopify.collection.update"), target: target2, payload: collectionUpdateSchema, ...opFields }),
|
|
66023
|
+
z52.object({ kind: z52.literal("shopify.collection.delete"), target: target2, payload: emptySchema.optional(), ...opFields }),
|
|
66024
|
+
z52.object({
|
|
66025
|
+
kind: z52.literal("shopify.collection.addProducts"),
|
|
66026
|
+
target: target2,
|
|
66027
|
+
payload: collectionProductsSchema,
|
|
66028
|
+
...opFields
|
|
66029
|
+
}),
|
|
66030
|
+
z52.object({
|
|
66031
|
+
kind: z52.literal("shopify.collection.removeProducts"),
|
|
66032
|
+
target: target2,
|
|
66033
|
+
payload: collectionProductsSchema,
|
|
66034
|
+
...opFields
|
|
66035
|
+
}),
|
|
66036
|
+
z52.object({ kind: z52.literal("shopify.discountCode.create"), payload: discountCreateSchema, ...opFields }),
|
|
66037
|
+
z52.object({
|
|
66038
|
+
kind: z52.literal("shopify.discountCode.deactivate"),
|
|
66039
|
+
target: target2,
|
|
66040
|
+
payload: emptySchema.optional(),
|
|
66041
|
+
...opFields
|
|
66042
|
+
}),
|
|
66043
|
+
z52.object({ kind: z52.literal("shopify.discountCode.schedule"), target: target2, payload: discountScheduleSchema, ...opFields }),
|
|
66044
|
+
z52.object({ kind: z52.literal("shopify.urlRedirect.create"), payload: urlRedirectCreateSchema, ...opFields }),
|
|
66045
|
+
z52.object({ kind: z52.literal("shopify.urlRedirect.delete"), target: target2, payload: emptySchema.optional(), ...opFields }),
|
|
66046
|
+
z52.object({ kind: z52.literal("shopify.marketingActivity.upsert"), payload: marketingUpsertSchema, ...opFields }),
|
|
66047
|
+
z52.object({
|
|
66048
|
+
kind: z52.literal("shopify.marketingActivity.delete"),
|
|
66049
|
+
target: target2,
|
|
66050
|
+
payload: emptySchema.optional(),
|
|
66051
|
+
...opFields
|
|
66052
|
+
}),
|
|
66053
|
+
z52.object({ kind: z52.literal("shopify.webPixel.update"), payload: emptySchema.optional(), ...opFields }),
|
|
66054
|
+
z52.object({
|
|
66055
|
+
kind: z52.literal("shopify.signupOffer.set"),
|
|
66056
|
+
target: signupOfferHandleSchema,
|
|
66057
|
+
payload: signupOfferSetSchema,
|
|
66058
|
+
...opFields
|
|
66059
|
+
}),
|
|
66060
|
+
z52.object({
|
|
66061
|
+
kind: z52.literal("shopify.signupOffer.end"),
|
|
66062
|
+
target: signupOfferHandleSchema,
|
|
66063
|
+
payload: emptySchema.optional(),
|
|
66064
|
+
...opFields
|
|
66065
|
+
})
|
|
66066
|
+
]);
|
|
66067
|
+
var PAYLOAD_KEYS = new Map(
|
|
66068
|
+
shopifyDraftOpInputSchema.options.map((option) => {
|
|
66069
|
+
const payload = option.shape.payload;
|
|
66070
|
+
const object = payload instanceof z52.ZodOptional ? payload.unwrap() : payload;
|
|
66071
|
+
return [option.shape.kind.value, object instanceof z52.ZodObject ? Object.keys(object.shape) : []];
|
|
66072
|
+
})
|
|
66073
|
+
);
|
|
66074
|
+
var askUserQuestionInputSchema = z52.object({
|
|
66075
|
+
questions: z52.array(
|
|
66076
|
+
z52.object({
|
|
66077
|
+
question: z52.string(),
|
|
66078
|
+
header: z52.string(),
|
|
66079
|
+
multiSelect: z52.boolean(),
|
|
66080
|
+
options: z52.array(z52.object({ label: z52.string(), description: z52.string() })).min(2)
|
|
66081
|
+
})
|
|
66082
|
+
).min(1)
|
|
66083
|
+
});
|
|
66084
|
+
var shopifyNeedsApprovalSchema = z52.object({
|
|
66085
|
+
/** Pass to AskUserQuestion exactly as given. */
|
|
66086
|
+
askUserQuestion: askUserQuestionInputSchema,
|
|
66087
|
+
/** The user already said no to (at least one of) these changes: don't ask again in this turn. */
|
|
66088
|
+
declined: z52.boolean()
|
|
66089
|
+
});
|
|
66090
|
+
var shopifyVerificationSchema = z52.object({
|
|
66091
|
+
status: z52.enum(["confirmed", "drifted", "unconfirmed"]),
|
|
66092
|
+
checkedAt: z52.number(),
|
|
66093
|
+
note: z52.string().optional()
|
|
66094
|
+
});
|
|
66095
|
+
var shopifyDraftOpResultSchema = z52.object({
|
|
66096
|
+
status: z52.enum(["applied", "failed", "skipped"]),
|
|
66097
|
+
createdId: z52.string().optional(),
|
|
66098
|
+
error: z52.string().optional(),
|
|
66099
|
+
note: z52.string().optional(),
|
|
66100
|
+
skippedBecause: z52.string().optional(),
|
|
66101
|
+
executedAt: z52.number().optional(),
|
|
66102
|
+
pendingJob: z52.string().optional(),
|
|
66103
|
+
verification: shopifyVerificationSchema.optional()
|
|
66104
|
+
});
|
|
66105
|
+
var stagedOpSchema = z52.object({
|
|
66106
|
+
ref: z52.string(),
|
|
66107
|
+
kind: shopifyDraftOpKindSchema,
|
|
66108
|
+
summary: z52.string(),
|
|
66109
|
+
warnings: z52.array(z52.string())
|
|
66110
|
+
});
|
|
66111
|
+
var shopifyDraftStageResponseSchema = z52.union([
|
|
66112
|
+
z52.object({
|
|
66113
|
+
staged: z52.literal(true),
|
|
66114
|
+
count: z52.number(),
|
|
66115
|
+
ops: z52.array(stagedOpSchema),
|
|
66116
|
+
amended: z52.boolean().optional()
|
|
66117
|
+
}),
|
|
66118
|
+
z52.object({ staged: z52.literal(false), needsApproval: shopifyNeedsApprovalSchema })
|
|
66119
|
+
]);
|
|
66120
|
+
var SHOPIFY_DRAFT_BATCH_MAX = 100;
|
|
66121
|
+
var shopifyDraftStageRequestSchema = z52.object({ chatId: z52.string(), op: shopifyDraftOpInputSchema });
|
|
66122
|
+
var shopifyDraftStageBatchRequestSchema = z52.object({
|
|
66123
|
+
chatId: z52.string(),
|
|
66124
|
+
ops: z52.array(shopifyDraftOpInputSchema).min(1).max(SHOPIFY_DRAFT_BATCH_MAX)
|
|
66125
|
+
});
|
|
66126
|
+
var shopifyDraftAmendRequestSchema = z52.object({
|
|
66127
|
+
chatId: z52.string(),
|
|
66128
|
+
ref: z52.string(),
|
|
66129
|
+
patch: z52.record(z52.string(), z52.unknown())
|
|
66130
|
+
});
|
|
66131
|
+
var shopifyDraftOpViewSchema = z52.object({
|
|
66132
|
+
ref: z52.string(),
|
|
66133
|
+
kind: shopifyDraftOpKindSchema,
|
|
66134
|
+
entity: shopifyWriteEntitySchema,
|
|
66135
|
+
target: z52.string().optional(),
|
|
66136
|
+
targetLabel: z52.string().optional(),
|
|
66137
|
+
summary: z52.string(),
|
|
66138
|
+
money: z52.boolean(),
|
|
66139
|
+
warnings: z52.array(z52.string()).optional(),
|
|
66140
|
+
dependsOn: z52.array(z52.string()),
|
|
66141
|
+
undoOf: z52.string().optional(),
|
|
66142
|
+
stagedAt: z52.number(),
|
|
66143
|
+
result: shopifyDraftOpResultSchema.optional()
|
|
66144
|
+
});
|
|
66145
|
+
var shopifyDraftListRequestSchema = z52.object({ chatId: z52.string() });
|
|
66146
|
+
var shopifyDraftListResponseSchema = z52.object({
|
|
66147
|
+
status: z52.enum(["active", "publishing", "applied", "discarded", "none"]),
|
|
66148
|
+
store: z52.string().optional(),
|
|
66149
|
+
count: z52.number(),
|
|
66150
|
+
ops: z52.array(shopifyDraftOpViewSchema),
|
|
66151
|
+
publishError: z52.string().optional()
|
|
66152
|
+
});
|
|
66153
|
+
var shopifyDraftShowRequestSchema = z52.object({ chatId: z52.string(), ref: z52.string() });
|
|
66154
|
+
var shopifyDraftShowResponseSchema = z52.object({
|
|
66155
|
+
op: shopifyDraftOpViewSchema.extend({ payload: z52.unknown().optional(), before: z52.unknown().optional() })
|
|
66156
|
+
});
|
|
66157
|
+
var shopifyDraftRemoveRequestSchema = z52.object({ chatId: z52.string(), ref: z52.string() });
|
|
66158
|
+
var shopifyDraftRemoveResponseSchema = z52.object({ removed: z52.array(z52.string()) });
|
|
66159
|
+
var shopifyDraftClearRequestSchema = z52.object({ chatId: z52.string() });
|
|
66160
|
+
var shopifyDraftClearResponseSchema = z52.object({ cleared: z52.number() });
|
|
66161
|
+
var shopifyChangeViewSchema = z52.object({
|
|
66162
|
+
changeId: z52.string(),
|
|
66163
|
+
when: z52.string(),
|
|
66164
|
+
kind: shopifyDraftOpKindSchema,
|
|
66165
|
+
summary: z52.string(),
|
|
66166
|
+
status: z52.enum(["applied", "failed", "undone"]),
|
|
66167
|
+
chatId: z52.string(),
|
|
66168
|
+
undoable: z52.boolean(),
|
|
66169
|
+
/** Why it can't be undone, when it can't. Whether the store still holds Baker's change is checked when undo is staged. */
|
|
66170
|
+
notUndoableBecause: z52.string().optional()
|
|
66171
|
+
});
|
|
66172
|
+
var shopifyChangesRequestSchema = z52.object({
|
|
66173
|
+
limit: z52.coerce.number().int().min(1).max(100).optional(),
|
|
66174
|
+
chatId: z52.string().optional()
|
|
66175
|
+
});
|
|
66176
|
+
var shopifyChangesResponseSchema = z52.object({ changes: z52.array(shopifyChangeViewSchema) });
|
|
66177
|
+
var shopifyUndoRequestSchema = z52.object({
|
|
66178
|
+
chatId: z52.string(),
|
|
66179
|
+
changeIds: z52.array(z52.string().min(1)).min(1).max(SHOPIFY_DRAFT_BATCH_MAX)
|
|
66180
|
+
});
|
|
66181
|
+
|
|
65776
66182
|
// src/commands/shopify/refusal.ts
|
|
65777
66183
|
var CONTINUE2 = "Keep going with the wider task: say which part you could not read, skip it, and finish everything else.";
|
|
65778
66184
|
function shopifyRefusal(code, message) {
|
|
@@ -65797,6 +66203,36 @@ function shopifyRefusal(code, message) {
|
|
|
65797
66203
|
}
|
|
65798
66204
|
return null;
|
|
65799
66205
|
}
|
|
66206
|
+
function storeChangeRefusal(code, message) {
|
|
66207
|
+
if (code !== "FORBIDDEN" || !message.startsWith(SHOPIFY_PERMISSION_NEEDED_PREFIX)) return null;
|
|
66208
|
+
return {
|
|
66209
|
+
code: "SHOPIFY_PERMISSION_NEEDED",
|
|
66210
|
+
fix: {
|
|
66211
|
+
action: "request_connection",
|
|
66212
|
+
explanation: 'AGENT INSTRUCTION: the store hasn\'t allowed Baker to make this kind of change yet, and only the user can. Call `request_connection` with { platform: "shopify", reason } \u2014 reason in their words, e.g. "so I can update your product titles". They allow it in Shopify; the card finishes when Shopify confirms. If its result lists something under cannotRead, that change is still not allowed: don\'t promise it. Then run this command again. Keep going with the rest of the task.'
|
|
66213
|
+
}
|
|
66214
|
+
};
|
|
66215
|
+
}
|
|
66216
|
+
function approvalRefusal(needsApproval) {
|
|
66217
|
+
if (needsApproval.declined) {
|
|
66218
|
+
return {
|
|
66219
|
+
code: "NEEDS_USER_APPROVAL",
|
|
66220
|
+
fix: {
|
|
66221
|
+
action: "continue_without",
|
|
66222
|
+
askUserQuestion: needsApproval.askUserQuestion,
|
|
66223
|
+
explanation: "The user said no to this change. Don't stage it, don't ask again on your own, and don't find another way to make it; carry on with the rest of the task. Ask again only if the user asks for it again (they changed their mind): then call AskUserQuestion with `askUserQuestion` exactly as given, and if they choose \"Yes, make these changes\", run this same command again."
|
|
66224
|
+
}
|
|
66225
|
+
};
|
|
66226
|
+
}
|
|
66227
|
+
return {
|
|
66228
|
+
code: "NEEDS_USER_APPROVAL",
|
|
66229
|
+
fix: {
|
|
66230
|
+
action: "ask_user",
|
|
66231
|
+
askUserQuestion: needsApproval.askUserQuestion,
|
|
66232
|
+
explanation: 'This changes money in the store, so the user says yes first. Call AskUserQuestion with `askUserQuestion` exactly as given (you may say a sentence before it, never change it). If the user chooses "Yes, make these changes", run this same command again. Do not stage it any other way.'
|
|
66233
|
+
}
|
|
66234
|
+
};
|
|
66235
|
+
}
|
|
65800
66236
|
|
|
65801
66237
|
// src/commands/shopify/common.ts
|
|
65802
66238
|
var PRODUCT_NOT_FOUND_HINT = "Find it with `baker shopify products --search <words>`. If `baker shopify store` shows products Baker couldn't read or products still coming in, it may be one of those \u2014 say so rather than that it doesn't exist.";
|
|
@@ -65952,17 +66388,17 @@ function stockHintsFor(product) {
|
|
|
65952
66388
|
}
|
|
65953
66389
|
var BUY_NOTES = {
|
|
65954
66390
|
password_protected: "The store asks for a password before anyone can see it: a shopper following a buy link meets it first \u2014 tell the user before sending traffic.",
|
|
65955
|
-
subscription_optional: "It can also be bought by subscription
|
|
66391
|
+
subscription_optional: "It can also be bought by subscription: on a landing `<ShopifyProduct>` offers the plans; a `buyUrl` elsewhere adds it once.",
|
|
65956
66392
|
own_storefront: "The store sells through its own storefront: buy links go straight to Shopify's checkout.",
|
|
65957
66393
|
details_pending: "Baker hasn't read this product's details yet (subscriptions among them): its buy links may change.",
|
|
65958
|
-
subscription_only: "It's sold only by subscription
|
|
66394
|
+
subscription_only: "It's sold only by subscription: on a landing `<ShopifyProduct>` offers the plans; a `buyUrl` elsewhere is its product page, where the shopper picks one."
|
|
65959
66395
|
};
|
|
65960
66396
|
function buyHints(product) {
|
|
65961
66397
|
const hints2 = [];
|
|
65962
66398
|
if (product.variants.some((variant) => variant.buyUrl)) {
|
|
65963
66399
|
hints2.push(
|
|
65964
|
-
`
|
|
65965
|
-
|
|
66400
|
+
`On a landing, place \`<ShopifyProduct product="${product.handle}" />\` (and \`<ShopifyBuyButton product="${product.handle}">\` for a second button): it shows the options, price and stock, and its button hands the shopper to the store. Never put a \`buyUrl\` on a landing yourself, and never build an option picker. See /landing \`.claude/skills/landing/references/verticals/ecommerce.md\`.`,
|
|
66401
|
+
`\`buyUrl\` is for links outside a landing \u2014 an ad, an email, a message: one per option; never \`storeUrl\` (a page to look at), never a Shopify link built yourself (it follows the store's setting: ${product.buyDestination.replace("_", " ")}; \`--quantity <n>\` sets how many).`
|
|
65966
66402
|
);
|
|
65967
66403
|
}
|
|
65968
66404
|
const notes = new Set(product.variants.flatMap((variant) => variant.notes ?? []));
|
|
@@ -66417,18 +66853,884 @@ var segmentsCommand = defineCommand209({
|
|
|
66417
66853
|
}
|
|
66418
66854
|
});
|
|
66419
66855
|
|
|
66420
|
-
// src/commands/shopify/
|
|
66856
|
+
// src/commands/shopify/change.ts
|
|
66857
|
+
import { defineCommand as defineCommand211 } from "citty";
|
|
66858
|
+
|
|
66859
|
+
// src/commands/shopify/writeCommon.ts
|
|
66421
66860
|
import { defineCommand as defineCommand210 } from "citty";
|
|
66422
66861
|
|
|
66862
|
+
// src/commands/shopify/writeHints.ts
|
|
66863
|
+
var STAGED = "Staged \u2014 nothing changes in the store until the user publishes the chat. Review: `baker shopify draft list`. Say the change is ready and happens when they publish, never that it is done.";
|
|
66864
|
+
var WARNINGS = "Read the warnings above before telling the user this is ready.";
|
|
66865
|
+
var DISCOUNT = "The code works only after the chat is published; don't announce it as live or share it as working yet.";
|
|
66866
|
+
var EMPTY_COLLECTION = "The new collection has no products yet: add them with `baker shopify change collection add <collection> <product\u2026>`.";
|
|
66867
|
+
var COLLECTION_DELETE = "Its address stops working when it is deleted: keep old links alive with `baker shopify change redirect create --from /collections/<handle> --to <page>` (ask the user where they should land).";
|
|
66868
|
+
function repointedRedirects(ops) {
|
|
66869
|
+
const removed = ops.flatMap(
|
|
66870
|
+
(op) => op.kind === "shopify.urlRedirect.delete" && op.ref.startsWith("urlRedirect:") ? [op.ref.slice("urlRedirect:".length)] : []
|
|
66871
|
+
);
|
|
66872
|
+
return removed.flatMap((path49) => {
|
|
66873
|
+
const made = ops.find(
|
|
66874
|
+
(op) => op.kind === "shopify.urlRedirect.create" && op.summary.startsWith(`Redirect ${path49} to `)
|
|
66875
|
+
);
|
|
66876
|
+
if (!made) return [];
|
|
66877
|
+
const to = made.summary.slice(`Redirect ${path49} to `.length);
|
|
66878
|
+
return [
|
|
66879
|
+
`${path49} still redirects after publish, now to ${to}: tell the user the old link goes there, not that its redirect is removed.`
|
|
66880
|
+
];
|
|
66881
|
+
});
|
|
66882
|
+
}
|
|
66883
|
+
function stageHints(data) {
|
|
66884
|
+
if (!data.staged) return [];
|
|
66885
|
+
const hints2 = [STAGED, ...repointedRedirects(data.ops)];
|
|
66886
|
+
if (data.ops.some((op) => op.warnings.length > 0)) hints2.push(WARNINGS);
|
|
66887
|
+
if (data.ops.some((op) => op.kind === "shopify.discountCode.create")) hints2.push(DISCOUNT);
|
|
66888
|
+
if (data.ops.some((op) => op.kind === "shopify.collection.delete")) hints2.push(COLLECTION_DELETE);
|
|
66889
|
+
if (data.ops.some(
|
|
66890
|
+
(op) => op.kind === "shopify.collection.create" && op.warnings.some((warning) => /no products/.test(warning))
|
|
66891
|
+
)) {
|
|
66892
|
+
hints2.push(EMPTY_COLLECTION);
|
|
66893
|
+
}
|
|
66894
|
+
return hints2;
|
|
66895
|
+
}
|
|
66896
|
+
function changesHints(data) {
|
|
66897
|
+
if (data.changes.length === 0) return ["Baker hasn't changed this store yet."];
|
|
66898
|
+
return data.changes.some((change) => change.undoable) ? ["Undo one with `baker shopify undo <changeId>` (staged like any change, applied when the user publishes)."] : [];
|
|
66899
|
+
}
|
|
66900
|
+
function auditHints(data) {
|
|
66901
|
+
const hints2 = [];
|
|
66902
|
+
for (const check of data.checks) {
|
|
66903
|
+
if (check.fix?.kind === "staged") hints2.push(`Stage the fix for "${check.title}": \`${check.fix.command}\`.`);
|
|
66904
|
+
}
|
|
66905
|
+
for (const check of data.checks) {
|
|
66906
|
+
if (check.fix?.kind === "merchant") {
|
|
66907
|
+
hints2.push(
|
|
66908
|
+
`Only the merchant can fix "${check.title}": ${check.fix.task}. Make it a Task with \`baker actions create\`${check.fix.link ? " and include the link" : ""}.`
|
|
66909
|
+
);
|
|
66910
|
+
}
|
|
66911
|
+
}
|
|
66912
|
+
for (const check of data.checks) {
|
|
66913
|
+
if (check.fix?.kind === "decision") hints2.push(`A person decides "${check.title}": ${check.fix.where}.`);
|
|
66914
|
+
}
|
|
66915
|
+
return hints2;
|
|
66916
|
+
}
|
|
66917
|
+
|
|
66918
|
+
// src/commands/shopify/writeCommon.ts
|
|
66919
|
+
function failChange(err) {
|
|
66920
|
+
if (err instanceof ApiError) {
|
|
66921
|
+
const refusal = storeChangeRefusal(err.code, err.message);
|
|
66922
|
+
if (refusal) {
|
|
66923
|
+
writeJson({ ok: false, error: { code: refusal.code, message: err.message, fix: refusal.fix } });
|
|
66924
|
+
process.exit(1);
|
|
66925
|
+
}
|
|
66926
|
+
}
|
|
66927
|
+
fail9(err);
|
|
66928
|
+
}
|
|
66929
|
+
function emitStage(data) {
|
|
66930
|
+
if (!data.staged) {
|
|
66931
|
+
const refusal = approvalRefusal(data.needsApproval);
|
|
66932
|
+
writeJson({
|
|
66933
|
+
ok: false,
|
|
66934
|
+
error: {
|
|
66935
|
+
code: refusal.code,
|
|
66936
|
+
message: data.needsApproval.declined ? "The user said no to this change in the chat." : "This changes money in the store: the user has to say yes to it in the chat first.",
|
|
66937
|
+
fix: refusal.fix
|
|
66938
|
+
}
|
|
66939
|
+
});
|
|
66940
|
+
process.exit(1);
|
|
66941
|
+
}
|
|
66942
|
+
writeJson({ ok: true, hints: stageHints(data), data });
|
|
66943
|
+
}
|
|
66944
|
+
async function stage(ops) {
|
|
66945
|
+
const chatId = requireChatId();
|
|
66946
|
+
try {
|
|
66947
|
+
const [only] = ops;
|
|
66948
|
+
const data = ops.length === 1 && only ? await apiPost("/api/shopify/draft/stage", { chatId, op: only }) : await apiPost("/api/shopify/draft/stage-batch", { chatId, ops });
|
|
66949
|
+
emitStage(data);
|
|
66950
|
+
} catch (err) {
|
|
66951
|
+
failChange(err);
|
|
66952
|
+
}
|
|
66953
|
+
}
|
|
66954
|
+
function list(value) {
|
|
66955
|
+
if (typeof value !== "string" || value.trim() === "") return void 0;
|
|
66956
|
+
return value.split(",").map((entry) => entry.trim()).filter(Boolean);
|
|
66957
|
+
}
|
|
66958
|
+
function defined(entries) {
|
|
66959
|
+
return Object.fromEntries(Object.entries(entries).filter(([, value]) => value !== void 0 && value !== ""));
|
|
66960
|
+
}
|
|
66961
|
+
function payloadsFrom(args5) {
|
|
66962
|
+
if (typeof args5.json !== "string" && typeof args5.file !== "string") return null;
|
|
66963
|
+
return loadJsonPayloads(args5);
|
|
66964
|
+
}
|
|
66965
|
+
var JSON_ARGS = {
|
|
66966
|
+
json: {
|
|
66967
|
+
type: "string",
|
|
66968
|
+
description: 'The change as inline JSON: a payload for the target given (e.g. {"addTags":["summer"]}), or an array of {"kind","target","payload"} items to stage together in one store read',
|
|
66969
|
+
required: false
|
|
66970
|
+
},
|
|
66971
|
+
file: { type: "string", description: "A JSON file holding the same", required: false }
|
|
66972
|
+
};
|
|
66973
|
+
function leaf(schemaName, meta, args5, run) {
|
|
66974
|
+
registerSchema({
|
|
66975
|
+
command: schemaName,
|
|
66976
|
+
description: meta.description,
|
|
66977
|
+
args: Object.fromEntries(
|
|
66978
|
+
Object.entries(args5).map(([name, arg]) => [
|
|
66979
|
+
name,
|
|
66980
|
+
{
|
|
66981
|
+
type: String(arg.type ?? "string"),
|
|
66982
|
+
description: String(arg.description ?? ""),
|
|
66983
|
+
required: arg.required === true
|
|
66984
|
+
}
|
|
66985
|
+
])
|
|
66986
|
+
)
|
|
66987
|
+
});
|
|
66988
|
+
return defineCommand210({ meta, args: args5, run: async ({ args: parsed }) => await run(parsed) });
|
|
66989
|
+
}
|
|
66990
|
+
|
|
66991
|
+
// src/commands/shopify/change.ts
|
|
66992
|
+
var TARGET = (what) => ({ type: "positional", description: what, required: false });
|
|
66993
|
+
function positionals(args5) {
|
|
66994
|
+
return Array.isArray(args5._) ? args5._.filter((value) => typeof value === "string") : [];
|
|
66995
|
+
}
|
|
66996
|
+
function target3(args5, what) {
|
|
66997
|
+
const [first] = positionals(args5);
|
|
66998
|
+
if (!first) failValidation3(`pass the ${what} as the first argument`);
|
|
66999
|
+
return first;
|
|
67000
|
+
}
|
|
67001
|
+
async function stageFor(kind, args5, flags, targetOf) {
|
|
67002
|
+
const payloads = payloadsFrom(args5);
|
|
67003
|
+
if (payloads) {
|
|
67004
|
+
await stage(
|
|
67005
|
+
payloads.map(
|
|
67006
|
+
(payload2) => "kind" in payload2 ? payload2 : { kind, ...targetOf ? { target: targetOf() } : {}, payload: payload2 }
|
|
67007
|
+
)
|
|
67008
|
+
);
|
|
67009
|
+
return;
|
|
67010
|
+
}
|
|
67011
|
+
const payload = flags();
|
|
67012
|
+
if (Object.keys(payload).length === 0)
|
|
67013
|
+
failValidation3("name at least one thing to change (see `baker schema` for the flags)");
|
|
67014
|
+
await stage([{ kind, ...targetOf ? { target: targetOf() } : {}, payload }]);
|
|
67015
|
+
}
|
|
67016
|
+
var productUpdate = leaf(
|
|
67017
|
+
"shopify.change.product.update",
|
|
67018
|
+
{
|
|
67019
|
+
name: "update",
|
|
67020
|
+
description: `Start here to change a product's title, description, SEO, tags or type. Example: baker shopify change product update the-snowboard --title "The Snowboard" --seo-title "\u2026" --add-tags summer. Tags: --tags replaces the whole list, --add-tags/--remove-tags edit it. Descriptions are HTML without scripts or frames. Many products in one call: --json '[{"kind":"shopify.product.update","target":"<handle>","payload":{"addTags":["summer"]}}, \u2026]' \u2014 payload keys: title, descriptionHtml, seo {title, description}, tags | addTags | removeTags, productType.`
|
|
67021
|
+
},
|
|
67022
|
+
{
|
|
67023
|
+
product: TARGET("Product id, handle or link"),
|
|
67024
|
+
title: { type: "string", description: "New title", required: false },
|
|
67025
|
+
description: { type: "string", description: "New description, as HTML (no scripts or frames)", required: false },
|
|
67026
|
+
"seo-title": { type: "string", description: "Title shown in search results", required: false },
|
|
67027
|
+
"seo-description": { type: "string", description: "Description shown in search results", required: false },
|
|
67028
|
+
tags: { type: "string", description: "The whole tag list, comma separated (replaces it)", required: false },
|
|
67029
|
+
"add-tags": { type: "string", description: "Tags to add, comma separated", required: false },
|
|
67030
|
+
"remove-tags": { type: "string", description: "Tags to remove, comma separated", required: false },
|
|
67031
|
+
type: { type: "string", description: "Product type", required: false },
|
|
67032
|
+
...JSON_ARGS
|
|
67033
|
+
},
|
|
67034
|
+
async (args5) => await stageFor(
|
|
67035
|
+
"shopify.product.update",
|
|
67036
|
+
args5,
|
|
67037
|
+
() => defined({
|
|
67038
|
+
title: args5.title,
|
|
67039
|
+
descriptionHtml: args5.description,
|
|
67040
|
+
seo: args5["seo-title"] !== void 0 || args5["seo-description"] !== void 0 ? defined({ title: args5["seo-title"], description: args5["seo-description"] }) : void 0,
|
|
67041
|
+
tags: list(args5.tags),
|
|
67042
|
+
addTags: list(args5["add-tags"]),
|
|
67043
|
+
removeTags: list(args5["remove-tags"]),
|
|
67044
|
+
productType: args5.type
|
|
67045
|
+
}),
|
|
67046
|
+
positionals(args5).length > 0 ? () => target3(args5, "product") : void 0
|
|
67047
|
+
)
|
|
67048
|
+
);
|
|
67049
|
+
var WEIGHT_UNITS = {
|
|
67050
|
+
g: "GRAMS",
|
|
67051
|
+
gram: "GRAMS",
|
|
67052
|
+
grams: "GRAMS",
|
|
67053
|
+
kg: "KILOGRAMS",
|
|
67054
|
+
kgs: "KILOGRAMS",
|
|
67055
|
+
kilogram: "KILOGRAMS",
|
|
67056
|
+
kilograms: "KILOGRAMS",
|
|
67057
|
+
oz: "OUNCES",
|
|
67058
|
+
ounce: "OUNCES",
|
|
67059
|
+
ounces: "OUNCES",
|
|
67060
|
+
lb: "POUNDS",
|
|
67061
|
+
lbs: "POUNDS",
|
|
67062
|
+
pound: "POUNDS",
|
|
67063
|
+
pounds: "POUNDS"
|
|
67064
|
+
};
|
|
67065
|
+
function weightOf(value, unit) {
|
|
67066
|
+
if (value === void 0) return void 0;
|
|
67067
|
+
const named = typeof unit === "string" ? unit.trim() : "";
|
|
67068
|
+
return {
|
|
67069
|
+
value: Number(value),
|
|
67070
|
+
unit: named ? WEIGHT_UNITS[named.toLowerCase()] ?? named.toUpperCase() : "KILOGRAMS"
|
|
67071
|
+
};
|
|
67072
|
+
}
|
|
67073
|
+
var variantUpdate = leaf(
|
|
67074
|
+
"shopify.change.variant.update",
|
|
67075
|
+
{
|
|
67076
|
+
name: "update",
|
|
67077
|
+
description: "Change one option (variant) of a product: SKU, barcode (GTIN), weight \u2014 and its price or sale price, which the user must approve in the chat first (the command hands you the question). Take the option's id from `baker shopify product <product>` (variantId). Example: baker shopify change variant update 456 --sku SNOW-L --barcode 4006381333931 --weight 1.5 --weight-unit kg. --sale-price none clears it."
|
|
67078
|
+
},
|
|
67079
|
+
{
|
|
67080
|
+
variant: TARGET("The option's id (variantId from baker shopify product)"),
|
|
67081
|
+
sku: { type: "string", description: "SKU", required: false },
|
|
67082
|
+
barcode: { type: "string", description: "Barcode / GTIN (8, 12, 13 or 14 digits)", required: false },
|
|
67083
|
+
weight: { type: "string", description: "Weight value", required: false },
|
|
67084
|
+
"weight-unit": {
|
|
67085
|
+
type: "string",
|
|
67086
|
+
description: "kg | g | lb | oz (default kg)",
|
|
67087
|
+
required: false
|
|
67088
|
+
},
|
|
67089
|
+
price: {
|
|
67090
|
+
type: "string",
|
|
67091
|
+
description: `Price as a decimal, e.g. "649.95" (needs the user's yes)`,
|
|
67092
|
+
required: false
|
|
67093
|
+
},
|
|
67094
|
+
"sale-price": {
|
|
67095
|
+
type: "string",
|
|
67096
|
+
description: `Compare-at price, or "none" to clear it (needs the user's yes)`,
|
|
67097
|
+
required: false
|
|
67098
|
+
},
|
|
67099
|
+
...JSON_ARGS
|
|
67100
|
+
},
|
|
67101
|
+
async (args5) => await stageFor(
|
|
67102
|
+
"shopify.variant.update",
|
|
67103
|
+
args5,
|
|
67104
|
+
() => defined({
|
|
67105
|
+
sku: args5.sku,
|
|
67106
|
+
barcode: args5.barcode,
|
|
67107
|
+
weight: weightOf(args5.weight, args5["weight-unit"]),
|
|
67108
|
+
price: args5.price,
|
|
67109
|
+
compareAtPrice: args5["sale-price"] === "none" ? null : args5["sale-price"]
|
|
67110
|
+
}),
|
|
67111
|
+
positionals(args5).length > 0 ? () => target3(args5, "option id") : void 0
|
|
67112
|
+
)
|
|
67113
|
+
);
|
|
67114
|
+
var fieldSet = leaf(
|
|
67115
|
+
"shopify.change.field.set",
|
|
67116
|
+
{
|
|
67117
|
+
name: "set",
|
|
67118
|
+
description: 'Set a custom field (metafield) on a product, an option or a collection. The owner is product:<ref>, variant:<id>, collection:<ref> or a collection staged in this chat (its shopify_temp_ ref); a bare reference is a product. If the store defines the field, send its type. Example: baker shopify change field set product:the-snowboard --namespace custom --key care --type single_line_text_field --value "Wash cold".'
|
|
67119
|
+
},
|
|
67120
|
+
{
|
|
67121
|
+
owner: TARGET("product:<ref>, variant:<id> or collection:<ref>"),
|
|
67122
|
+
namespace: { type: "string", description: "Namespace, e.g. custom", required: false },
|
|
67123
|
+
key: { type: "string", description: "Key, e.g. care", required: false },
|
|
67124
|
+
type: {
|
|
67125
|
+
type: "string",
|
|
67126
|
+
description: "Shopify's type, e.g. single_line_text_field, rich_text_field, json",
|
|
67127
|
+
required: false
|
|
67128
|
+
},
|
|
67129
|
+
value: { type: "string", description: "The value, as Shopify takes it for that type", required: false },
|
|
67130
|
+
...JSON_ARGS
|
|
67131
|
+
},
|
|
67132
|
+
async (args5) => await stageFor(
|
|
67133
|
+
"shopify.metafield.set",
|
|
67134
|
+
args5,
|
|
67135
|
+
() => defined({ namespace: args5.namespace, key: args5.key, type: args5.type, value: args5.value }),
|
|
67136
|
+
() => target3(args5, "owner")
|
|
67137
|
+
)
|
|
67138
|
+
);
|
|
67139
|
+
var fieldRemove = leaf(
|
|
67140
|
+
"shopify.change.field.remove",
|
|
67141
|
+
{
|
|
67142
|
+
name: "remove",
|
|
67143
|
+
description: "Remove a custom field from a product, an option or a collection. Example: baker shopify change field remove product:the-snowboard custom.care"
|
|
67144
|
+
},
|
|
67145
|
+
{
|
|
67146
|
+
owner: TARGET("product:<ref>, variant:<id> or collection:<ref>"),
|
|
67147
|
+
field: TARGET("namespace.key, e.g. custom.care")
|
|
67148
|
+
},
|
|
67149
|
+
async (args5) => {
|
|
67150
|
+
const [owner, field] = positionals(args5);
|
|
67151
|
+
const [namespace, ...rest] = (field ?? "").split(".");
|
|
67152
|
+
if (!owner || !namespace || rest.length === 0) failValidation3("pass the owner and the field as namespace.key");
|
|
67153
|
+
await stage([{ kind: "shopify.metafield.delete", target: owner, payload: { namespace, key: rest.join(".") } }]);
|
|
67154
|
+
}
|
|
67155
|
+
);
|
|
67156
|
+
var collectionCreate = leaf(
|
|
67157
|
+
"shopify.change.collection.create",
|
|
67158
|
+
{
|
|
67159
|
+
name: "create",
|
|
67160
|
+
description: 'Create a collection of hand-picked products, shown on the online store (a second change, staged with it) unless --hidden. Example: baker shopify change collection create --title "Summer boards" --products snowboard-a,snowboard-b. Collections whose products join by rules are made in Shopify.'
|
|
67161
|
+
},
|
|
67162
|
+
{
|
|
67163
|
+
title: { type: "string", description: "Title", required: false },
|
|
67164
|
+
handle: {
|
|
67165
|
+
type: "string",
|
|
67166
|
+
description: "Its address (/collections/<handle>); default from the title",
|
|
67167
|
+
required: false
|
|
67168
|
+
},
|
|
67169
|
+
description: { type: "string", description: "Description, as HTML (no scripts or frames)", required: false },
|
|
67170
|
+
"seo-title": { type: "string", description: "Title shown in search results", required: false },
|
|
67171
|
+
"seo-description": { type: "string", description: "Description shown in search results", required: false },
|
|
67172
|
+
products: {
|
|
67173
|
+
type: "string",
|
|
67174
|
+
description: "Products, comma separated (ids, handles or links), in order",
|
|
67175
|
+
required: false
|
|
67176
|
+
},
|
|
67177
|
+
sort: {
|
|
67178
|
+
type: "string",
|
|
67179
|
+
description: "MANUAL | BEST_SELLING | ALPHA_ASC | ALPHA_DESC | PRICE_ASC | PRICE_DESC | CREATED | CREATED_DESC",
|
|
67180
|
+
required: false
|
|
67181
|
+
},
|
|
67182
|
+
hidden: { type: "boolean", description: "Don't show it on the online store yet", required: false },
|
|
67183
|
+
...JSON_ARGS
|
|
67184
|
+
},
|
|
67185
|
+
async (args5) => await stageFor(
|
|
67186
|
+
"shopify.collection.create",
|
|
67187
|
+
args5,
|
|
67188
|
+
() => defined({
|
|
67189
|
+
title: args5.title,
|
|
67190
|
+
handle: args5.handle,
|
|
67191
|
+
descriptionHtml: args5.description,
|
|
67192
|
+
seo: args5["seo-title"] !== void 0 || args5["seo-description"] !== void 0 ? defined({ title: args5["seo-title"], description: args5["seo-description"] }) : void 0,
|
|
67193
|
+
products: list(args5.products),
|
|
67194
|
+
sortOrder: typeof args5.sort === "string" ? args5.sort.toUpperCase() : void 0,
|
|
67195
|
+
onlineStore: args5.hidden === true ? false : void 0
|
|
67196
|
+
})
|
|
67197
|
+
)
|
|
67198
|
+
);
|
|
67199
|
+
var collectionUpdate = leaf(
|
|
67200
|
+
"shopify.change.collection.update",
|
|
67201
|
+
{
|
|
67202
|
+
name: "update",
|
|
67203
|
+
description: `Change a collection's title, description, SEO or sort order (its address stays: stage a redirect to move it). Example: baker shopify change collection update summer-boards --title "Summer"`
|
|
67204
|
+
},
|
|
67205
|
+
{
|
|
67206
|
+
collection: TARGET("Collection id, handle or link"),
|
|
67207
|
+
title: { type: "string", description: "New title", required: false },
|
|
67208
|
+
description: { type: "string", description: "New description, as HTML", required: false },
|
|
67209
|
+
"seo-title": { type: "string", description: "Title shown in search results", required: false },
|
|
67210
|
+
"seo-description": { type: "string", description: "Description shown in search results", required: false },
|
|
67211
|
+
sort: { type: "string", description: "Sort order (see collection create)", required: false },
|
|
67212
|
+
...JSON_ARGS
|
|
67213
|
+
},
|
|
67214
|
+
async (args5) => await stageFor(
|
|
67215
|
+
"shopify.collection.update",
|
|
67216
|
+
args5,
|
|
67217
|
+
() => defined({
|
|
67218
|
+
title: args5.title,
|
|
67219
|
+
descriptionHtml: args5.description,
|
|
67220
|
+
seo: args5["seo-title"] !== void 0 || args5["seo-description"] !== void 0 ? defined({ title: args5["seo-title"], description: args5["seo-description"] }) : void 0,
|
|
67221
|
+
sortOrder: typeof args5.sort === "string" ? args5.sort.toUpperCase() : void 0
|
|
67222
|
+
}),
|
|
67223
|
+
() => target3(args5, "collection")
|
|
67224
|
+
)
|
|
67225
|
+
);
|
|
67226
|
+
function collectionVerb(verb, kind, description2) {
|
|
67227
|
+
return leaf(
|
|
67228
|
+
`shopify.change.collection.${verb}`,
|
|
67229
|
+
{ name: verb, description: description2 },
|
|
67230
|
+
{ collection: TARGET("Collection id, handle, link or a shopify_temp_ ref staged in this chat") },
|
|
67231
|
+
async (args5) => await stage([{ kind, target: target3(args5, "collection") }])
|
|
67232
|
+
);
|
|
67233
|
+
}
|
|
67234
|
+
function membership(verb, kind, description2) {
|
|
67235
|
+
return leaf(
|
|
67236
|
+
`shopify.change.collection.${verb}`,
|
|
67237
|
+
{ name: verb, description: description2 },
|
|
67238
|
+
{
|
|
67239
|
+
collection: TARGET("Collection id, handle, link or a shopify_temp_ ref"),
|
|
67240
|
+
products: TARGET("Products (ids, handles or links), space separated")
|
|
67241
|
+
},
|
|
67242
|
+
async (args5) => {
|
|
67243
|
+
const [collection, ...products] = positionals(args5);
|
|
67244
|
+
if (!collection || products.length === 0) failValidation3("pass the collection, then the products");
|
|
67245
|
+
await stage([{ kind, target: collection, payload: { products } }]);
|
|
67246
|
+
}
|
|
67247
|
+
);
|
|
67248
|
+
}
|
|
67249
|
+
function discountValue(args5) {
|
|
67250
|
+
if (args5.percent !== void 0) return { percentage: Number(args5.percent) };
|
|
67251
|
+
if (args5.amount !== void 0) return { amount: args5.amount };
|
|
67252
|
+
return void 0;
|
|
67253
|
+
}
|
|
67254
|
+
function appliesToOf(args5) {
|
|
67255
|
+
const products = list(args5.products);
|
|
67256
|
+
if (products) return { products };
|
|
67257
|
+
const collections = list(args5.collections);
|
|
67258
|
+
return collections ? { collections } : { all: true };
|
|
67259
|
+
}
|
|
67260
|
+
function combinesOf(value) {
|
|
67261
|
+
const combines = list(value) ?? [];
|
|
67262
|
+
if (combines.length === 0) return void 0;
|
|
67263
|
+
return {
|
|
67264
|
+
productDiscounts: combines.includes("product"),
|
|
67265
|
+
orderDiscounts: combines.includes("order"),
|
|
67266
|
+
shippingDiscounts: combines.includes("shipping")
|
|
67267
|
+
};
|
|
67268
|
+
}
|
|
67269
|
+
function numberOf3(value) {
|
|
67270
|
+
return value === void 0 ? void 0 : Number(value);
|
|
67271
|
+
}
|
|
67272
|
+
function discountPayload(args5) {
|
|
67273
|
+
return defined({
|
|
67274
|
+
code: args5.code,
|
|
67275
|
+
title: args5.title,
|
|
67276
|
+
value: discountValue(args5),
|
|
67277
|
+
appliesTo: appliesToOf(args5),
|
|
67278
|
+
minimumSubtotal: args5["min-subtotal"],
|
|
67279
|
+
minimumQuantity: numberOf3(args5["min-quantity"]),
|
|
67280
|
+
startsAt: args5.starts,
|
|
67281
|
+
endsAt: args5.ends,
|
|
67282
|
+
usageLimit: numberOf3(args5.limit),
|
|
67283
|
+
oncePerCustomer: args5["any-times"] === true ? false : void 0,
|
|
67284
|
+
combinesWith: combinesOf(args5.combines)
|
|
67285
|
+
});
|
|
67286
|
+
}
|
|
67287
|
+
function signupOfferPayload(args5) {
|
|
67288
|
+
return defined({
|
|
67289
|
+
name: args5.name,
|
|
67290
|
+
value: discountValue(args5),
|
|
67291
|
+
appliesTo: appliesToOf(args5),
|
|
67292
|
+
minimumSubtotal: args5.minimum,
|
|
67293
|
+
validForDays: numberOf3(args5["valid-days"]),
|
|
67294
|
+
endsAt: args5.ends,
|
|
67295
|
+
maxCodes: numberOf3(args5["max-codes"]),
|
|
67296
|
+
codePrefix: args5.prefix,
|
|
67297
|
+
combinesWith: combinesOf(args5.combines)
|
|
67298
|
+
});
|
|
67299
|
+
}
|
|
67300
|
+
var signupOfferSet = leaf(
|
|
67301
|
+
"shopify.change.signupOffer.set",
|
|
67302
|
+
{
|
|
67303
|
+
name: "set",
|
|
67304
|
+
description: 'Stage a sign-up offer. Changes money: ask the exact approval question returned by this command. Example: baker shopify change signup-offer set welcome-10 --name "Welcome" --percent 10 --valid-days 14 --max-codes 1000'
|
|
67305
|
+
},
|
|
67306
|
+
{
|
|
67307
|
+
handle: TARGET("Offer handle, e.g. welcome-10"),
|
|
67308
|
+
name: { type: "string", description: "Offer name", required: false },
|
|
67309
|
+
percent: { type: "string", description: "Percentage off (choose percent or amount)", required: false },
|
|
67310
|
+
amount: { type: "string", description: "Amount off in the store currency", required: false },
|
|
67311
|
+
products: { type: "string", description: "Product handles or ids, comma separated", required: false },
|
|
67312
|
+
collections: { type: "string", description: "Collection handles or ids, comma separated", required: false },
|
|
67313
|
+
minimum: { type: "string", description: "Minimum subtotal", required: false },
|
|
67314
|
+
"valid-days": { type: "string", description: "Each code lasts 1\u2013365 days", required: false },
|
|
67315
|
+
ends: { type: "string", description: "Offer end as ISO date and time; no code outlives it", required: false },
|
|
67316
|
+
"max-codes": { type: "string", description: "Total codes the offer may give, 1\u2013100000", required: false },
|
|
67317
|
+
prefix: { type: "string", description: "Code prefix: 2\u201312 uppercase letters or digits", required: false },
|
|
67318
|
+
combines: {
|
|
67319
|
+
type: "string",
|
|
67320
|
+
description: "Comma-separated product,order,shipping discounts to combine",
|
|
67321
|
+
required: false
|
|
67322
|
+
},
|
|
67323
|
+
...JSON_ARGS
|
|
67324
|
+
},
|
|
67325
|
+
async (args5) => await stageFor(
|
|
67326
|
+
"shopify.signupOffer.set",
|
|
67327
|
+
args5,
|
|
67328
|
+
() => signupOfferPayload(args5),
|
|
67329
|
+
() => target3(args5, "offer handle")
|
|
67330
|
+
)
|
|
67331
|
+
);
|
|
67332
|
+
var signupOfferEnd = leaf(
|
|
67333
|
+
"shopify.change.signupOffer.end",
|
|
67334
|
+
{
|
|
67335
|
+
name: "end",
|
|
67336
|
+
description: "End a sign-up offer at Publish. Existing codes keep their terms. Example: baker shopify change signup-offer end welcome-10"
|
|
67337
|
+
},
|
|
67338
|
+
{ handle: TARGET("Offer handle") },
|
|
67339
|
+
async (args5) => await stage([{ kind: "shopify.signupOffer.end", target: target3(args5, "offer handle") }])
|
|
67340
|
+
);
|
|
67341
|
+
var discountCreate = leaf(
|
|
67342
|
+
"shopify.change.discount.create",
|
|
67343
|
+
{
|
|
67344
|
+
name: "create",
|
|
67345
|
+
description: "Create a discount code for everyone who has it. Changes money: the user must say yes in the chat first \u2014 the command hands you the exact question. Example: baker shopify change discount create --code SPRING10 --percent 10 --ends 2026-10-12T23:59:00Z --limit 500. Applies to everything unless --products or --collections; starts when published unless --starts."
|
|
67346
|
+
},
|
|
67347
|
+
{
|
|
67348
|
+
code: { type: "string", description: "The code shoppers type, e.g. SPRING10", required: false },
|
|
67349
|
+
title: { type: "string", description: "What the store's staff see (default: the code)", required: false },
|
|
67350
|
+
percent: { type: "string", description: "Percentage off, 1-100", required: false },
|
|
67351
|
+
amount: { type: "string", description: "Amount off, as a decimal in the store's currency", required: false },
|
|
67352
|
+
products: { type: "string", description: "Only these products, comma separated", required: false },
|
|
67353
|
+
collections: { type: "string", description: "Only these collections, comma separated", required: false },
|
|
67354
|
+
"min-subtotal": { type: "string", description: "Minimum order amount", required: false },
|
|
67355
|
+
"min-quantity": { type: "string", description: "Minimum number of items", required: false },
|
|
67356
|
+
starts: { type: "string", description: "Start, ISO date-time (default: when published)", required: false },
|
|
67357
|
+
ends: { type: "string", description: "End, ISO date-time (default: no end)", required: false },
|
|
67358
|
+
limit: { type: "string", description: "Total number of uses", required: false },
|
|
67359
|
+
"any-times": { type: "boolean", description: "A customer may use it more than once", required: false },
|
|
67360
|
+
combines: {
|
|
67361
|
+
type: "string",
|
|
67362
|
+
description: "What it combines with, comma separated: product, order, shipping",
|
|
67363
|
+
required: false
|
|
67364
|
+
},
|
|
67365
|
+
...JSON_ARGS
|
|
67366
|
+
},
|
|
67367
|
+
async (args5) => await stageFor("shopify.discountCode.create", args5, () => discountPayload(args5))
|
|
67368
|
+
);
|
|
67369
|
+
var discountStop = leaf(
|
|
67370
|
+
"shopify.change.discount.stop",
|
|
67371
|
+
{
|
|
67372
|
+
name: "stop",
|
|
67373
|
+
description: "Stop a discount code (it ends when published). Changes money: needs the user's yes. Undo puts its own dates back. Example: baker shopify change discount stop SPRING10"
|
|
67374
|
+
},
|
|
67375
|
+
{ code: TARGET("The discount code") },
|
|
67376
|
+
async (args5) => await stage([{ kind: "shopify.discountCode.deactivate", target: target3(args5, "code") }])
|
|
67377
|
+
);
|
|
67378
|
+
var discountDates = leaf(
|
|
67379
|
+
"shopify.change.discount.dates",
|
|
67380
|
+
{
|
|
67381
|
+
name: "dates",
|
|
67382
|
+
description: "Change when a discount code runs. Changes money: needs the user's yes. Example: baker shopify change discount dates SPRING10 --starts 2026-10-01T00:00:00Z --ends never"
|
|
67383
|
+
},
|
|
67384
|
+
{
|
|
67385
|
+
code: TARGET("The discount code"),
|
|
67386
|
+
starts: { type: "string", description: "Start, ISO date-time", required: false },
|
|
67387
|
+
ends: { type: "string", description: 'End, ISO date-time, or "never"', required: false }
|
|
67388
|
+
},
|
|
67389
|
+
async (args5) => {
|
|
67390
|
+
if (typeof args5.starts !== "string" || typeof args5.ends !== "string") failValidation3("pass --starts and --ends");
|
|
67391
|
+
await stage([
|
|
67392
|
+
{
|
|
67393
|
+
kind: "shopify.discountCode.schedule",
|
|
67394
|
+
target: target3(args5, "code"),
|
|
67395
|
+
payload: { startsAt: args5.starts, endsAt: args5.ends === "never" ? null : args5.ends }
|
|
67396
|
+
}
|
|
67397
|
+
]);
|
|
67398
|
+
}
|
|
67399
|
+
);
|
|
67400
|
+
var redirectCreate = leaf(
|
|
67401
|
+
"shopify.change.redirect.create",
|
|
67402
|
+
{
|
|
67403
|
+
name: "create",
|
|
67404
|
+
description: "Redirect an old address on the store to a new one (store paths, or a full https link). Example: baker shopify change redirect create --from /collections/old-boards --to /collections/summer-boards"
|
|
67405
|
+
},
|
|
67406
|
+
{
|
|
67407
|
+
from: { type: "string", description: "The old path, starting with /", required: false },
|
|
67408
|
+
to: { type: "string", description: "Where it sends shoppers: a path or an https link", required: false },
|
|
67409
|
+
...JSON_ARGS
|
|
67410
|
+
},
|
|
67411
|
+
async (args5) => await stageFor("shopify.urlRedirect.create", args5, () => defined({ from: args5.from, to: args5.to }))
|
|
67412
|
+
);
|
|
67413
|
+
var redirectRemove = leaf(
|
|
67414
|
+
"shopify.change.redirect.remove",
|
|
67415
|
+
{
|
|
67416
|
+
name: "remove",
|
|
67417
|
+
description: "Remove a redirect, by the path it redirects. Example: baker shopify change redirect remove /old"
|
|
67418
|
+
},
|
|
67419
|
+
{ from: TARGET("The redirected path") },
|
|
67420
|
+
async (args5) => await stage([{ kind: "shopify.urlRedirect.delete", target: target3(args5, "path") }])
|
|
67421
|
+
);
|
|
67422
|
+
var campaignAdd = leaf(
|
|
67423
|
+
"shopify.change.campaign.add",
|
|
67424
|
+
{
|
|
67425
|
+
name: "add",
|
|
67426
|
+
description: "Show a Baker ad campaign in the store's Shopify Marketing, so Shopify's reports credit its sales; Baker then sends its daily figures. Title, status, budget and spend come from the ad platform. Example: baker shopify change campaign add --platform google-ads --account 3865083115 --campaign 2101 --utm-campaign spring --utm-source google --utm-medium cpc"
|
|
67427
|
+
},
|
|
67428
|
+
{
|
|
67429
|
+
platform: { type: "string", description: "google-ads | meta", required: false },
|
|
67430
|
+
account: { type: "string", description: "The ad account id", required: false },
|
|
67431
|
+
campaign: { type: "string", description: "The campaign id", required: false },
|
|
67432
|
+
"utm-campaign": { type: "string", description: "utm_campaign its links carry", required: false },
|
|
67433
|
+
"utm-source": { type: "string", description: "utm_source", required: false },
|
|
67434
|
+
"utm-medium": { type: "string", description: "utm_medium", required: false }
|
|
67435
|
+
},
|
|
67436
|
+
async (args5) => await stage([
|
|
67437
|
+
{
|
|
67438
|
+
kind: "shopify.marketingActivity.upsert",
|
|
67439
|
+
payload: {
|
|
67440
|
+
platform: args5.platform,
|
|
67441
|
+
accountId: args5.account,
|
|
67442
|
+
campaignId: args5.campaign,
|
|
67443
|
+
utm: { campaign: args5["utm-campaign"], source: args5["utm-source"], medium: args5["utm-medium"] }
|
|
67444
|
+
}
|
|
67445
|
+
}
|
|
67446
|
+
])
|
|
67447
|
+
);
|
|
67448
|
+
var campaignRemove = leaf(
|
|
67449
|
+
"shopify.change.campaign.remove",
|
|
67450
|
+
{
|
|
67451
|
+
name: "remove",
|
|
67452
|
+
description: "Take a Baker campaign out of Shopify Marketing. Example: baker shopify change campaign remove google-ads:3865083115:2101"
|
|
67453
|
+
},
|
|
67454
|
+
{ campaign: TARGET("<platform>:<account>:<campaign>") },
|
|
67455
|
+
async (args5) => await stage([{ kind: "shopify.marketingActivity.delete", target: target3(args5, "campaign") }])
|
|
67456
|
+
);
|
|
67457
|
+
var measurementReconnect = leaf(
|
|
67458
|
+
"shopify.change.measurement.reconnect",
|
|
67459
|
+
{
|
|
67460
|
+
name: "reconnect",
|
|
67461
|
+
description: "Reconnect Baker's measurement on the store (the fix `baker shopify tracking audit` names when it is gone). Applies when published; Baker also repairs it on its own once a day."
|
|
67462
|
+
},
|
|
67463
|
+
{},
|
|
67464
|
+
async () => await stage([{ kind: "shopify.webPixel.update" }])
|
|
67465
|
+
);
|
|
67466
|
+
var group = (name, description2, subCommands) => defineCommand211({ meta: { name, description: description2 }, subCommands });
|
|
67467
|
+
var changeCommand = defineCommand211({
|
|
67468
|
+
meta: {
|
|
67469
|
+
name: "change",
|
|
67470
|
+
description: `Stage a change to the connected store. Nothing changes until the user publishes the chat; every
|
|
67471
|
+
change can be put back with \`baker shopify undo\`. Prices and discounts need the user's yes first.
|
|
67472
|
+
|
|
67473
|
+
baker shopify change product update <product> --title \u2026 --seo-title \u2026 --add-tags a,b
|
|
67474
|
+
baker shopify change variant update <variantId> --sku \u2026 --barcode \u2026 --price \u2026
|
|
67475
|
+
baker shopify change field set product:<product> --namespace custom --key \u2026 --type \u2026 --value \u2026
|
|
67476
|
+
baker shopify change collection create --title \u2026 --products a,b
|
|
67477
|
+
baker shopify change discount create --code SPRING10 --percent 10 --ends <iso>
|
|
67478
|
+
baker shopify change redirect create --from /old --to /new
|
|
67479
|
+
baker shopify change campaign add --platform google-ads --account \u2026 --campaign \u2026
|
|
67480
|
+
baker shopify change measurement reconnect`
|
|
67481
|
+
},
|
|
67482
|
+
subCommands: {
|
|
67483
|
+
"signup-offer": group("signup-offer", "Stage or end a sign-up offer", { set: signupOfferSet, end: signupOfferEnd }),
|
|
67484
|
+
product: group("product", "Change a product's title, description, SEO, tags or type", { update: productUpdate }),
|
|
67485
|
+
variant: group("variant", "Change an option's SKU, barcode, weight or price", { update: variantUpdate }),
|
|
67486
|
+
field: group("field", "Set or remove a custom field", { set: fieldSet, remove: fieldRemove }),
|
|
67487
|
+
collection: group("collection", "Create, change, show, hide, fill or delete a collection", {
|
|
67488
|
+
create: collectionCreate,
|
|
67489
|
+
update: collectionUpdate,
|
|
67490
|
+
show: collectionVerb(
|
|
67491
|
+
"show",
|
|
67492
|
+
"shopify.collection.publish",
|
|
67493
|
+
"Show a collection on the online store. Example: baker shopify change collection show summer-boards"
|
|
67494
|
+
),
|
|
67495
|
+
hide: collectionVerb(
|
|
67496
|
+
"hide",
|
|
67497
|
+
"shopify.collection.unpublish",
|
|
67498
|
+
"Hide a collection from the online store. Example: baker shopify change collection hide summer-boards"
|
|
67499
|
+
),
|
|
67500
|
+
delete: collectionVerb(
|
|
67501
|
+
"delete",
|
|
67502
|
+
"shopify.collection.delete",
|
|
67503
|
+
"Delete a collection \u2014 only one undo could rebuild exactly (hand-picked products, no image, no custom fields, no discount or menu pointing at it); refused otherwise with the reason. Stage a redirect for its address. Example: baker shopify change collection delete old-boards"
|
|
67504
|
+
),
|
|
67505
|
+
add: membership(
|
|
67506
|
+
"add",
|
|
67507
|
+
"shopify.collection.addProducts",
|
|
67508
|
+
"Add products to a collection. Example: baker shopify change collection add summer-boards snowboard-a snowboard-b"
|
|
67509
|
+
),
|
|
67510
|
+
remove: membership(
|
|
67511
|
+
"remove",
|
|
67512
|
+
"shopify.collection.removeProducts",
|
|
67513
|
+
"Remove products from a collection. Example: baker shopify change collection remove summer-boards snowboard-a"
|
|
67514
|
+
)
|
|
67515
|
+
}),
|
|
67516
|
+
discount: group("discount", "Create, stop or reschedule a discount code (needs the user's yes)", {
|
|
67517
|
+
create: discountCreate,
|
|
67518
|
+
stop: discountStop,
|
|
67519
|
+
dates: discountDates
|
|
67520
|
+
}),
|
|
67521
|
+
redirect: group("redirect", "Create or remove a redirect", { create: redirectCreate, remove: redirectRemove }),
|
|
67522
|
+
campaign: group("campaign", "Show or remove a Baker campaign in Shopify Marketing", {
|
|
67523
|
+
add: campaignAdd,
|
|
67524
|
+
remove: campaignRemove
|
|
67525
|
+
}),
|
|
67526
|
+
measurement: group("measurement", "Reconnect Baker's measurement on the store", {
|
|
67527
|
+
reconnect: measurementReconnect
|
|
67528
|
+
})
|
|
67529
|
+
}
|
|
67530
|
+
});
|
|
67531
|
+
|
|
67532
|
+
// src/commands/shopify/changes.ts
|
|
67533
|
+
var changesCommand2 = leaf(
|
|
67534
|
+
"shopify.changes",
|
|
67535
|
+
{
|
|
67536
|
+
name: "changes",
|
|
67537
|
+
description: "The changes Baker made to the store, newest first, each with its changeId and whether it can be undone. Example: baker shopify changes --limit 20 (or --chat <id> for one chat's)."
|
|
67538
|
+
},
|
|
67539
|
+
{
|
|
67540
|
+
limit: { type: "string", description: "How many, 1-100 (default 20)", required: false },
|
|
67541
|
+
chat: { type: "string", description: "Only one chat's changes", required: false }
|
|
67542
|
+
},
|
|
67543
|
+
async (args5) => {
|
|
67544
|
+
try {
|
|
67545
|
+
const data = await apiGet(
|
|
67546
|
+
"/api/shopify/changes",
|
|
67547
|
+
params({ limit: args5.limit, chatId: args5.chat })
|
|
67548
|
+
);
|
|
67549
|
+
writeJson({ ok: true, hints: changesHints(data), data });
|
|
67550
|
+
} catch (err) {
|
|
67551
|
+
failChange(err);
|
|
67552
|
+
}
|
|
67553
|
+
}
|
|
67554
|
+
);
|
|
67555
|
+
var undoCommand = leaf(
|
|
67556
|
+
"shopify.undo",
|
|
67557
|
+
{
|
|
67558
|
+
name: "undo",
|
|
67559
|
+
description: "Stage the change that puts an applied store change back, read from the store as it was before it \u2014 applied when the user publishes, like any change. Prices and discounts ask the user again. Refused when the store changed since. Example: baker shopify undo <changeId> [<changeId>\u2026]"
|
|
67560
|
+
},
|
|
67561
|
+
{ changeIds: { type: "positional", description: "changeIds from `baker shopify changes`", required: false } },
|
|
67562
|
+
async (args5) => {
|
|
67563
|
+
const changeIds = Array.isArray(args5._) ? args5._.filter((value) => typeof value === "string") : [];
|
|
67564
|
+
if (changeIds.length === 0) failValidation3("pass the changeIds to undo (see `baker shopify changes`)");
|
|
67565
|
+
try {
|
|
67566
|
+
const data = await apiPost("/api/shopify/undo", {
|
|
67567
|
+
chatId: requireChatId(),
|
|
67568
|
+
changeIds
|
|
67569
|
+
});
|
|
67570
|
+
emitStage(data);
|
|
67571
|
+
} catch (err) {
|
|
67572
|
+
failChange(err);
|
|
67573
|
+
}
|
|
67574
|
+
}
|
|
67575
|
+
);
|
|
67576
|
+
|
|
67577
|
+
// src/commands/shopify/draft.ts
|
|
67578
|
+
import { defineCommand as defineCommand212 } from "citty";
|
|
67579
|
+
function refOf(args5) {
|
|
67580
|
+
const [ref] = Array.isArray(args5._) ? args5._ : [];
|
|
67581
|
+
if (typeof ref !== "string" || ref === "")
|
|
67582
|
+
failValidation3("pass the staged change's ref (see `baker shopify draft list`)");
|
|
67583
|
+
return ref;
|
|
67584
|
+
}
|
|
67585
|
+
function listHints(data) {
|
|
67586
|
+
if (data.status === "none" || data.count === 0) return ["No store changes are staged on this chat."];
|
|
67587
|
+
if (data.status === "active") {
|
|
67588
|
+
return [
|
|
67589
|
+
"Staged only: these apply when the user publishes the chat. Correct one with `baker shopify draft amend <ref>`, drop one with `remove`."
|
|
67590
|
+
];
|
|
67591
|
+
}
|
|
67592
|
+
const failed = data.ops.filter((op) => op.result?.status === "failed" || op.result?.status === "skipped");
|
|
67593
|
+
return failed.length > 0 ? [`${failed.length} of ${data.count} changes did not go through \u2014 say which and why; never report them as made.`] : [];
|
|
67594
|
+
}
|
|
67595
|
+
var list2 = leaf(
|
|
67596
|
+
"shopify.draft.list",
|
|
67597
|
+
{
|
|
67598
|
+
name: "list",
|
|
67599
|
+
description: "Start here to review the store changes staged on this chat (or another chat's with --chat): what each is, whether it changes money, and after publish what happened to it."
|
|
67600
|
+
},
|
|
67601
|
+
{ chat: CHAT_READ_ARG },
|
|
67602
|
+
async (args5) => {
|
|
67603
|
+
try {
|
|
67604
|
+
const data = await apiPost("/api/shopify/draft/list", {
|
|
67605
|
+
chatId: resolveChatId(args5.chat)
|
|
67606
|
+
});
|
|
67607
|
+
writeJson({ ok: true, hints: listHints(data), data });
|
|
67608
|
+
} catch (err) {
|
|
67609
|
+
failChange(err);
|
|
67610
|
+
}
|
|
67611
|
+
}
|
|
67612
|
+
);
|
|
67613
|
+
var show = leaf(
|
|
67614
|
+
"shopify.draft.show",
|
|
67615
|
+
{
|
|
67616
|
+
name: "show",
|
|
67617
|
+
description: "One staged store change in full: what it sends and the store as it was read when it was staged (the before)."
|
|
67618
|
+
},
|
|
67619
|
+
{ ref: { type: "positional", description: "The staged change's ref", required: false }, chat: CHAT_READ_ARG },
|
|
67620
|
+
async (args5) => {
|
|
67621
|
+
try {
|
|
67622
|
+
const data = await apiPost("/api/shopify/draft/show", { chatId: resolveChatId(args5.chat), ref: refOf(args5) });
|
|
67623
|
+
writeJson({ ok: true, data });
|
|
67624
|
+
} catch (err) {
|
|
67625
|
+
failChange(err);
|
|
67626
|
+
}
|
|
67627
|
+
}
|
|
67628
|
+
);
|
|
67629
|
+
var amend = leaf(
|
|
67630
|
+
"shopify.draft.amend",
|
|
67631
|
+
{
|
|
67632
|
+
name: "amend",
|
|
67633
|
+
description: `Correct a staged change in place: a JSON patch merged into its payload (objects merge, null removes a key) and every check run again \u2014 a changed price asks the user again. Example: baker shopify draft amend product:123 --patch '{"title":"Better"}'. Keys are the payload's, not the flags': productType (not type), seo {title, description}, addTags, compareAtPrice; a key it doesn't take is refused with the ones it does.`
|
|
67634
|
+
},
|
|
67635
|
+
{
|
|
67636
|
+
ref: { type: "positional", description: "The staged change's ref", required: false },
|
|
67637
|
+
patch: { type: "string", description: "Inline JSON patch", required: false },
|
|
67638
|
+
file: { type: "string", description: "A JSON file with the patch", required: false }
|
|
67639
|
+
},
|
|
67640
|
+
async (args5) => {
|
|
67641
|
+
try {
|
|
67642
|
+
const data = await apiPost("/api/shopify/draft/amend", {
|
|
67643
|
+
chatId: requireChatId(),
|
|
67644
|
+
ref: refOf(args5),
|
|
67645
|
+
patch: loadJsonArg(args5, "patch")
|
|
67646
|
+
});
|
|
67647
|
+
emitStage(data);
|
|
67648
|
+
} catch (err) {
|
|
67649
|
+
failChange(err);
|
|
67650
|
+
}
|
|
67651
|
+
}
|
|
67652
|
+
);
|
|
67653
|
+
var remove = leaf(
|
|
67654
|
+
"shopify.draft.remove",
|
|
67655
|
+
{
|
|
67656
|
+
name: "remove",
|
|
67657
|
+
description: "Drop one staged store change, and any change that needs it (a new collection's show)."
|
|
67658
|
+
},
|
|
67659
|
+
{ ref: { type: "positional", description: "The staged change's ref", required: false } },
|
|
67660
|
+
async (args5) => {
|
|
67661
|
+
try {
|
|
67662
|
+
writeJson({
|
|
67663
|
+
ok: true,
|
|
67664
|
+
data: await apiPost("/api/shopify/draft/remove", { chatId: requireChatId(), ref: refOf(args5) })
|
|
67665
|
+
});
|
|
67666
|
+
} catch (err) {
|
|
67667
|
+
failChange(err);
|
|
67668
|
+
}
|
|
67669
|
+
}
|
|
67670
|
+
);
|
|
67671
|
+
var clear = leaf(
|
|
67672
|
+
"shopify.draft.clear",
|
|
67673
|
+
{ name: "clear", description: "Drop every store change staged on this chat." },
|
|
67674
|
+
{},
|
|
67675
|
+
async () => {
|
|
67676
|
+
try {
|
|
67677
|
+
writeJson({ ok: true, data: await apiPost("/api/shopify/draft/clear", { chatId: requireChatId() }) });
|
|
67678
|
+
} catch (err) {
|
|
67679
|
+
failChange(err);
|
|
67680
|
+
}
|
|
67681
|
+
}
|
|
67682
|
+
);
|
|
67683
|
+
var draftCommand5 = defineCommand212({
|
|
67684
|
+
meta: { name: "draft", description: "List, show, amend, remove or clear the store changes staged on this chat" },
|
|
67685
|
+
subCommands: { list: list2, show, amend, remove, clear }
|
|
67686
|
+
});
|
|
67687
|
+
|
|
67688
|
+
// src/commands/shopify/promotions.ts
|
|
67689
|
+
import { defineCommand as defineCommand213 } from "citty";
|
|
67690
|
+
|
|
66423
67691
|
// src/commands/shopify/landingLinks.ts
|
|
66424
67692
|
import { readdir as readdir16, readFile as readFile33, stat as stat11 } from "fs/promises";
|
|
66425
67693
|
import path47 from "path";
|
|
67694
|
+
import { parse } from "@astrojs/compiler/sync";
|
|
66426
67695
|
var PAGES_MAX = 300;
|
|
66427
67696
|
var FILE_BYTES_MAX = 256 * 1024;
|
|
66428
67697
|
function escaped(text2) {
|
|
66429
67698
|
return text2.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
66430
67699
|
}
|
|
66431
|
-
|
|
67700
|
+
var BUY_COMPONENTS = /* @__PURE__ */ new Set(["ShopifyProduct", "ShopifyBuyButton"]);
|
|
67701
|
+
var STRING_LITERAL = /^\s*(["'`])([^"'`]*)\1\s*$/;
|
|
67702
|
+
var ITEM_PRODUCT = /\bproduct\s*:\s*(["'`])([^"'`]*)\1/g;
|
|
67703
|
+
function namedBy(attribute) {
|
|
67704
|
+
if (attribute.name === "product") {
|
|
67705
|
+
if (attribute.kind === "quoted" || attribute.kind === "template-literal") return [attribute.value];
|
|
67706
|
+
if (attribute.kind === "expression") return [STRING_LITERAL.exec(attribute.value)?.[2] ?? ""];
|
|
67707
|
+
}
|
|
67708
|
+
if (attribute.name === "items" && attribute.kind === "expression") {
|
|
67709
|
+
return [...attribute.value.matchAll(ITEM_PRODUCT)].map((match) => match[2] ?? "");
|
|
67710
|
+
}
|
|
67711
|
+
return [];
|
|
67712
|
+
}
|
|
67713
|
+
function componentProducts(node, into) {
|
|
67714
|
+
if (node.type === "component" && BUY_COMPONENTS.has(node.name)) {
|
|
67715
|
+
for (const attribute of node.attributes) into.push(...namedBy(attribute));
|
|
67716
|
+
}
|
|
67717
|
+
if ("children" in node) for (const child of node.children) componentProducts(child, into);
|
|
67718
|
+
return into;
|
|
67719
|
+
}
|
|
67720
|
+
function buyComponentProducts(file) {
|
|
67721
|
+
if (!file.path.endsWith(".astro")) return [];
|
|
67722
|
+
try {
|
|
67723
|
+
return componentProducts(parse(file.text, void 0).ast, []);
|
|
67724
|
+
} catch {
|
|
67725
|
+
return [];
|
|
67726
|
+
}
|
|
67727
|
+
}
|
|
67728
|
+
function namesProduct(file, keys) {
|
|
67729
|
+
const wanted = new Set([keys.handle.toLowerCase(), ...keys.productId ? [keys.productId] : []].filter(Boolean));
|
|
67730
|
+
return buyComponentProducts(file).some((named) => wanted.has(named.trim().toLowerCase()));
|
|
67731
|
+
}
|
|
67732
|
+
function evidenceIn(file, keys) {
|
|
67733
|
+
const { text: text2 } = file;
|
|
66432
67734
|
const evidence = [];
|
|
66433
67735
|
if (keys.buyUrls.some((url) => url && text2.includes(url))) evidence.push("buy_link");
|
|
66434
67736
|
const page = new RegExp(`/products/${escaped(keys.handle)}(?![A-Za-z0-9_-])`, "i");
|
|
@@ -66437,24 +67739,25 @@ function evidenceIn(text2, keys) {
|
|
|
66437
67739
|
(id) => new RegExp(`(?:/cart/(?:\\d+:\\d+,)*${id}:|variant=${id}(?!\\d))`).test(text2)
|
|
66438
67740
|
);
|
|
66439
67741
|
if (option) evidence.push("variant_id");
|
|
67742
|
+
if (namesProduct(file, keys)) evidence.push("buy_button");
|
|
66440
67743
|
return evidence;
|
|
66441
67744
|
}
|
|
66442
67745
|
function landingLinksOf(pages, keys) {
|
|
66443
67746
|
const bySlug = /* @__PURE__ */ new Map();
|
|
66444
67747
|
for (const page of pages) {
|
|
66445
|
-
const found = new Set(page.files.flatMap((file) => evidenceIn(file
|
|
67748
|
+
const found = new Set(page.files.flatMap((file) => evidenceIn(file, keys)));
|
|
66446
67749
|
if (found.size === 0) continue;
|
|
66447
67750
|
const slug = page.slug.replace(/--\d+$/, "");
|
|
66448
67751
|
const held = bySlug.get(slug) ?? /* @__PURE__ */ new Set();
|
|
66449
67752
|
for (const evidence of found) held.add(evidence);
|
|
66450
67753
|
bySlug.set(slug, held);
|
|
66451
67754
|
}
|
|
66452
|
-
const order = ["buy_link", "product_page", "variant_id"];
|
|
67755
|
+
const order = ["buy_link", "product_page", "variant_id", "buy_button"];
|
|
66453
67756
|
return [...bySlug].map(([slug, evidence]) => ({ slug, evidence: order.filter((entry) => evidence.has(entry)) }));
|
|
66454
67757
|
}
|
|
66455
|
-
async function isDir4(
|
|
67758
|
+
async function isDir4(target4) {
|
|
66456
67759
|
try {
|
|
66457
|
-
return (await stat11(
|
|
67760
|
+
return (await stat11(target4)).isDirectory();
|
|
66458
67761
|
} catch {
|
|
66459
67762
|
return false;
|
|
66460
67763
|
}
|
|
@@ -66492,15 +67795,17 @@ function plural(count2, one, many = `${one}s`) {
|
|
|
66492
67795
|
}
|
|
66493
67796
|
function pauseCommand2(ad) {
|
|
66494
67797
|
if (ad.platform === "meta") return `\`baker ads meta ads pause ${ad.id ?? "<id>"}\``;
|
|
66495
|
-
const
|
|
66496
|
-
return ad.level === "asset_group" ? `\`baker ads google asset-groups update ${
|
|
67798
|
+
const target4 = `${ad.resource ?? "<resource>"} --customer-id ${ad.account_id ?? "<account_id>"}`;
|
|
67799
|
+
return ad.level === "asset_group" ? `\`baker ads google asset-groups update ${target4} --status PAUSED\`` : `\`baker ads google ads pause ${target4}\``;
|
|
66497
67800
|
}
|
|
66498
67801
|
function pauseHint(answer) {
|
|
67802
|
+
if (answer.product.stock === "in_stock" || answer.ads.length === 0) return null;
|
|
66499
67803
|
const active = answer.ads.filter((ad) => ad.status === "active");
|
|
66500
|
-
if (
|
|
67804
|
+
if (active.length === 0) {
|
|
67805
|
+
return `Every ad that sends people to \u201C${answer.product.title}\u201D is already paused: stage nothing for it. Tell the user this run paused nothing, and promise nothing about these ads coming back when it is back in stock.`;
|
|
67806
|
+
}
|
|
66501
67807
|
const one = active.length === 1;
|
|
66502
|
-
|
|
66503
|
-
return `${plural(active.length, "active ad")} still ${one ? "sends" : "send"} people to \u201C${answer.product.title}\u201D, which is out of stock. Stage pausing ${one ? "it" : "them"}: ${active.map(pauseCommand2).join("; ")}.${remembered}`;
|
|
67808
|
+
return `${plural(active.length, "active ad")} still ${one ? "sends" : "send"} people to \u201C${answer.product.title}\u201D, which is out of stock. Stage pausing ${one ? "it" : "them"}: ${active.map(pauseCommand2).join("; ")}. Each pause waits for the user to review and publish, and nothing turns it back on by itself.`;
|
|
66504
67809
|
}
|
|
66505
67810
|
function promotionHints(answer, scan) {
|
|
66506
67811
|
const hints2 = [];
|
|
@@ -66520,7 +67825,10 @@ function promotionHints(answer, scan) {
|
|
|
66520
67825
|
hints2.push(`/${landing.slug} links to this product but is not live; no ad can send people to it yet.`);
|
|
66521
67826
|
}
|
|
66522
67827
|
if (answer.ads.length === 0 && answer.landings.length === 0 && answer.shopping.length === 0) {
|
|
66523
|
-
|
|
67828
|
+
const nothing = `Nothing advertises \u201C${answer.product.title}\u201D that Baker can see`;
|
|
67829
|
+
hints2.push(
|
|
67830
|
+
answer.product.stock === "in_stock" ? `${nothing}.` : `${nothing}: stage nothing for it. Tell the user this run paused nothing for it, and promise nothing about ads coming back when it is back in stock.`
|
|
67831
|
+
);
|
|
66524
67832
|
}
|
|
66525
67833
|
if (!scan.scanned) {
|
|
66526
67834
|
hints2.push(
|
|
@@ -66529,15 +67837,25 @@ function promotionHints(answer, scan) {
|
|
|
66529
67837
|
} else if (scan.skippedPages > 0) {
|
|
66530
67838
|
hints2.push(`${plural(scan.skippedPages, "page")} not checked for links to this product (past the 300-page bound).`);
|
|
66531
67839
|
}
|
|
66532
|
-
if (!answer.remembered) {
|
|
67840
|
+
if (!answer.remembered && answer.ads.length > 0) {
|
|
66533
67841
|
hints2.push(
|
|
66534
|
-
"This read was
|
|
67842
|
+
"This read was made outside a Session (no chat): a pause staged from it will never be offered back for restarting when the product returns."
|
|
66535
67843
|
);
|
|
66536
67844
|
}
|
|
66537
67845
|
return hints2;
|
|
66538
67846
|
}
|
|
66539
|
-
|
|
66540
|
-
|
|
67847
|
+
|
|
67848
|
+
// src/commands/shopify/stockHoldReport.ts
|
|
67849
|
+
var PLATFORM2 = { "google-ads": "Google Ads", meta: "Meta" };
|
|
67850
|
+
var LEVEL = {
|
|
67851
|
+
campaign: "campaign",
|
|
67852
|
+
ad_group: "ad group",
|
|
67853
|
+
ad: "ad",
|
|
67854
|
+
asset_group: "asset group",
|
|
67855
|
+
adset: "ad set"
|
|
67856
|
+
};
|
|
67857
|
+
function adName(ad) {
|
|
67858
|
+
return `${PLATFORM2[ad.platform]} ${LEVEL[ad.level]} ${ad.name ? `\u201C${ad.name}\u201D` : ad.entity_id}`;
|
|
66541
67859
|
}
|
|
66542
67860
|
var GOOGLE_GROUP = {
|
|
66543
67861
|
campaign: "campaigns",
|
|
@@ -66546,10 +67864,6 @@ var GOOGLE_GROUP = {
|
|
|
66546
67864
|
asset_group: "asset-groups",
|
|
66547
67865
|
adset: "ads"
|
|
66548
67866
|
};
|
|
66549
|
-
function googleResume(hold) {
|
|
66550
|
-
const target2 = `${hold.entity_id} --customer-id ${hold.account_id}`;
|
|
66551
|
-
return hold.level === "asset_group" ? `baker ads google asset-groups update ${target2} --status ENABLED` : `baker ads google ${GOOGLE_GROUP[hold.level]} resume ${target2}`;
|
|
66552
|
-
}
|
|
66553
67867
|
var META_GROUP = {
|
|
66554
67868
|
campaign: "campaigns",
|
|
66555
67869
|
ad_group: "adsets",
|
|
@@ -66558,46 +67872,92 @@ var META_GROUP = {
|
|
|
66558
67872
|
adset: "adsets"
|
|
66559
67873
|
};
|
|
66560
67874
|
function resumeCommand2(hold) {
|
|
66561
|
-
|
|
67875
|
+
if (hold.platform === "meta") return `baker ads meta ${META_GROUP[hold.level]} resume ${hold.entity_id}`;
|
|
67876
|
+
const target4 = `${hold.entity_id} --customer-id ${hold.account_id}`;
|
|
67877
|
+
return hold.level === "asset_group" ? `baker ads google asset-groups update ${target4} --status ENABLED` : `baker ads google ${GOOGLE_GROUP[hold.level]} resume ${target4}`;
|
|
67878
|
+
}
|
|
67879
|
+
function quoted(title, fallback) {
|
|
67880
|
+
return title ? `\u201C${title}\u201D` : fallback;
|
|
67881
|
+
}
|
|
67882
|
+
function heldLine(hold) {
|
|
67883
|
+
const name = adName(hold);
|
|
67884
|
+
const product = quoted(hold.product.title, "the product");
|
|
67885
|
+
switch (hold.verdict) {
|
|
67886
|
+
case "resumable":
|
|
67887
|
+
return `Ready to turn back on when you publish: ${name} \u2014 Baker paused it because ${product} ran out.`;
|
|
67888
|
+
case "still_out_of_stock":
|
|
67889
|
+
return `Stays paused: ${name} \u2014 ${product} still cannot be bought.`;
|
|
67890
|
+
case "changed_since":
|
|
67891
|
+
return `Left as it is: ${name} \u2014 someone changed it after Baker paused it when ${product} ran out, so check it yourself.`;
|
|
67892
|
+
case "still_promotes_out_of_stock":
|
|
67893
|
+
return `Stays paused: ${name} \u2014 it also sends people to ${quoted(hold.verdict_detail, "another product")}, which is still out of stock.`;
|
|
67894
|
+
case "cannot_confirm":
|
|
67895
|
+
return `Left as it is: ${name} \u2014 Baker cannot tell whether someone changed it after pausing it when ${product} ran out, so check it before turning it back on.`;
|
|
67896
|
+
case "already_active":
|
|
67897
|
+
return `Nothing to do: ${name} is already running again.`;
|
|
67898
|
+
default:
|
|
67899
|
+
return null;
|
|
67900
|
+
}
|
|
66562
67901
|
}
|
|
66563
|
-
|
|
66564
|
-
|
|
66565
|
-
|
|
66566
|
-
|
|
66567
|
-
|
|
66568
|
-
|
|
66569
|
-
|
|
66570
|
-
|
|
66571
|
-
|
|
66572
|
-
|
|
67902
|
+
var CLOSED_HOW = {
|
|
67903
|
+
resumed_by_back_in_stock_run: "turned back on by an earlier back-in-stock run",
|
|
67904
|
+
changed_by_baker: "turned back on by other work published in Baker",
|
|
67905
|
+
already_active: "found running again, turned back on by someone",
|
|
67906
|
+
removed: "removed",
|
|
67907
|
+
product_removed: "let go when the product left the store"
|
|
67908
|
+
};
|
|
67909
|
+
function plural2(count2, one) {
|
|
67910
|
+
return `${count2} ${count2 === 1 ? one : `${one}s`}`;
|
|
67911
|
+
}
|
|
67912
|
+
function nothingHeldLines(answer, asked, product) {
|
|
67913
|
+
if (!asked.product) {
|
|
67914
|
+
return answer.holds.length > 0 ? ["None of these is still paused because its product ran out, so there is nothing to turn back on."] : ["Baker has no ad paused because a product ran out, so there is nothing to turn back on."];
|
|
66573
67915
|
}
|
|
66574
|
-
|
|
66575
|
-
|
|
66576
|
-
|
|
66577
|
-
|
|
66578
|
-
hints2.push(
|
|
66579
|
-
`${holdName(hold)} still sends people to \u201C${hold.verdict_detail ?? "another product"}\u201D, which is out: leave it until that is back.`
|
|
66580
|
-
);
|
|
66581
|
-
} else if (hold.verdict === "cannot_confirm") {
|
|
66582
|
-
hints2.push(
|
|
66583
|
-
`${holdName(hold)} could not be confirmed as untouched since Baker paused it: leave it, and tell the user to check it.`
|
|
66584
|
-
);
|
|
66585
|
-
} else if (hold.verdict === "already_active") {
|
|
66586
|
-
hints2.push(`${holdName(hold)} is already running again: nothing to do.`);
|
|
66587
|
-
}
|
|
67916
|
+
if (answer.pending_sessions.length > 0) {
|
|
67917
|
+
return [
|
|
67918
|
+
`Baker's pause for ${product} is still waiting for your review and was never published, so there is nothing to turn back on.`
|
|
67919
|
+
];
|
|
66588
67920
|
}
|
|
66589
|
-
|
|
66590
|
-
|
|
66591
|
-
`
|
|
67921
|
+
if (answer.recently_closed.length > 0) {
|
|
67922
|
+
return answer.recently_closed.map(
|
|
67923
|
+
(hold) => `Already handled: Baker paused ${adName(hold)} when ${product} ran out, and it was later ${CLOSED_HOW[hold.resolved_reason]}.`
|
|
66592
67924
|
);
|
|
66593
67925
|
}
|
|
66594
|
-
return
|
|
67926
|
+
return [`Baker has nothing paused because ${product} ran out, so there is nothing to turn back on.`];
|
|
67927
|
+
}
|
|
67928
|
+
function stockHoldReport(answer, asked) {
|
|
67929
|
+
const product = quoted(answer.product?.title, "the product that came back");
|
|
67930
|
+
const held = answer.holds.filter((hold) => hold.state === "held");
|
|
67931
|
+
const tell_user = [
|
|
67932
|
+
...held.length === 0 ? nothingHeldLines(answer, asked, product) : [],
|
|
67933
|
+
...held.flatMap((hold) => heldLine(hold) ?? []),
|
|
67934
|
+
...answer.not_paused.map((ad) => `Left as it is: ${adName(ad)} \u2014 Baker did not pause it when ${product} ran out.`),
|
|
67935
|
+
...answer.pending_sessions.map(
|
|
67936
|
+
(pending) => `Discard the work waiting for your review, \u201C${pending.title}\u201D: it would pause ${plural2(pending.staged_pauses, "ad")} for ${product}, which can be bought again.`
|
|
67937
|
+
)
|
|
67938
|
+
];
|
|
67939
|
+
const resume = held.filter((hold) => hold.verdict === "resumable").map((hold) => ({ ad: adName(hold), command: resumeCommand2(hold) }));
|
|
67940
|
+
const pages = answer.pages_changed.map((run) => ({
|
|
67941
|
+
pages: run.pages.map((slug) => `/${slug}`),
|
|
67942
|
+
read: `baker chats diff ${run.chat_id} --surface landings --full`
|
|
67943
|
+
}));
|
|
67944
|
+
const hints2 = [
|
|
67945
|
+
resume.length > 0 ? "Stage each `resume` command, and nothing else." : `Stage nothing${asked.product ? ` for ${product}` : ""}.`,
|
|
67946
|
+
"Tell the user the `tell_user` lines as they are written, and nothing about any other ad: an ad this read does not name is not this run's to explain.",
|
|
67947
|
+
...pages.map(
|
|
67948
|
+
(run) => `The out-of-stock run also changed ${run.pages.join(", ")}: read those changes with its \`read\` command, and put back only what it changed for ${product}.`
|
|
67949
|
+
)
|
|
67950
|
+
];
|
|
67951
|
+
const compact3 = { tell_user, resume, ...pages.length > 0 ? { pages } : {} };
|
|
67952
|
+
if (!asked.full) return { data: compact3, hints: hints2 };
|
|
67953
|
+
const { store: _store, ...everything } = answer;
|
|
67954
|
+
return { data: { ...compact3, ...everything }, hints: hints2 };
|
|
66595
67955
|
}
|
|
66596
67956
|
|
|
66597
67957
|
// src/commands/shopify/promotions.ts
|
|
66598
67958
|
registerSchema({
|
|
66599
67959
|
command: "shopify.promotions",
|
|
66600
|
-
description: "Start here when a product runs out of stock or comes back: what is advertising it \u2014 ads on Google Ads and Meta that lead to its store page, its buy link or a landing that links to it (`via`), the landings themselves (from this workspace's pages, and from measured visits), and the Shopping campaigns and catalog ads that show it (they stop by themselves when it runs out \u2014 never pause them). Read it in the Session that pauses: a pause is remembered \
|
|
67960
|
+
description: "Start here when a product runs out of stock or comes back: what is advertising it \u2014 ads on Google Ads and Meta that lead to its store page, its buy link or a landing that links to it (`via`), the landings themselves (from this workspace's pages, and from measured visits), and the Shopping campaigns and catalog ads that show it (they stop by themselves when it runs out \u2014 never pause them). Read it in the Session that pauses: a pause is remembered only for a product whose promotions that Session read, so that \u201CRestart ads when a product is back\u201D, if the company has it, can later stage turning it back on for the user to review \u2014 nothing restarts by itself, and every pause waits for the user to review and publish. `checked.not_checked` names a platform Baker could not read.",
|
|
66601
67961
|
args: {
|
|
66602
67962
|
product: { type: "positional", description: "Product id, handle, or a link to the product", required: true },
|
|
66603
67963
|
full: {
|
|
@@ -66608,14 +67968,19 @@ registerSchema({
|
|
|
66608
67968
|
});
|
|
66609
67969
|
registerSchema({
|
|
66610
67970
|
command: "shopify.stock-holds",
|
|
66611
|
-
description: "Start here when a product is back in stock:
|
|
67971
|
+
description: "Start here when a product is back in stock: what Baker paused because it ran out, answered for the marketer. `resume` holds the one command to stage per ad Baker may turn back on now \u2014 stage those and nothing else. `tell_user` holds one sentence per ad (and per piece of out-of-stock work still waiting for review), written for the marketer: tell them those, as written. `pages` names landings the out-of-stock run changed and the read that shows those page changes. Use `--product` for the product that came back: the answer then speaks only of its ads. `--full` adds the codes behind each sentence (verdicts, ids) for debugging, never for the user.",
|
|
66612
67972
|
args: {
|
|
66613
67973
|
product: { type: "string", description: "Only one product's holds: its id, handle or link", required: false },
|
|
66614
67974
|
all: { type: "boolean", description: "Resolved holds too (resumed or released)", required: false, default: false },
|
|
66615
67975
|
full: fullArg
|
|
66616
67976
|
}
|
|
66617
67977
|
});
|
|
66618
|
-
|
|
67978
|
+
function promotionsShown(answer, full) {
|
|
67979
|
+
if (full) return answer;
|
|
67980
|
+
const { remembered: _remembered, ...shown } = answer;
|
|
67981
|
+
return shown;
|
|
67982
|
+
}
|
|
67983
|
+
var promotionsCommand = defineCommand213({
|
|
66619
67984
|
meta: {
|
|
66620
67985
|
name: "promotions",
|
|
66621
67986
|
description: "What advertises one product: ads that lead to it, landings that link to it, Shopping that shows it. Example: baker shopify promotions 10310413942969"
|
|
@@ -66631,8 +67996,8 @@ var promotionsCommand = defineCommand210({
|
|
|
66631
67996
|
},
|
|
66632
67997
|
run: async ({ args: args5 }) => {
|
|
66633
67998
|
try {
|
|
66634
|
-
const
|
|
66635
|
-
if (
|
|
67999
|
+
const positionals2 = Array.isArray(args5._) ? args5._ : [];
|
|
68000
|
+
if (positionals2.length > 1) {
|
|
66636
68001
|
throw new ApiError("VALIDATION_ERROR", "product: Pass one product id, handle or product link.");
|
|
66637
68002
|
}
|
|
66638
68003
|
const { product, store } = await apiGet(
|
|
@@ -66642,6 +68007,7 @@ var promotionsCommand = defineCommand210({
|
|
|
66642
68007
|
const scan = await readWorkspacePages(process.cwd());
|
|
66643
68008
|
const linked = scan ? landingLinksOf(scan.pages, {
|
|
66644
68009
|
handle: product.handle,
|
|
68010
|
+
productId: product.productId,
|
|
66645
68011
|
variantIds: product.variants.map((variant) => variant.variantId),
|
|
66646
68012
|
buyUrls: product.variants.flatMap((variant) => variant.buyUrl ? [variant.buyUrl] : [])
|
|
66647
68013
|
}) : void 0;
|
|
@@ -66651,7 +68017,7 @@ var promotionsCommand = defineCommand210({
|
|
|
66651
68017
|
scanned: scan !== null,
|
|
66652
68018
|
...args5.full ? { full: true } : {}
|
|
66653
68019
|
});
|
|
66654
|
-
emit2(answer, { store }, [
|
|
68020
|
+
emit2(promotionsShown(answer, args5.full === true), { store }, [
|
|
66655
68021
|
...storeStateHints(store),
|
|
66656
68022
|
...promotionHints(answer, { scanned: scan !== null, skippedPages: scan?.skipped ?? 0 })
|
|
66657
68023
|
]);
|
|
@@ -66660,7 +68026,7 @@ var promotionsCommand = defineCommand210({
|
|
|
66660
68026
|
}
|
|
66661
68027
|
}
|
|
66662
68028
|
});
|
|
66663
|
-
var stockHoldsCommand =
|
|
68029
|
+
var stockHoldsCommand = defineCommand213({
|
|
66664
68030
|
meta: {
|
|
66665
68031
|
name: "stock-holds",
|
|
66666
68032
|
description: "The ads Baker paused because a product ran out, and whether each may be resumed now. Example: baker shopify stock-holds --product 10310413942969"
|
|
@@ -66676,18 +68042,130 @@ var stockHoldsCommand = defineCommand210({
|
|
|
66676
68042
|
"/api/shopify/stock-holds",
|
|
66677
68043
|
params({ product: args5.product, all: args5.all, full: args5.full })
|
|
66678
68044
|
);
|
|
66679
|
-
const {
|
|
66680
|
-
emit2(data, { store, count: answer.holds.length }, [
|
|
68045
|
+
const report = stockHoldReport(answer, { product: args5.product !== void 0, full: args5.full === true });
|
|
68046
|
+
emit2(report.data, { store: answer.store, count: answer.holds.length }, [
|
|
68047
|
+
...storeStateHints(answer.store),
|
|
68048
|
+
...report.hints
|
|
68049
|
+
]);
|
|
68050
|
+
} catch (err) {
|
|
68051
|
+
fail9(err);
|
|
68052
|
+
}
|
|
68053
|
+
}
|
|
68054
|
+
});
|
|
68055
|
+
|
|
68056
|
+
// ../api/src/shopify/signups.ts
|
|
68057
|
+
import { z as z53 } from "zod";
|
|
68058
|
+
var SIGNUP_CODE_PATTERN = /^[A-Z0-9]{2,12}-[0-9A-HJKMNP-TV-Z]{8}$/;
|
|
68059
|
+
var SIGNUP_STATES = [
|
|
68060
|
+
"waiting",
|
|
68061
|
+
"ready",
|
|
68062
|
+
"used",
|
|
68063
|
+
"expired",
|
|
68064
|
+
"already",
|
|
68065
|
+
"ended",
|
|
68066
|
+
"none",
|
|
68067
|
+
"unavailable"
|
|
68068
|
+
];
|
|
68069
|
+
var CODE_STATES = /* @__PURE__ */ new Set(["ready", "used", "expired"]);
|
|
68070
|
+
var shopifySignupReplySchema = z53.object({
|
|
68071
|
+
state: z53.enum(SIGNUP_STATES),
|
|
68072
|
+
code: z53.string().regex(SIGNUP_CODE_PATTERN).optional(),
|
|
68073
|
+
endsAt: z53.string().min(1).max(40).optional()
|
|
68074
|
+
}).strict().refine(
|
|
68075
|
+
(reply) => CODE_STATES.has(reply.state) ? reply.code !== void 0 && reply.endsAt !== void 0 : true,
|
|
68076
|
+
"a code state carries the code and when it ends"
|
|
68077
|
+
).refine(
|
|
68078
|
+
(reply) => CODE_STATES.has(reply.state) ? true : reply.code === void 0 && reply.endsAt === void 0,
|
|
68079
|
+
"only the browser the code was given to is told it"
|
|
68080
|
+
);
|
|
68081
|
+
var shopifySignupResponseSchema = z53.union([
|
|
68082
|
+
z53.object({ ok: z53.literal(true), signup: shopifySignupReplySchema }).passthrough(),
|
|
68083
|
+
z53.object({ error: z53.string(), signup: shopifySignupReplySchema }).passthrough()
|
|
68084
|
+
]);
|
|
68085
|
+
var signupOfferStatusSchema = z53.enum(["active", "ended", "staged"]);
|
|
68086
|
+
var shopifySignupOfferViewSchema = z53.object({
|
|
68087
|
+
handle: z53.string(),
|
|
68088
|
+
name: z53.string(),
|
|
68089
|
+
/** "10% off everything, each code valid 14 days, at most 1,000 codes". */
|
|
68090
|
+
summary: z53.string(),
|
|
68091
|
+
status: signupOfferStatusSchema,
|
|
68092
|
+
validForDays: z53.number(),
|
|
68093
|
+
endsAt: z53.string().optional(),
|
|
68094
|
+
maxCodes: z53.number().optional(),
|
|
68095
|
+
codesGiven: z53.number()
|
|
68096
|
+
});
|
|
68097
|
+
var shopifySignupOffersResponseSchema = z53.object({
|
|
68098
|
+
/** The store allows Baker to add customers (sign-ups work). */
|
|
68099
|
+
canAddCustomers: z53.boolean(),
|
|
68100
|
+
/** The store allows Baker to make discount codes (offers give codes). */
|
|
68101
|
+
canGiveCodes: z53.boolean(),
|
|
68102
|
+
offers: z53.array(shopifySignupOfferViewSchema),
|
|
68103
|
+
/** Sign-ups the store didn't take in the last 7 days: for a missing permission, and for anything else. */
|
|
68104
|
+
refusedLast7Days: z53.object({ permission: z53.number(), other: z53.number() })
|
|
68105
|
+
});
|
|
68106
|
+
|
|
68107
|
+
// src/commands/shopify/signupOffers.ts
|
|
68108
|
+
import { defineCommand as defineCommand214 } from "citty";
|
|
68109
|
+
var description = "Start here for landing sign-ups: published and this chat's staged offers, codes given and store permissions. Example: baker shopify signup-offers. Forms name an offer handle; terms are staged with baker shopify change signup-offer set.";
|
|
68110
|
+
registerSchema({ command: "shopify.signupOffers", description, args: {} });
|
|
68111
|
+
var signupOffersCommand = defineCommand214({
|
|
68112
|
+
meta: { name: "signup-offers", description },
|
|
68113
|
+
async run() {
|
|
68114
|
+
try {
|
|
68115
|
+
const data = shopifySignupOffersResponseSchema.parse(
|
|
68116
|
+
await apiGet("/api/shopify/signup-offers", params({ chatId: optionalChatId() }))
|
|
68117
|
+
);
|
|
68118
|
+
const hints2 = [];
|
|
68119
|
+
if (!data.canAddCustomers)
|
|
68120
|
+
hints2.push(
|
|
68121
|
+
'Sign-ups cannot be added to the store yet: call request_connection with { platform: "shopify", reason: "Add sign-ups from your landing" }. Keep building the page; the Form works once allowed.'
|
|
68122
|
+
);
|
|
68123
|
+
if (!data.canGiveCodes)
|
|
68124
|
+
hints2.push(
|
|
68125
|
+
"Codes need the store to allow discounts: use request_connection for Shopify and keep building the page."
|
|
68126
|
+
);
|
|
68127
|
+
if (data.offers.some((offer) => offer.status === "ended"))
|
|
68128
|
+
hints2.push(
|
|
68129
|
+
"Forms naming ended offers still add customers but give no code: stage a new offer or remove offer from the Form."
|
|
68130
|
+
);
|
|
68131
|
+
if (data.offers.length === 0)
|
|
68132
|
+
hints2.push(
|
|
68133
|
+
"No sign-up offer exists yet. Omit offer for sign-ups without a code, or stage one after the user approves its terms."
|
|
68134
|
+
);
|
|
68135
|
+
writeJson({ ok: true, hints: hints2, data });
|
|
68136
|
+
} catch (error) {
|
|
68137
|
+
fail9(error);
|
|
68138
|
+
}
|
|
68139
|
+
}
|
|
68140
|
+
});
|
|
68141
|
+
|
|
68142
|
+
// src/commands/shopify/tracking.ts
|
|
68143
|
+
import { defineCommand as defineCommand215 } from "citty";
|
|
68144
|
+
var audit = leaf(
|
|
68145
|
+
"shopify.tracking.audit",
|
|
68146
|
+
{
|
|
68147
|
+
name: "audit",
|
|
68148
|
+
description: "Start here for 'is our store's tracking set up right?': Baker's measurement on the store, Baker switched on in the theme, whether orders carry Baker's visitor, who reports sales to Meta and Google, shoppers' consent, and whether the store sells through its own website. Each check is ok, problem (with its fix: a change to stage, a task for the merchant, or a decision) or unknown (with why). Reads only \u2014 it changes nothing."
|
|
68149
|
+
},
|
|
68150
|
+
{ full: { type: "boolean", description: "Add the facts behind each check", required: false } },
|
|
68151
|
+
async (args5) => {
|
|
68152
|
+
try {
|
|
68153
|
+
const data = await apiPost("/api/shopify/tracking/audit", { full: args5.full === true });
|
|
68154
|
+
writeJson({ ok: true, hints: auditHints(data), data });
|
|
66681
68155
|
} catch (err) {
|
|
66682
68156
|
fail9(err);
|
|
66683
68157
|
}
|
|
66684
68158
|
}
|
|
68159
|
+
);
|
|
68160
|
+
var trackingCommand2 = defineCommand215({
|
|
68161
|
+
meta: { name: "tracking", description: "Check how the store is measured: baker shopify tracking audit" },
|
|
68162
|
+
subCommands: { audit }
|
|
66685
68163
|
});
|
|
66686
68164
|
|
|
66687
68165
|
// src/commands/shopify/index.ts
|
|
66688
68166
|
registerSchema({
|
|
66689
68167
|
command: "shopify.store",
|
|
66690
|
-
description: 'Start here for the connected Shopify store: its name, currency, home country, how many products and collections it has, how many products are out of stock or partly out, the prices by country it sells at (only when it has more than one), where its buy links send a shopper (`buyDestination`: checkout, store_cart or product_page), its languages and policies, whether it asks for a password, and the link to the Products page in Baker. `capped: true` means a count stopped at its ceiling (100 out of stock or partly out, 500 collections): say "100 or more", never the number as exact. `meta.store` says whether Baker\'s copy is complete and whether Baker can still reach the store.',
|
|
68168
|
+
description: 'Start here for the connected Shopify store: its name, currency, home country, how many products and collections it has, how many products are out of stock or partly out, the prices by country it sells at (only when it has more than one), where its buy links send a shopper (`buyDestination`: checkout, store_cart or product_page), its languages and policies, whether it asks for a password, and the link to the Products page in Baker. `capped: true` means a count stopped at its ceiling (100 out of stock or partly out, 500 collections): say "100 or more", never the number as exact. `meta.store` says whether Baker\'s copy is complete and whether Baker can still reach the store. `canChange` says which kinds of store change the store allows Baker to stage (products, publishing, discounts, redirects, marketing); a `false` one is allowed by the user in Shopify (`request_connection`).',
|
|
66691
68169
|
args: {
|
|
66692
68170
|
full: {
|
|
66693
68171
|
...fullArg,
|
|
@@ -66722,7 +68200,7 @@ registerSchema({
|
|
|
66722
68200
|
});
|
|
66723
68201
|
registerSchema({
|
|
66724
68202
|
command: "shopify.product",
|
|
66725
|
-
description: "One product of the connected store \u2014 accepts its id, its handle, a link to it on the store, in Baker or in Shopify admin. Returns its options with price, stock and `buyUrl` (
|
|
68203
|
+
description: "One product of the connected store \u2014 accepts its id, its handle, a link to it on the store, in Baker or in Shopify admin. Returns its options with price, stock and `buyUrl` (each option's buy link for ads, emails and messages; on a landing use `<ShopifyProduct>` instead \u2014 never build a Shopify link yourself, never use `storeUrl` for it), prices by country, the options out of stock, its collections, its photos' library imageIds to place on a landing (`baker images download <imageIds> --out src/pages/<page>/_images/`) or pass to a creative, its videos' playbackIds (`<Video playbackId=\u2026/>`), and `contentSummary`: what else Baker keeps about it. Start here for a landing from a product: add `--content` for the formatted description, every custom field by its name (a size chart, care instructions), the rating, subscriptions and translations; `--field <name>` returns one field whole. `--out-of-stock` lists only the options out of stock.",
|
|
66726
68204
|
args: {
|
|
66727
68205
|
ref: { type: "positional", description: "Product id, handle, or a link to the product", required: true },
|
|
66728
68206
|
"variants-cursor": {
|
|
@@ -66812,7 +68290,7 @@ registerSchema({
|
|
|
66812
68290
|
}
|
|
66813
68291
|
}
|
|
66814
68292
|
});
|
|
66815
|
-
var salesCommand =
|
|
68293
|
+
var salesCommand = defineCommand216({
|
|
66816
68294
|
meta: {
|
|
66817
68295
|
name: "sales",
|
|
66818
68296
|
description: "What the store sold: orders, sales, refunds and net, by day, product, campaign or landing \u2014 or when buyers order (weekday, hour, month). Example: baker shopify sales --days 7 --by campaign"
|
|
@@ -66850,7 +68328,7 @@ var salesCommand = defineCommand211({
|
|
|
66850
68328
|
}
|
|
66851
68329
|
}
|
|
66852
68330
|
});
|
|
66853
|
-
var storeCommand =
|
|
68331
|
+
var storeCommand = defineCommand216({
|
|
66854
68332
|
meta: {
|
|
66855
68333
|
name: "store",
|
|
66856
68334
|
description: "The connected Shopify store: currency, countries, how many products, what is out of stock. Example: baker shopify store"
|
|
@@ -66861,13 +68339,14 @@ var storeCommand = defineCommand211({
|
|
|
66861
68339
|
run: async ({ args: args5 }) => {
|
|
66862
68340
|
try {
|
|
66863
68341
|
const answer = await apiGet("/api/shopify/store", params({ full: args5.full }));
|
|
66864
|
-
|
|
68342
|
+
const data = answer.canChange ? { ...answer.summary, canChange: answer.canChange } : answer.summary;
|
|
68343
|
+
emit2(data, { store: answer.store }, [...storeStateHints(answer.store), ...storeHints(answer.summary)]);
|
|
66865
68344
|
} catch (err) {
|
|
66866
68345
|
fail9(err);
|
|
66867
68346
|
}
|
|
66868
68347
|
}
|
|
66869
68348
|
});
|
|
66870
|
-
var productsCommand =
|
|
68349
|
+
var productsCommand = defineCommand216({
|
|
66871
68350
|
meta: {
|
|
66872
68351
|
name: "products",
|
|
66873
68352
|
description: 'What the store sells, with price and stock. Example: baker shopify products --search "snowboard" --stock out_of_stock'
|
|
@@ -66913,7 +68392,7 @@ var productsCommand = defineCommand211({
|
|
|
66913
68392
|
}
|
|
66914
68393
|
}
|
|
66915
68394
|
});
|
|
66916
|
-
var productCommand =
|
|
68395
|
+
var productCommand = defineCommand216({
|
|
66917
68396
|
meta: {
|
|
66918
68397
|
name: "product",
|
|
66919
68398
|
description: "One product: options with buy links, prices by country, stock, photo imageIds, video playbackIds. Accepts an id, handle or link. Example: baker shopify product the-complete-snowboard --content"
|
|
@@ -66947,8 +68426,8 @@ var productCommand = defineCommand211({
|
|
|
66947
68426
|
},
|
|
66948
68427
|
run: async ({ args: args5 }) => {
|
|
66949
68428
|
try {
|
|
66950
|
-
const
|
|
66951
|
-
if (
|
|
68429
|
+
const positionals2 = Array.isArray(args5._) ? args5._ : [];
|
|
68430
|
+
if (positionals2.length > 1) {
|
|
66952
68431
|
throw new ApiError(
|
|
66953
68432
|
"VALIDATION_ERROR",
|
|
66954
68433
|
"ref: Pass one product id, handle or product link \u2014 quote a link, and search by words with `baker shopify products --search`."
|
|
@@ -66976,7 +68455,7 @@ var productCommand = defineCommand211({
|
|
|
66976
68455
|
}
|
|
66977
68456
|
}
|
|
66978
68457
|
});
|
|
66979
|
-
var collectionsCommand =
|
|
68458
|
+
var collectionsCommand = defineCommand216({
|
|
66980
68459
|
meta: {
|
|
66981
68460
|
name: "collections",
|
|
66982
68461
|
description: "The store's collections, with the handles --collection takes. Example: baker shopify collections --full"
|
|
@@ -67001,7 +68480,7 @@ var collectionsCommand = defineCommand211({
|
|
|
67001
68480
|
}
|
|
67002
68481
|
}
|
|
67003
68482
|
});
|
|
67004
|
-
var stockCommand2 =
|
|
68483
|
+
var stockCommand2 = defineCommand216({
|
|
67005
68484
|
meta: {
|
|
67006
68485
|
name: "stock",
|
|
67007
68486
|
description: "Every product with options out of stock, naming them. Example: baker shopify stock"
|
|
@@ -67025,12 +68504,12 @@ var stockCommand2 = defineCommand211({
|
|
|
67025
68504
|
}
|
|
67026
68505
|
}
|
|
67027
68506
|
});
|
|
67028
|
-
var shopifyCommand =
|
|
68507
|
+
var shopifyCommand = defineCommand216({
|
|
67029
68508
|
meta: {
|
|
67030
68509
|
name: "shopify",
|
|
67031
68510
|
description: `Read the connected Shopify store as Baker keeps it \u2014 products, prices by country, stock, collections,
|
|
67032
|
-
product photos, what it sold, who buys, where and what, and what advertises each product
|
|
67033
|
-
|
|
68511
|
+
product photos, what it sold, who buys, where and what, and what advertises each product \u2014 and stage
|
|
68512
|
+
changes to it (\`change\`), applied when the user publishes the chat and undone with \`undo\`.
|
|
67034
68513
|
|
|
67035
68514
|
Examples:
|
|
67036
68515
|
baker shopify store # currency, countries, how many products, what is out of stock
|
|
@@ -67053,18 +68532,23 @@ Examples:
|
|
|
67053
68532
|
baker shopify segments # best, loyal, at risk, new, one-time \u2014 and who to find more of
|
|
67054
68533
|
baker shopify cohorts --months 12 # buyers by first-order month: who came back
|
|
67055
68534
|
baker shopify promotions <product> # what advertises it: ads, landings, Shopping (read before pausing)
|
|
67056
|
-
baker shopify stock-holds --product <product> # what Baker paused because it ran out,
|
|
68535
|
+
baker shopify stock-holds --product <product> # what Baker paused because it ran out: what to resume, what to tell the user
|
|
68536
|
+
baker shopify change product update <product> --title "\u2026" # stage a store change (product|variant|field|collection|discount|redirect|campaign|measurement)
|
|
68537
|
+
baker shopify draft list # the store changes staged on this chat
|
|
68538
|
+
baker shopify changes # what Baker changed in the store; baker shopify undo <changeId> puts one back
|
|
68539
|
+
baker shopify tracking audit # is the store measured right? (reads only)
|
|
67057
68540
|
|
|
67058
68541
|
Product photos are already in the image library: place them by imageId
|
|
67059
68542
|
(baker images download <imageIds> --out src/pages/<page>/_images/), never by a cdn.shopify.com link.
|
|
67060
68543
|
Product videos are in the video library: place them with <Video playbackId="\u2026" />.
|
|
67061
|
-
|
|
67062
|
-
|
|
68544
|
+
On a landing, the buy button is <ShopifyProduct product="\u2026"/> \u2014 never a buyUrl or a Shopify link
|
|
68545
|
+
written by hand. buyUrl is for ads, emails and messages; never use storeUrl (a page to look at) for it.
|
|
67063
68546
|
NO_SHOPIFY_CONNECTION means no store is connected: follow error.fix. SHOPIFY_NOT_AVAILABLE means it cannot be
|
|
67064
68547
|
connected here yet: carry on without it.
|
|
67065
68548
|
Full guide: __tooling__/docs/tools/baker/shopify.md`
|
|
67066
68549
|
},
|
|
67067
68550
|
subCommands: {
|
|
68551
|
+
"signup-offers": signupOffersCommand,
|
|
67068
68552
|
store: storeCommand,
|
|
67069
68553
|
products: productsCommand,
|
|
67070
68554
|
product: productCommand,
|
|
@@ -67078,15 +68562,20 @@ Full guide: __tooling__/docs/tools/baker/shopify.md`
|
|
|
67078
68562
|
cohorts: cohortsCommand,
|
|
67079
68563
|
segments: segmentsCommand,
|
|
67080
68564
|
promotions: promotionsCommand,
|
|
67081
|
-
"stock-holds": stockHoldsCommand
|
|
68565
|
+
"stock-holds": stockHoldsCommand,
|
|
68566
|
+
change: changeCommand,
|
|
68567
|
+
draft: draftCommand5,
|
|
68568
|
+
changes: changesCommand2,
|
|
68569
|
+
undo: undoCommand,
|
|
68570
|
+
tracking: trackingCommand2
|
|
67082
68571
|
}
|
|
67083
68572
|
});
|
|
67084
68573
|
|
|
67085
68574
|
// src/commands/studio/index.ts
|
|
67086
|
-
import { defineCommand as
|
|
68575
|
+
import { defineCommand as defineCommand225 } from "citty";
|
|
67087
68576
|
|
|
67088
68577
|
// src/commands/studio/animate.ts
|
|
67089
|
-
import { defineCommand as
|
|
68578
|
+
import { defineCommand as defineCommand217 } from "citty";
|
|
67090
68579
|
|
|
67091
68580
|
// src/commands/studio/spokenPace.ts
|
|
67092
68581
|
var WORDS_PER_SECOND3 = SPOKEN_WORDS_PER_SECOND;
|
|
@@ -67599,7 +69088,7 @@ async function adByHandHintFor(body) {
|
|
|
67599
69088
|
return null;
|
|
67600
69089
|
}
|
|
67601
69090
|
}
|
|
67602
|
-
var animateCommand =
|
|
69091
|
+
var animateCommand = defineCommand217({
|
|
67603
69092
|
meta: {
|
|
67604
69093
|
name: "animate",
|
|
67605
69094
|
description: "Render a clip. With an image the look is already fixed, so the prompt describes MOVEMENT \u2014 what the camera does, what the subject does, in what order. With --from text there is no image and the prompt is the whole shot.\n\nA rendered clip is NOT usable anywhere until you keep it: `baker studio keep <id> --slot N` is what puts it in the video library. Takes nobody keeps are never ingested, which is what makes a rejected batch cheap.\n\nFor anything longer than 15 seconds, or to build on footage that already exists, use --model bytedance/seedance-2.5: it renders 4-30s and is the only model that reads an existing clip or an existing soundtrack.\n\nExamples:\n baker studio animate 'slow push in, model turns to camera and smiles' --image j57abc123def456ghi789\n baker studio animate 'she looks to camera and says: \"Hola, soy Elena\"' --avatar elena --quality 720p --aspect-ratio 9:16\n baker studio animate 'handheld drift right, steam rising from the cup' --image './out/hero.png' --duration 6 --quality 1080p\n baker studio animate 'product rotates once on a turntable' --image j57abc\u2026,j57def\u2026 --from references\n baker studio animate 'she keeps walking, camera stays with her, then she stops and looks up' --image j57abc\u2026 --from references --from-clip j57batch\u2026:0 --model bytedance/seedance-2.5 --duration 20\n baker studio animate 'hold on the product, then a slow push-in' --from references --from-video j57vid\u2026 --model bytedance/seedance-2.5\n baker studio animate 'slow drone pull-back over a solar farm at golden hour, no people' --from text --model bytedance/seedance-2.5 --duration 12"
|
|
@@ -67724,7 +69213,7 @@ var animateCommand = defineCommand212({
|
|
|
67724
69213
|
});
|
|
67725
69214
|
|
|
67726
69215
|
// src/commands/studio/generate.ts
|
|
67727
|
-
import { defineCommand as
|
|
69216
|
+
import { defineCommand as defineCommand218 } from "citty";
|
|
67728
69217
|
var MODEL_LIST2 = IMAGE_MODEL_IDS;
|
|
67729
69218
|
var DEFAULT_MAX_WAIT_MS2 = 24e4;
|
|
67730
69219
|
registerSchema({
|
|
@@ -67860,7 +69349,7 @@ function buildGenerateBody(args5, prompt) {
|
|
|
67860
69349
|
}
|
|
67861
69350
|
return body;
|
|
67862
69351
|
}
|
|
67863
|
-
var generateCommand =
|
|
69352
|
+
var generateCommand = defineCommand218({
|
|
67864
69353
|
meta: {
|
|
67865
69354
|
name: "generate",
|
|
67866
69355
|
description: "Start here to make an image. Renders 1-8 takes of one brief, ingests each into the media library as it lands, and shows the batch in the dashboard Studio next to the ones the client ran.\n\nModel choice: google/gemini-3.1-flash-image-preview (default \u2014 fast, best at editing a reference and at extreme ratios), google/gemini-3-pro-image-preview (highest fidelity, slower), openai/gpt-image-2 (photoreal and the cleanest in-image text \u2014 no --image-size, no 4:5 / 5:4), recraft/recraft-v4.1-pro-vector (vector/flat marks with palette control).\n\n--reference is the biggest quality lever there is: a real logo, product shot, Pinterest pin or sandbox screenshot beats any amount of adjectives.\n\nExamples:\n baker studio generate 'matte black bottle on wet marble, hard studio light, 35mm' --aspect-ratio 3:2 --count 3\n baker studio generate 'this bottle on a sunlit kitchen counter' --reference './src/brand/product.png,https://\u2026/kitchen.jpg'\n baker studio generate 'founder-style selfie, kitchen background, natural light' --skill ugc-selfie-hook\n baker studio generate 'flat geometric mascot, brand palette' --model recraft/recraft-v4.1-pro-vector --rgb-colors '[[10,10,10],[255,80,0]]'"
|
|
@@ -67943,7 +69432,7 @@ var generateCommand = defineCommand213({
|
|
|
67943
69432
|
});
|
|
67944
69433
|
|
|
67945
69434
|
// src/commands/studio/get.ts
|
|
67946
|
-
import { defineCommand as
|
|
69435
|
+
import { defineCommand as defineCommand219 } from "citty";
|
|
67947
69436
|
registerSchema({
|
|
67948
69437
|
command: "studio.get",
|
|
67949
69438
|
description: "Read one Studio batch: every take, where it lives, and why a take is missing. This is how you pick up a batch that was still rendering when the start command returned.",
|
|
@@ -67952,7 +69441,7 @@ registerSchema({
|
|
|
67952
69441
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
67953
69442
|
}
|
|
67954
69443
|
});
|
|
67955
|
-
var getCommand5 =
|
|
69444
|
+
var getCommand5 = defineCommand219({
|
|
67956
69445
|
meta: {
|
|
67957
69446
|
name: "get",
|
|
67958
69447
|
description: "Read one Studio batch \u2014 the takes, their urls, whether each is in the library, and the reason for any that failed.\n\nExample: baker studio get j57abc123def456ghi789\nExample: baker studio get j57abc123def456ghi789 --full"
|
|
@@ -67981,7 +69470,7 @@ var getCommand5 = defineCommand214({
|
|
|
67981
69470
|
});
|
|
67982
69471
|
|
|
67983
69472
|
// src/commands/studio/improve.ts
|
|
67984
|
-
import { defineCommand as
|
|
69473
|
+
import { defineCommand as defineCommand220 } from "citty";
|
|
67985
69474
|
var DESCRIPTION2 = "Sharpen a rough brief into directed art direction \u2014 the same rewrite the client gets from the wand in the Studio prompt bar. Reach for it when you are relaying the CLIENT's own words and want them shaped without substituting your voice; when you are writing the art direction yourself, just write it, because you will do a better job than this does.";
|
|
67986
69475
|
registerSchema({
|
|
67987
69476
|
command: "studio.improve",
|
|
@@ -68006,7 +69495,7 @@ registerSchema({
|
|
|
68006
69495
|
}
|
|
68007
69496
|
}
|
|
68008
69497
|
});
|
|
68009
|
-
var improveCommand =
|
|
69498
|
+
var improveCommand = defineCommand220({
|
|
68010
69499
|
meta: {
|
|
68011
69500
|
name: "improve",
|
|
68012
69501
|
description: `${DESCRIPTION2}
|
|
@@ -68050,7 +69539,7 @@ Examples:
|
|
|
68050
69539
|
});
|
|
68051
69540
|
|
|
68052
69541
|
// src/commands/studio/keep.ts
|
|
68053
|
-
import { defineCommand as
|
|
69542
|
+
import { defineCommand as defineCommand221 } from "citty";
|
|
68054
69543
|
registerSchema({
|
|
68055
69544
|
command: "studio.keep",
|
|
68056
69545
|
description: "Mark one take as the keeper. For an image this stars it, so the client reviewing the batch sees which one you used. For a CLIP it is the step that puts it in the video library \u2014 until then the clip cannot be used in a canvas, a landing, or an ad.",
|
|
@@ -68060,7 +69549,7 @@ registerSchema({
|
|
|
68060
69549
|
undo: { type: "boolean", description: "Un-star an image, or take a kept clip back out", required: false }
|
|
68061
69550
|
}
|
|
68062
69551
|
});
|
|
68063
|
-
var keepCommand =
|
|
69552
|
+
var keepCommand = defineCommand221({
|
|
68064
69553
|
meta: {
|
|
68065
69554
|
name: "keep",
|
|
68066
69555
|
description: "Mark one take as the keeper. An image gets starred (it was already in the library); a clip gets INGESTED into the video library, which is what makes it usable anywhere else.\n\nExample: baker studio keep j57abc123def456ghi789 --slot 2\nExample: baker studio keep j57abc123def456ghi789 --slot 2 --undo"
|
|
@@ -68111,7 +69600,7 @@ var keepCommand = defineCommand216({
|
|
|
68111
69600
|
});
|
|
68112
69601
|
|
|
68113
69602
|
// src/commands/studio/list.ts
|
|
68114
|
-
import { defineCommand as
|
|
69603
|
+
import { defineCommand as defineCommand222 } from "citty";
|
|
68115
69604
|
registerSchema({
|
|
68116
69605
|
command: "studio.list",
|
|
68117
69606
|
description: "Recent Studio batches for THIS conversation, newest first \u2014 what you have already generated, so you re-use a take instead of paying for it twice. `--all` widens it to everything the company generated, including what people ran themselves in the dashboard.",
|
|
@@ -68122,7 +69611,7 @@ registerSchema({
|
|
|
68122
69611
|
full: { type: "boolean", description: "Include settings, references and attribution", required: false }
|
|
68123
69612
|
}
|
|
68124
69613
|
});
|
|
68125
|
-
var listCommand18 =
|
|
69614
|
+
var listCommand18 = defineCommand222({
|
|
68126
69615
|
meta: {
|
|
68127
69616
|
name: "list",
|
|
68128
69617
|
description: "Recent Studio batches, newest first. Scoped to this conversation unless you pass --all.\n\nExample: baker studio list\nExample: baker studio list --kind video --limit 5\nExample: baker studio list --all # includes batches the client ran in the dashboard"
|
|
@@ -68156,7 +69645,7 @@ var listCommand18 = defineCommand217({
|
|
|
68156
69645
|
});
|
|
68157
69646
|
|
|
68158
69647
|
// src/commands/studio/models.ts
|
|
68159
|
-
import { defineCommand as
|
|
69648
|
+
import { defineCommand as defineCommand223 } from "citty";
|
|
68160
69649
|
var DESCRIPTION3 = "What each Studio model actually accepts: its shapes, resolutions, clip lengths, prompt character cap, how many reference images it takes, and which knobs it has. Read this before a batch you care about \u2014 the models disagree far more than they look like they do, and a setting the chosen model does not have is REFUSED, not ignored.";
|
|
68161
69650
|
registerSchema({
|
|
68162
69651
|
command: "studio.models",
|
|
@@ -68243,7 +69732,7 @@ function buildModelCards(kind, model) {
|
|
|
68243
69732
|
const selected = model ? ids.filter((id) => id === model) : ids;
|
|
68244
69733
|
return selected.map((id) => build(id));
|
|
68245
69734
|
}
|
|
68246
|
-
var modelsCommand =
|
|
69735
|
+
var modelsCommand = defineCommand223({
|
|
68247
69736
|
meta: {
|
|
68248
69737
|
name: "models",
|
|
68249
69738
|
description: `${DESCRIPTION3}
|
|
@@ -68290,13 +69779,13 @@ Examples:
|
|
|
68290
69779
|
});
|
|
68291
69780
|
|
|
68292
69781
|
// src/commands/studio/skills.ts
|
|
68293
|
-
import { defineCommand as
|
|
69782
|
+
import { defineCommand as defineCommand224 } from "citty";
|
|
68294
69783
|
registerSchema({
|
|
68295
69784
|
command: "studio.skills",
|
|
68296
69785
|
description: "The craft directions `studio generate --skill <id>` accepts. Each one carries directed art direction plus the model, shape and take count it wants, so you pick a look by name instead of writing the boilerplate yourself.",
|
|
68297
69786
|
args: {}
|
|
68298
69787
|
});
|
|
68299
|
-
var skillsCommand =
|
|
69788
|
+
var skillsCommand = defineCommand224({
|
|
68300
69789
|
meta: {
|
|
68301
69790
|
name: "skills",
|
|
68302
69791
|
description: "List the craft directions available to `baker studio generate --skill <id>` \u2014 what each one is for, whether it wants a reference image, and the model/shape/count it defaults to.\n\nExample: baker studio skills"
|
|
@@ -68320,7 +69809,7 @@ var skillsCommand = defineCommand219({
|
|
|
68320
69809
|
});
|
|
68321
69810
|
|
|
68322
69811
|
// src/commands/studio/index.ts
|
|
68323
|
-
var studioCommand =
|
|
69812
|
+
var studioCommand = defineCommand225({
|
|
68324
69813
|
meta: {
|
|
68325
69814
|
name: "studio",
|
|
68326
69815
|
description: `Make new imagery and clips. Every batch is recorded and shows up in the dashboard Studio for the client to review, labelled with this conversation.
|
|
@@ -68363,10 +69852,10 @@ Full guide: __tooling__/docs/tools/baker/studio.md`
|
|
|
68363
69852
|
});
|
|
68364
69853
|
|
|
68365
69854
|
// src/commands/tag-manager/index.ts
|
|
68366
|
-
import { defineCommand as
|
|
69855
|
+
import { defineCommand as defineCommand229 } from "citty";
|
|
68367
69856
|
|
|
68368
69857
|
// src/commands/tag-manager/draft.ts
|
|
68369
|
-
import { defineCommand as
|
|
69858
|
+
import { defineCommand as defineCommand226 } from "citty";
|
|
68370
69859
|
|
|
68371
69860
|
// src/commands/tag-manager/shared.ts
|
|
68372
69861
|
import { readFileSync as readFileSync20 } from "fs";
|
|
@@ -68376,11 +69865,11 @@ function failValidation5(message) {
|
|
|
68376
69865
|
}
|
|
68377
69866
|
function requireTarget6(args5, entity) {
|
|
68378
69867
|
const positional = Array.isArray(args5._) ? args5._[0] : void 0;
|
|
68379
|
-
const
|
|
68380
|
-
if (typeof
|
|
69868
|
+
const target4 = args5.id ?? args5.target ?? positional;
|
|
69869
|
+
if (typeof target4 !== "string" || target4.length === 0) {
|
|
68381
69870
|
failValidation5(`pass the ${entity} id or path as the positional argument`);
|
|
68382
69871
|
}
|
|
68383
|
-
return
|
|
69872
|
+
return target4;
|
|
68384
69873
|
}
|
|
68385
69874
|
function loadJsonArg2(args5, flag3 = "json") {
|
|
68386
69875
|
const inline = args5[flag3];
|
|
@@ -68489,13 +69978,13 @@ registerSchema({
|
|
|
68489
69978
|
chat: { type: "string", description: CHAT_READ_ARG.description, required: false }
|
|
68490
69979
|
}
|
|
68491
69980
|
});
|
|
68492
|
-
var
|
|
69981
|
+
var draftCommand6 = defineCommand226({
|
|
68493
69982
|
meta: {
|
|
68494
69983
|
name: "draft",
|
|
68495
69984
|
description: "List, show, amend, remove, or clear staged Tag Manager changes for this chat. `list` and `show` take --chat <id> to read an earlier chat's changes instead."
|
|
68496
69985
|
},
|
|
68497
69986
|
subCommands: {
|
|
68498
|
-
list:
|
|
69987
|
+
list: defineCommand226({
|
|
68499
69988
|
meta: {
|
|
68500
69989
|
name: "list",
|
|
68501
69990
|
description: "Review everything staged on this chat (--json for the raw envelope)"
|
|
@@ -68508,7 +69997,7 @@ var draftCommand5 = defineCommand221({
|
|
|
68508
69997
|
await draftList2(args5.json === true, args5.chat);
|
|
68509
69998
|
}
|
|
68510
69999
|
}),
|
|
68511
|
-
show:
|
|
70000
|
+
show: defineCommand226({
|
|
68512
70001
|
meta: {
|
|
68513
70002
|
name: "show",
|
|
68514
70003
|
description: "Print the full staged payload for one change \u2014 the receipt to verify it looks right before publish (never truncated)."
|
|
@@ -68525,7 +70014,7 @@ var draftCommand5 = defineCommand221({
|
|
|
68525
70014
|
);
|
|
68526
70015
|
}
|
|
68527
70016
|
}),
|
|
68528
|
-
amend:
|
|
70017
|
+
amend: defineCommand226({
|
|
68529
70018
|
meta: {
|
|
68530
70019
|
name: "amend",
|
|
68531
70020
|
description: "Update a staged change in place \u2014 merges a JSON patch into its payload (objects deep-merge, null deletes a key, arrays/scalars replace) and re-validates. Use this instead of remove + re-create."
|
|
@@ -68542,7 +70031,7 @@ var draftCommand5 = defineCommand221({
|
|
|
68542
70031
|
});
|
|
68543
70032
|
}
|
|
68544
70033
|
}),
|
|
68545
|
-
remove:
|
|
70034
|
+
remove: defineCommand226({
|
|
68546
70035
|
meta: { name: "remove", description: "Remove one staged change (cascades to anything depending on it)" },
|
|
68547
70036
|
args: { ref: { type: "positional", description: "Staged ref (gtm_temp_*) or target", required: false } },
|
|
68548
70037
|
run: async ({ args: args5 }) => {
|
|
@@ -68551,7 +70040,7 @@ var draftCommand5 = defineCommand221({
|
|
|
68551
70040
|
});
|
|
68552
70041
|
}
|
|
68553
70042
|
}),
|
|
68554
|
-
clear:
|
|
70043
|
+
clear: defineCommand226({
|
|
68555
70044
|
meta: { name: "clear", description: "Discard all Tag Manager changes staged on this chat" },
|
|
68556
70045
|
run: async () => {
|
|
68557
70046
|
await draftAction3("/api/tag-manager/draft/clear", {});
|
|
@@ -68561,7 +70050,7 @@ var draftCommand5 = defineCommand221({
|
|
|
68561
70050
|
});
|
|
68562
70051
|
|
|
68563
70052
|
// src/commands/tag-manager/read.ts
|
|
68564
|
-
import { defineCommand as
|
|
70053
|
+
import { defineCommand as defineCommand227 } from "citty";
|
|
68565
70054
|
registerSchema({
|
|
68566
70055
|
command: "tagManager.containers",
|
|
68567
70056
|
description: "List the Google Tag Manager containers this company's connection can reach. Every container the company connected is flagged `connected: true` \u2014 there can be several, and Baker may read and change all of them. Start here to confirm which containers you are managing.",
|
|
@@ -68602,7 +70091,7 @@ function containersHints(containers) {
|
|
|
68602
70091
|
}))
|
|
68603
70092
|
});
|
|
68604
70093
|
}
|
|
68605
|
-
var containersCommand =
|
|
70094
|
+
var containersCommand = defineCommand227({
|
|
68606
70095
|
meta: {
|
|
68607
70096
|
name: "containers",
|
|
68608
70097
|
description: `List Tag Manager containers reachable by this company's connection.
|
|
@@ -68619,7 +70108,7 @@ Start here:
|
|
|
68619
70108
|
}
|
|
68620
70109
|
}
|
|
68621
70110
|
});
|
|
68622
|
-
var readCommand =
|
|
70111
|
+
var readCommand = defineCommand227({
|
|
68623
70112
|
meta: {
|
|
68624
70113
|
name: "read",
|
|
68625
70114
|
description: `Read the current contents of the Tag Manager container \u2014 always do this before staging changes.
|
|
@@ -68661,7 +70150,7 @@ Examples:
|
|
|
68661
70150
|
});
|
|
68662
70151
|
|
|
68663
70152
|
// src/commands/tag-manager/write-commands.ts
|
|
68664
|
-
import { defineCommand as
|
|
70153
|
+
import { defineCommand as defineCommand228 } from "citty";
|
|
68665
70154
|
var CONTAINER_ARG_DESCRIPTION = "Numeric container id (optional only when one container is connected \u2014 run `baker tag-manager containers`)";
|
|
68666
70155
|
var ENTITIES = [
|
|
68667
70156
|
{
|
|
@@ -68717,10 +70206,10 @@ for (const { entity, noun, createHint } of ENTITIES) {
|
|
|
68717
70206
|
});
|
|
68718
70207
|
}
|
|
68719
70208
|
function entityCommand(entity, noun, example) {
|
|
68720
|
-
return
|
|
70209
|
+
return defineCommand228({
|
|
68721
70210
|
meta: { name: entity, description: `Stage ${noun} changes on this chat's Tag Manager draft` },
|
|
68722
70211
|
subCommands: {
|
|
68723
|
-
create:
|
|
70212
|
+
create: defineCommand228({
|
|
68724
70213
|
meta: {
|
|
68725
70214
|
name: "create",
|
|
68726
70215
|
description: `Stage a new ${noun}
|
|
@@ -68742,7 +70231,7 @@ Examples:
|
|
|
68742
70231
|
});
|
|
68743
70232
|
}
|
|
68744
70233
|
}),
|
|
68745
|
-
update:
|
|
70234
|
+
update: defineCommand228({
|
|
68746
70235
|
meta: {
|
|
68747
70236
|
name: "update",
|
|
68748
70237
|
description: `Stage an update to an existing ${noun} (pass its id or path)`
|
|
@@ -68762,7 +70251,7 @@ Examples:
|
|
|
68762
70251
|
});
|
|
68763
70252
|
}
|
|
68764
70253
|
}),
|
|
68765
|
-
delete:
|
|
70254
|
+
delete: defineCommand228({
|
|
68766
70255
|
meta: { name: "delete", description: `Stage the deletion of a ${noun} (pass its id or path)` },
|
|
68767
70256
|
args: {
|
|
68768
70257
|
id: { type: "positional", description: `${noun} id or path`, required: false },
|
|
@@ -68803,7 +70292,7 @@ function builtinTypes(args5) {
|
|
|
68803
70292
|
}
|
|
68804
70293
|
return raw.split(",").map((entry) => entry.trim());
|
|
68805
70294
|
}
|
|
68806
|
-
var builtinCommand =
|
|
70295
|
+
var builtinCommand = defineCommand228({
|
|
68807
70296
|
meta: {
|
|
68808
70297
|
name: "builtin",
|
|
68809
70298
|
description: `Enable or disable built-in variables
|
|
@@ -68813,7 +70302,7 @@ Examples:
|
|
|
68813
70302
|
baker tag-manager builtin disable --types formId`
|
|
68814
70303
|
},
|
|
68815
70304
|
subCommands: {
|
|
68816
|
-
enable:
|
|
70305
|
+
enable: defineCommand228({
|
|
68817
70306
|
meta: { name: "enable", description: "Stage enabling built-in variables" },
|
|
68818
70307
|
args: {
|
|
68819
70308
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -68827,7 +70316,7 @@ Examples:
|
|
|
68827
70316
|
});
|
|
68828
70317
|
}
|
|
68829
70318
|
}),
|
|
68830
|
-
disable:
|
|
70319
|
+
disable: defineCommand228({
|
|
68831
70320
|
meta: { name: "disable", description: "Stage disabling built-in variables" },
|
|
68832
70321
|
args: {
|
|
68833
70322
|
types: { type: "string", description: "Comma-separated types", required: false },
|
|
@@ -68845,7 +70334,7 @@ Examples:
|
|
|
68845
70334
|
});
|
|
68846
70335
|
|
|
68847
70336
|
// src/commands/tag-manager/index.ts
|
|
68848
|
-
var tagManagerCommand =
|
|
70337
|
+
var tagManagerCommand = defineCommand229({
|
|
68849
70338
|
meta: {
|
|
68850
70339
|
name: "tag-manager",
|
|
68851
70340
|
description: `Read and change what lives inside the client's Google Tag Manager container \u2014 tags, triggers, variables, folders and built-in variables.
|
|
@@ -68877,12 +70366,12 @@ Full guide: __tooling__/docs/tools/baker/tag-manager.md`
|
|
|
68877
70366
|
variable: variableCommand,
|
|
68878
70367
|
folder: folderCommand,
|
|
68879
70368
|
builtin: builtinCommand,
|
|
68880
|
-
draft:
|
|
70369
|
+
draft: draftCommand6
|
|
68881
70370
|
}
|
|
68882
70371
|
});
|
|
68883
70372
|
|
|
68884
70373
|
// src/commands/tags/index.ts
|
|
68885
|
-
import { defineCommand as
|
|
70374
|
+
import { defineCommand as defineCommand230 } from "citty";
|
|
68886
70375
|
|
|
68887
70376
|
// src/commands/tags/shared.ts
|
|
68888
70377
|
function failApi3(err) {
|
|
@@ -68962,7 +70451,7 @@ async function listTags(json) {
|
|
|
68962
70451
|
var listArgs11 = {
|
|
68963
70452
|
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" }
|
|
68964
70453
|
};
|
|
68965
|
-
var listCommand19 =
|
|
70454
|
+
var listCommand19 = defineCommand230({
|
|
68966
70455
|
meta: {
|
|
68967
70456
|
name: "list",
|
|
68968
70457
|
description: "Effective tags for this chat (production + staged), with each tag's full readable config (secrets excluded) \u2014 reuse a stored value to pre-fill a change rather than asking the user. Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
|
|
@@ -68981,7 +70470,7 @@ async function listDraft4(chat) {
|
|
|
68981
70470
|
failApi3(err);
|
|
68982
70471
|
}
|
|
68983
70472
|
}
|
|
68984
|
-
var
|
|
70473
|
+
var draftCommand7 = defineCommand230({
|
|
68985
70474
|
meta: {
|
|
68986
70475
|
name: "draft",
|
|
68987
70476
|
description: "Review the tag changes staged in this chat (read-only). Staged changes were approved via request_tag_input and apply when the chat is published; to amend or drop one, propose a follow-up change through the same tool (a delete on a tag_temp_* ref drops the staged create). Takes --chat <id> to read an earlier chat's staged changes instead."
|
|
@@ -68991,7 +70480,7 @@ var draftCommand6 = defineCommand225({
|
|
|
68991
70480
|
await listDraft4(args5.chat);
|
|
68992
70481
|
}
|
|
68993
70482
|
});
|
|
68994
|
-
var tagsCommand4 =
|
|
70483
|
+
var tagsCommand4 = defineCommand230({
|
|
68995
70484
|
meta: {
|
|
68996
70485
|
name: "tags",
|
|
68997
70486
|
description: `Read the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) \u2014 production tags plus the changes staged in this chat.
|
|
@@ -69009,7 +70498,7 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
|
|
|
69009
70498
|
},
|
|
69010
70499
|
subCommands: {
|
|
69011
70500
|
list: listCommand19,
|
|
69012
|
-
draft:
|
|
70501
|
+
draft: draftCommand7
|
|
69013
70502
|
},
|
|
69014
70503
|
// Bare `baker tags` lists the effective tags. `args` stays so citty knows this
|
|
69015
70504
|
// command's flags when it scans rawArgs for a subcommand name, and `default` (not
|
|
@@ -69020,10 +70509,10 @@ Full guide: __tooling__/docs/tools/baker/tags.md`
|
|
|
69020
70509
|
});
|
|
69021
70510
|
|
|
69022
70511
|
// src/commands/testimonials/index.ts
|
|
69023
|
-
import { defineCommand as
|
|
70512
|
+
import { defineCommand as defineCommand234 } from "citty";
|
|
69024
70513
|
|
|
69025
70514
|
// src/commands/testimonials/get.ts
|
|
69026
|
-
import { defineCommand as
|
|
70515
|
+
import { defineCommand as defineCommand231 } from "citty";
|
|
69027
70516
|
registerSchema({
|
|
69028
70517
|
command: "testimonials.get",
|
|
69029
70518
|
description: "Get a single testimonial by ID",
|
|
@@ -69031,7 +70520,7 @@ registerSchema({
|
|
|
69031
70520
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
69032
70521
|
}
|
|
69033
70522
|
});
|
|
69034
|
-
var getCommand6 =
|
|
70523
|
+
var getCommand6 = defineCommand231({
|
|
69035
70524
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
69036
70525
|
args: {
|
|
69037
70526
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -69068,7 +70557,7 @@ var getCommand6 = defineCommand226({
|
|
|
69068
70557
|
});
|
|
69069
70558
|
|
|
69070
70559
|
// src/commands/testimonials/list.ts
|
|
69071
|
-
import { defineCommand as
|
|
70560
|
+
import { defineCommand as defineCommand232 } from "citty";
|
|
69072
70561
|
|
|
69073
70562
|
// src/commands/testimonials/emptyCorpusHints.ts
|
|
69074
70563
|
function resolveEmptyReason({
|
|
@@ -69225,7 +70714,7 @@ function buildListParams(args5) {
|
|
|
69225
70714
|
}
|
|
69226
70715
|
return params2;
|
|
69227
70716
|
}
|
|
69228
|
-
var listCommand20 =
|
|
70717
|
+
var listCommand20 = defineCommand232({
|
|
69229
70718
|
meta: {
|
|
69230
70719
|
name: "list",
|
|
69231
70720
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -69285,7 +70774,7 @@ var listCommand20 = defineCommand227({
|
|
|
69285
70774
|
});
|
|
69286
70775
|
|
|
69287
70776
|
// src/commands/testimonials/search.ts
|
|
69288
|
-
import { defineCommand as
|
|
70777
|
+
import { defineCommand as defineCommand233 } from "citty";
|
|
69289
70778
|
var FILTER_FLAGS2 = ["source", "rating-min", "rating-max", "status", "sentiment", "language", "tags"];
|
|
69290
70779
|
function languageBiasHint(results, requestedLanguage) {
|
|
69291
70780
|
if (requestedLanguage) {
|
|
@@ -69364,7 +70853,7 @@ function buildSearchRequest(query, args5) {
|
|
|
69364
70853
|
}
|
|
69365
70854
|
return body;
|
|
69366
70855
|
}
|
|
69367
|
-
var searchCommand3 =
|
|
70856
|
+
var searchCommand3 = defineCommand233({
|
|
69368
70857
|
meta: {
|
|
69369
70858
|
name: "search",
|
|
69370
70859
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -69428,7 +70917,7 @@ var searchCommand3 = defineCommand228({
|
|
|
69428
70917
|
var tagsCommand5 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
69429
70918
|
|
|
69430
70919
|
// src/commands/testimonials/index.ts
|
|
69431
|
-
var testimonialsCommand =
|
|
70920
|
+
var testimonialsCommand = defineCommand234({
|
|
69432
70921
|
meta: {
|
|
69433
70922
|
name: "testimonials",
|
|
69434
70923
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -69450,10 +70939,10 @@ Full guide: __tooling__/docs/tools/baker/testimonials.md`
|
|
|
69450
70939
|
});
|
|
69451
70940
|
|
|
69452
70941
|
// src/commands/videos/index.ts
|
|
69453
|
-
import { defineCommand as
|
|
70942
|
+
import { defineCommand as defineCommand242 } from "citty";
|
|
69454
70943
|
|
|
69455
70944
|
// src/commands/videos/delete.ts
|
|
69456
|
-
import { defineCommand as
|
|
70945
|
+
import { defineCommand as defineCommand235 } from "citty";
|
|
69457
70946
|
registerSchema({
|
|
69458
70947
|
command: "videos.delete",
|
|
69459
70948
|
description: "Delete a video by ID",
|
|
@@ -69467,7 +70956,7 @@ registerSchema({
|
|
|
69467
70956
|
}
|
|
69468
70957
|
}
|
|
69469
70958
|
});
|
|
69470
|
-
var deleteCommand4 =
|
|
70959
|
+
var deleteCommand4 = defineCommand235({
|
|
69471
70960
|
meta: {
|
|
69472
70961
|
name: "delete",
|
|
69473
70962
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -69508,7 +70997,7 @@ var deleteCommand4 = defineCommand230({
|
|
|
69508
70997
|
});
|
|
69509
70998
|
|
|
69510
70999
|
// src/commands/videos/describe.ts
|
|
69511
|
-
import { defineCommand as
|
|
71000
|
+
import { defineCommand as defineCommand236 } from "citty";
|
|
69512
71001
|
var DESCRIPTION_HELP2 = "What the clip actually shows, in the words someone would search for it by. This is what `baker videos search` retrieves on.";
|
|
69513
71002
|
registerSchema({
|
|
69514
71003
|
command: "videos.describe",
|
|
@@ -69525,7 +71014,7 @@ registerSchema({
|
|
|
69525
71014
|
full: { type: "boolean", description: "Return the whole library row, not just what changed", required: false }
|
|
69526
71015
|
}
|
|
69527
71016
|
});
|
|
69528
|
-
var describeCommand2 =
|
|
71017
|
+
var describeCommand2 = defineCommand236({
|
|
69529
71018
|
meta: {
|
|
69530
71019
|
name: "describe",
|
|
69531
71020
|
description: `Correct what the library says a video is. Only the fields you pass change; the rest keep their current values.
|
|
@@ -69583,7 +71072,7 @@ Example: baker videos describe j571abc123 --description "Customer explaining how
|
|
|
69583
71072
|
});
|
|
69584
71073
|
|
|
69585
71074
|
// src/commands/videos/get.ts
|
|
69586
|
-
import { defineCommand as
|
|
71075
|
+
import { defineCommand as defineCommand237 } from "citty";
|
|
69587
71076
|
registerSchema({
|
|
69588
71077
|
command: "videos.get",
|
|
69589
71078
|
description: "Get a single video by ID",
|
|
@@ -69591,7 +71080,7 @@ registerSchema({
|
|
|
69591
71080
|
id: { type: "string", description: "Video ID", required: true }
|
|
69592
71081
|
}
|
|
69593
71082
|
});
|
|
69594
|
-
var getCommand7 =
|
|
71083
|
+
var getCommand7 = defineCommand237({
|
|
69595
71084
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
69596
71085
|
args: {
|
|
69597
71086
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -69628,7 +71117,7 @@ var getCommand7 = defineCommand232({
|
|
|
69628
71117
|
});
|
|
69629
71118
|
|
|
69630
71119
|
// src/commands/videos/group.ts
|
|
69631
|
-
import { defineCommand as
|
|
71120
|
+
import { defineCommand as defineCommand238 } from "citty";
|
|
69632
71121
|
registerSchema({
|
|
69633
71122
|
command: "videos.group",
|
|
69634
71123
|
description: "List every clip and image that arrived in the same set as this video (carousel slides, one page)",
|
|
@@ -69637,7 +71126,7 @@ registerSchema({
|
|
|
69637
71126
|
"group-key": { type: "string", description: "The set key directly, when you already have it", required: false }
|
|
69638
71127
|
}
|
|
69639
71128
|
});
|
|
69640
|
-
var groupCommand2 =
|
|
71129
|
+
var groupCommand2 = defineCommand238({
|
|
69641
71130
|
meta: {
|
|
69642
71131
|
name: "group",
|
|
69643
71132
|
description: "List every asset that arrived in the same set as this clip \u2014 the other slides of the Instagram post it came from, stills included. A carousel is authored to be read in order, so a clip pulled out of one is usually missing half its meaning. Example: baker videos group <videoId>"
|
|
@@ -69660,7 +71149,7 @@ var groupCommand2 = defineCommand233({
|
|
|
69660
71149
|
import { mkdtemp as mkdtemp3, rm as rm9, stat as stat12 } from "fs/promises";
|
|
69661
71150
|
import { tmpdir as tmpdir4 } from "os";
|
|
69662
71151
|
import path48 from "path";
|
|
69663
|
-
import { defineCommand as
|
|
71152
|
+
import { defineCommand as defineCommand239 } from "citty";
|
|
69664
71153
|
|
|
69665
71154
|
// src/lib/streamUpload.ts
|
|
69666
71155
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -69824,7 +71313,7 @@ registerSchema({
|
|
|
69824
71313
|
"dry-run": { type: "boolean", description: "Preview the operation without executing", required: false }
|
|
69825
71314
|
}
|
|
69826
71315
|
});
|
|
69827
|
-
var ingestCommand2 =
|
|
71316
|
+
var ingestCommand2 = defineCommand239({
|
|
69828
71317
|
meta: {
|
|
69829
71318
|
name: "ingest",
|
|
69830
71319
|
description: "Add a video to the library from a URL. A direct file URL is handed straight to Baker, which fetches it. A page URL (YouTube, TikTok, Vimeo, Instagram) is downloaded here first, then uploaded \u2014 and a direct URL that Baker cannot fetch falls back to that same path automatically.\n\nExample: baker videos ingest https://www.youtube.com/watch?v=abc123"
|
|
@@ -70086,7 +71575,7 @@ async function uploadToAssetStore(filePath, sizeBytes) {
|
|
|
70086
71575
|
}
|
|
70087
71576
|
|
|
70088
71577
|
// src/commands/videos/search.ts
|
|
70089
|
-
import { defineCommand as
|
|
71578
|
+
import { defineCommand as defineCommand240 } from "citty";
|
|
70090
71579
|
registerSchema({
|
|
70091
71580
|
command: "videos.search",
|
|
70092
71581
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -70096,7 +71585,7 @@ registerSchema({
|
|
|
70096
71585
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
70097
71586
|
}
|
|
70098
71587
|
});
|
|
70099
|
-
var searchCommand4 =
|
|
71588
|
+
var searchCommand4 = defineCommand240({
|
|
70100
71589
|
meta: {
|
|
70101
71590
|
name: "search",
|
|
70102
71591
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -70148,7 +71637,7 @@ var tagsCommand6 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
|
70148
71637
|
// src/commands/videos/upload.ts
|
|
70149
71638
|
import { readFile as readFile34, stat as stat13 } from "fs/promises";
|
|
70150
71639
|
import { basename as basename3, extname as extname4 } from "path";
|
|
70151
|
-
import { defineCommand as
|
|
71640
|
+
import { defineCommand as defineCommand241 } from "citty";
|
|
70152
71641
|
var MIME_MAP = {
|
|
70153
71642
|
".mp4": "video/mp4",
|
|
70154
71643
|
".mov": "video/quicktime",
|
|
@@ -70190,7 +71679,7 @@ function detectContentType(filePath) {
|
|
|
70190
71679
|
function isRemoteUrl3(value) {
|
|
70191
71680
|
return /^https?:\/\//i.test(value);
|
|
70192
71681
|
}
|
|
70193
|
-
var uploadCommand2 =
|
|
71682
|
+
var uploadCommand2 = defineCommand241({
|
|
70194
71683
|
meta: {
|
|
70195
71684
|
name: "upload",
|
|
70196
71685
|
description: "Upload a video to Baker \u2014 accepts a local file path OR a remote http(s) URL.\n\nLocal: auto-detects content type and uploads via Mux direct upload.\nRemote: hands off to `videos ingest` (direct fetch, or download-then-upload for a YouTube/TikTok/Vimeo page).\n\nExamples:\n baker videos upload ./demo.mp4\n baker videos upload https://www.youtube.com/watch?v=abc123"
|
|
@@ -70274,7 +71763,7 @@ var uploadCommand2 = defineCommand236({
|
|
|
70274
71763
|
});
|
|
70275
71764
|
|
|
70276
71765
|
// src/commands/videos/index.ts
|
|
70277
|
-
var videosCommand =
|
|
71766
|
+
var videosCommand = defineCommand242({
|
|
70278
71767
|
meta: {
|
|
70279
71768
|
name: "videos",
|
|
70280
71769
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, ingest, describe, delete, tags.
|
|
@@ -70303,10 +71792,10 @@ Full guide: __tooling__/docs/tools/baker/videos.md`
|
|
|
70303
71792
|
});
|
|
70304
71793
|
|
|
70305
71794
|
// src/commands/winning-ads/index.ts
|
|
70306
|
-
import { defineCommand as
|
|
71795
|
+
import { defineCommand as defineCommand255 } from "citty";
|
|
70307
71796
|
|
|
70308
71797
|
// src/commands/winning-ads/advertisers.ts
|
|
70309
|
-
import { defineCommand as
|
|
71798
|
+
import { defineCommand as defineCommand243 } from "citty";
|
|
70310
71799
|
|
|
70311
71800
|
// src/commands/winning-ads/shared.ts
|
|
70312
71801
|
function splitList3(value) {
|
|
@@ -70359,7 +71848,7 @@ function advertiserNormalizer(record, full) {
|
|
|
70359
71848
|
last_synced_at: record.last_synced_at ?? null
|
|
70360
71849
|
};
|
|
70361
71850
|
}
|
|
70362
|
-
var advertisersCommand2 =
|
|
71851
|
+
var advertisersCommand2 = defineCommand243({
|
|
70363
71852
|
meta: {
|
|
70364
71853
|
name: "advertisers",
|
|
70365
71854
|
description: 'List corpus advertisers by name or domain. Find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id / winners. Example: baker winning-ads advertisers "Deel" --output md'
|
|
@@ -70417,7 +71906,7 @@ var advertisersCommand2 = defineCommand238({
|
|
|
70417
71906
|
});
|
|
70418
71907
|
|
|
70419
71908
|
// src/commands/winning-ads/brief.ts
|
|
70420
|
-
import { defineCommand as
|
|
71909
|
+
import { defineCommand as defineCommand244 } from "citty";
|
|
70421
71910
|
registerSchema({
|
|
70422
71911
|
command: "winning-ads.brief",
|
|
70423
71912
|
description: "Generate a creative brief grounded in strategically-similar winning ads. Optionally describe the target creative with --dna (JSON) and steer with --notes.",
|
|
@@ -70463,7 +71952,7 @@ function parseDna(raw) {
|
|
|
70463
71952
|
}
|
|
70464
71953
|
return parsed;
|
|
70465
71954
|
}
|
|
70466
|
-
var briefCommand =
|
|
71955
|
+
var briefCommand = defineCommand244({
|
|
70467
71956
|
meta: {
|
|
70468
71957
|
name: "brief",
|
|
70469
71958
|
description: `Generate a creative brief from winning references. Example: baker winning-ads brief --dna '{"angle":"cost savings"}' --notes "B2B, LinkedIn video" --k 8`
|
|
@@ -70499,7 +71988,7 @@ var briefCommand = defineCommand239({
|
|
|
70499
71988
|
});
|
|
70500
71989
|
|
|
70501
71990
|
// src/commands/winning-ads/content.ts
|
|
70502
|
-
import { defineCommand as
|
|
71991
|
+
import { defineCommand as defineCommand245 } from "citty";
|
|
70503
71992
|
registerSchema({
|
|
70504
71993
|
command: "winning-ads.content",
|
|
70505
71994
|
description: "Read what's INSIDE one winning ad: the spoken transcript, the on-screen text, and the ad copy. Use this after `search`/`winners`/`feed` return a shortlist \u2014 pass an ad_id to understand a reference before reproducing it. Add --full for speech, pacing, and soundtrack detail. Video ads carry the transcript/on-screen text; static ads carry only the copy.",
|
|
@@ -70512,7 +72001,7 @@ registerSchema({
|
|
|
70512
72001
|
}
|
|
70513
72002
|
}
|
|
70514
72003
|
});
|
|
70515
|
-
var contentCommand =
|
|
72004
|
+
var contentCommand = defineCommand245({
|
|
70516
72005
|
meta: {
|
|
70517
72006
|
name: "content",
|
|
70518
72007
|
description: "Read the transcript + on-screen text + copy of one winning ad. Example: baker winning-ads content adg_123 --platform meta --full --output md"
|
|
@@ -70561,7 +72050,7 @@ var contentCommand = defineCommand240({
|
|
|
70561
72050
|
});
|
|
70562
72051
|
|
|
70563
72052
|
// src/commands/winning-ads/feed.ts
|
|
70564
|
-
import { defineCommand as
|
|
72053
|
+
import { defineCommand as defineCommand246 } from "citty";
|
|
70565
72054
|
function buildFeedParams(input) {
|
|
70566
72055
|
const params2 = {};
|
|
70567
72056
|
const advertiser = splitList3(input.advertiser);
|
|
@@ -70613,7 +72102,7 @@ registerSchema({
|
|
|
70613
72102
|
format: { type: "string", description: "Comma-separated formats to include (e.g. static,video)", required: false }
|
|
70614
72103
|
}
|
|
70615
72104
|
});
|
|
70616
|
-
var feedCommand2 =
|
|
72105
|
+
var feedCommand2 = defineCommand246({
|
|
70617
72106
|
meta: {
|
|
70618
72107
|
name: "feed",
|
|
70619
72108
|
description: "Winners across every brand you follow (browse, then trim per advertiser). Example: baker winning-ads feed --per-advertiser 5 --output md"
|
|
@@ -70698,7 +72187,7 @@ var feedCommand2 = defineCommand241({
|
|
|
70698
72187
|
});
|
|
70699
72188
|
|
|
70700
72189
|
// src/commands/winning-ads/follow.ts
|
|
70701
|
-
import { defineCommand as
|
|
72190
|
+
import { defineCommand as defineCommand247 } from "citty";
|
|
70702
72191
|
var PLATFORMS = adLibraryPlatformSchema.options;
|
|
70703
72192
|
registerSchema({
|
|
70704
72193
|
command: "winning-ads.follow",
|
|
@@ -70713,7 +72202,7 @@ registerSchema({
|
|
|
70713
72202
|
label: { type: "string", description: "Optional display label (defaults to the resolved name)", required: false }
|
|
70714
72203
|
}
|
|
70715
72204
|
});
|
|
70716
|
-
var followCommand =
|
|
72205
|
+
var followCommand = defineCommand247({
|
|
70717
72206
|
meta: {
|
|
70718
72207
|
name: "follow",
|
|
70719
72208
|
description: 'Follow a brand to track ALL its ads \u2014 every platform and country. --platform is how we read your input, not a limit. A domain tracks every platform we can resolve. Example: baker winning-ads follow "deel.com" --platform meta'
|
|
@@ -70760,7 +72249,7 @@ var followCommand = defineCommand242({
|
|
|
70760
72249
|
});
|
|
70761
72250
|
|
|
70762
72251
|
// src/commands/winning-ads/follow-competitors.ts
|
|
70763
|
-
import { defineCommand as
|
|
72252
|
+
import { defineCommand as defineCommand248 } from "citty";
|
|
70764
72253
|
var PLATFORMS2 = adLibraryPlatformSchema.options;
|
|
70765
72254
|
var BATCH_TIMEOUT_MS = 3e5;
|
|
70766
72255
|
function buildFollowBatchBody(input) {
|
|
@@ -70793,7 +72282,7 @@ registerSchema({
|
|
|
70793
72282
|
}
|
|
70794
72283
|
}
|
|
70795
72284
|
});
|
|
70796
|
-
var followCompetitorsCommand =
|
|
72285
|
+
var followCompetitorsCommand = defineCommand248({
|
|
70797
72286
|
meta: {
|
|
70798
72287
|
name: "follow-competitors",
|
|
70799
72288
|
description: 'Follow many brands at once by domain \u2014 add every competitor in one call. Example: baker winning-ads follow-competitors "deel.com,notion.so,hubspot.com"'
|
|
@@ -70868,7 +72357,7 @@ var followCompetitorsCommand = defineCommand243({
|
|
|
70868
72357
|
});
|
|
70869
72358
|
|
|
70870
72359
|
// src/commands/winning-ads/following.ts
|
|
70871
|
-
import { defineCommand as
|
|
72360
|
+
import { defineCommand as defineCommand249 } from "citty";
|
|
70872
72361
|
registerSchema({
|
|
70873
72362
|
command: "winning-ads.following",
|
|
70874
72363
|
description: "List the brands you follow in your ad-dna library, with each one's status (ready vs still adding) and cached ad counts. A brand still adding has counts that are a lie in progress; one with `discovery_failed` has counts that are short because we couldn't finish looking, which is not the same as it running no ads.",
|
|
@@ -70922,7 +72411,7 @@ function followingNormalizer(record, full) {
|
|
|
70922
72411
|
platforms: Array.isArray(record.platforms) ? record.platforms : []
|
|
70923
72412
|
};
|
|
70924
72413
|
}
|
|
70925
|
-
var followingCommand =
|
|
72414
|
+
var followingCommand = defineCommand249({
|
|
70926
72415
|
meta: {
|
|
70927
72416
|
name: "following",
|
|
70928
72417
|
description: "List brands you follow, with status (ready / adding\u2026) and cached counts. A brand's counts are only final once it is ready. Example: baker winning-ads following --output md"
|
|
@@ -70958,7 +72447,7 @@ var followingCommand = defineCommand244({
|
|
|
70958
72447
|
});
|
|
70959
72448
|
|
|
70960
72449
|
// src/commands/winning-ads/patterns.ts
|
|
70961
|
-
import { defineCommand as
|
|
72450
|
+
import { defineCommand as defineCommand250 } from "citty";
|
|
70962
72451
|
registerSchema({
|
|
70963
72452
|
command: "winning-ads.patterns",
|
|
70964
72453
|
description: "Mine what separates two cohorts of ads: pass a comma-list of winning ad ids (--winners) and a comma-list of weaker ad ids (--duds). Returns the discriminating DNA fields.",
|
|
@@ -70997,7 +72486,7 @@ function discriminatorRow(record) {
|
|
|
70997
72486
|
top_values_duds: Array.isArray(record.top_values_b) ? record.top_values_b.join(", ") : ""
|
|
70998
72487
|
};
|
|
70999
72488
|
}
|
|
71000
|
-
var patternsCommand =
|
|
72489
|
+
var patternsCommand = defineCommand250({
|
|
71001
72490
|
meta: {
|
|
71002
72491
|
name: "patterns",
|
|
71003
72492
|
description: "Discover what separates winning ads from weak ones. Example: baker winning-ads patterns --winners a_1,a_2,a_3 --duds a_9,a_8 --output md"
|
|
@@ -71053,7 +72542,7 @@ var patternsCommand = defineCommand245({
|
|
|
71053
72542
|
});
|
|
71054
72543
|
|
|
71055
72544
|
// src/commands/winning-ads/search.ts
|
|
71056
|
-
import { defineCommand as
|
|
72545
|
+
import { defineCommand as defineCommand251 } from "citty";
|
|
71057
72546
|
registerSchema({
|
|
71058
72547
|
command: "winning-ads.search",
|
|
71059
72548
|
description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url) to pick a reference to reproduce.",
|
|
@@ -71126,15 +72615,15 @@ function setNumber(body, key, value) {
|
|
|
71126
72615
|
body[key] = Number(value);
|
|
71127
72616
|
}
|
|
71128
72617
|
}
|
|
71129
|
-
function setList(
|
|
71130
|
-
const
|
|
71131
|
-
if (
|
|
71132
|
-
|
|
72618
|
+
function setList(target4, key, value) {
|
|
72619
|
+
const list3 = splitList3(value);
|
|
72620
|
+
if (list3.length) {
|
|
72621
|
+
target4[key] = list3;
|
|
71133
72622
|
}
|
|
71134
72623
|
}
|
|
71135
|
-
function setString(
|
|
72624
|
+
function setString(target4, key, value) {
|
|
71136
72625
|
if (value) {
|
|
71137
|
-
|
|
72626
|
+
target4[key] = value;
|
|
71138
72627
|
}
|
|
71139
72628
|
}
|
|
71140
72629
|
function buildSearchBody2(args5) {
|
|
@@ -71191,7 +72680,7 @@ function reportSearchError(err) {
|
|
|
71191
72680
|
}
|
|
71192
72681
|
reportError2(err);
|
|
71193
72682
|
}
|
|
71194
|
-
var searchCommand5 =
|
|
72683
|
+
var searchCommand5 = defineCommand251({
|
|
71195
72684
|
meta: {
|
|
71196
72685
|
name: "search",
|
|
71197
72686
|
description: "Search winning reference ads. Example: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --winner-category winner --exclude-advertiser adv_123 --output md"
|
|
@@ -71311,7 +72800,7 @@ var searchCommand5 = defineCommand246({
|
|
|
71311
72800
|
});
|
|
71312
72801
|
|
|
71313
72802
|
// src/commands/winning-ads/seeds.ts
|
|
71314
|
-
import { defineCommand as
|
|
72803
|
+
import { defineCommand as defineCommand252 } from "citty";
|
|
71315
72804
|
function leanRow(r) {
|
|
71316
72805
|
return {
|
|
71317
72806
|
key: r.key,
|
|
@@ -71339,7 +72828,7 @@ function makeSeedCommand(opts) {
|
|
|
71339
72828
|
limit: { type: "number", description: "Max keys 1-100 (default 20)", required: false, default: 20 }
|
|
71340
72829
|
}
|
|
71341
72830
|
});
|
|
71342
|
-
return
|
|
72831
|
+
return defineCommand252({
|
|
71343
72832
|
meta: { name: opts.name, description: opts.description },
|
|
71344
72833
|
args: {
|
|
71345
72834
|
platform: { type: "string", description: "Single platform to segment on", required: false },
|
|
@@ -71388,7 +72877,7 @@ var formatsCommand = makeSeedCommand({
|
|
|
71388
72877
|
});
|
|
71389
72878
|
|
|
71390
72879
|
// src/commands/winning-ads/unfollow.ts
|
|
71391
|
-
import { defineCommand as
|
|
72880
|
+
import { defineCommand as defineCommand253 } from "citty";
|
|
71392
72881
|
registerSchema({
|
|
71393
72882
|
command: "winning-ads.unfollow",
|
|
71394
72883
|
description: "Stop following a brand \u2014 removes it from your ad-dna library by advertiser id.",
|
|
@@ -71396,7 +72885,7 @@ registerSchema({
|
|
|
71396
72885
|
advertiser: { type: "string", description: "Advertiser id to unfollow", required: true }
|
|
71397
72886
|
}
|
|
71398
72887
|
});
|
|
71399
|
-
var unfollowCommand =
|
|
72888
|
+
var unfollowCommand = defineCommand253({
|
|
71400
72889
|
meta: {
|
|
71401
72890
|
name: "unfollow",
|
|
71402
72891
|
description: "Stop following a brand by advertiser id. Example: baker winning-ads unfollow adv_123"
|
|
@@ -71417,7 +72906,7 @@ var unfollowCommand = defineCommand248({
|
|
|
71417
72906
|
});
|
|
71418
72907
|
|
|
71419
72908
|
// src/commands/winning-ads/winners.ts
|
|
71420
|
-
import { defineCommand as
|
|
72909
|
+
import { defineCommand as defineCommand254 } from "citty";
|
|
71421
72910
|
registerSchema({
|
|
71422
72911
|
command: "winning-ads.winners",
|
|
71423
72912
|
description: "Top winning ads for one advertiser id (from `advertisers` or `following`). Returns lean winner cards; add --full for DNA + longevity.",
|
|
@@ -71427,7 +72916,7 @@ registerSchema({
|
|
|
71427
72916
|
platform: { type: "string", description: "Filter to a single platform: meta|linkedin|tiktok", required: false }
|
|
71428
72917
|
}
|
|
71429
72918
|
});
|
|
71430
|
-
var winnersCommand =
|
|
72919
|
+
var winnersCommand = defineCommand254({
|
|
71431
72920
|
meta: {
|
|
71432
72921
|
name: "winners",
|
|
71433
72922
|
description: "Top winning ads for a specific advertiser id. Example: baker winning-ads winners adv_123 --top 15 --output md"
|
|
@@ -71477,7 +72966,7 @@ var winnersCommand = defineCommand249({
|
|
|
71477
72966
|
});
|
|
71478
72967
|
|
|
71479
72968
|
// src/commands/winning-ads/index.ts
|
|
71480
|
-
var winningAdsCommand =
|
|
72969
|
+
var winningAdsCommand = defineCommand255({
|
|
71481
72970
|
meta: {
|
|
71482
72971
|
name: "winning-ads",
|
|
71483
72972
|
description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce, and manage the brands your library tracks. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
|
|
@@ -71677,8 +73166,8 @@ function suggestFlag(unknown, command) {
|
|
|
71677
73166
|
const name = flag3.slice(2);
|
|
71678
73167
|
const def = args5[name] ?? args5[camel(name)];
|
|
71679
73168
|
const described = def && typeof def === "object" ? def.description : void 0;
|
|
71680
|
-
const
|
|
71681
|
-
return
|
|
73169
|
+
const description2 = typeof described === "string" ? described.split(/(?<!\b(?:e\.g|i\.e|vs))\.\s/)[0]?.trim().replace(/\.$/, "") : "";
|
|
73170
|
+
return description2 ? { flag: flag3, description: description2 } : { flag: flag3 };
|
|
71682
73171
|
}
|
|
71683
73172
|
function suggestionText(suggestion) {
|
|
71684
73173
|
if (!suggestion) {
|
|
@@ -71688,12 +73177,12 @@ function suggestionText(suggestion) {
|
|
|
71688
73177
|
}
|
|
71689
73178
|
var OUTPUT_EXPLANATION = "This command has no --output option: `--output json` is accepted by every command that prints the JSON envelope, and md or files only where `baker schema` lists `output`. Drop the option and read the default output. ";
|
|
71690
73179
|
function unknownFlagEnvelope(unknown, commandPath, suggestion, hasSchema) {
|
|
71691
|
-
const
|
|
73180
|
+
const list3 = unknown.join(", ");
|
|
71692
73181
|
return {
|
|
71693
73182
|
ok: false,
|
|
71694
73183
|
error: {
|
|
71695
73184
|
code: "UNKNOWN_ARGUMENT",
|
|
71696
|
-
message: `${
|
|
73185
|
+
message: `${list3} ${unknown.length > 1 ? "are not options" : "is not an option"} of \`baker ${commandPath}\`.`,
|
|
71697
73186
|
fix: {
|
|
71698
73187
|
action: hasSchema ? "check_schema" : "check_help",
|
|
71699
73188
|
explanation: (unknown.some((flag3) => flag3 === "--output" || flag3.startsWith("--output=")) ? OUTPUT_EXPLANATION : "") + suggestionText(suggestion) + `Run \`${hasSchema ? `baker schema ${commandPath}` : `baker ${commandPath} --help`}\` to see every option this command takes. Do not re-run the same call: until this version an unrecognised option was accepted and ignored, so a command that looked like it had filtered or narrowed the result was in fact answering the unfiltered question.`
|
|
@@ -71804,7 +73293,7 @@ function getCliVersion() {
|
|
|
71804
73293
|
}
|
|
71805
73294
|
|
|
71806
73295
|
// src/cli.ts
|
|
71807
|
-
var main =
|
|
73296
|
+
var main = defineCommand256({
|
|
71808
73297
|
meta: {
|
|
71809
73298
|
name: "baker",
|
|
71810
73299
|
version: getCliVersion(),
|