@koda-sl/baker-cli 0.254.0 → 0.255.0-dev.8030cfcf9
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 +13 -0
- package/dist/{chunk-5JHSX3ZB.js → chunk-6F52WYB7.js} +2 -2
- package/dist/{chunk-JKSMLAQN.js → chunk-GU7IWEBC.js} +7 -3
- package/dist/chunk-GU7IWEBC.js.map +1 -0
- package/dist/{chunk-SJPRTSGY.js → chunk-HBDGUVUH.js} +4 -4
- package/dist/chunk-HBDGUVUH.js.map +1 -0
- package/dist/{chunk-6MAVG6LO.js → chunk-KSOGK7SJ.js} +3 -3
- package/dist/cli.js +92 -21
- package/dist/cli.js.map +1 -1
- package/dist/{client-3TNO43FO.js → client-VDCCDEHE.js} +4 -2
- package/dist/engine/index.js +1 -1
- package/dist/{output-B3OCLLS5.js → output-JBYI5IVX.js} +3 -3
- package/dist/{shared-J24NXBW4.js → shared-GWFXJXKJ.js} +4 -4
- package/package.json +1 -1
- package/dist/chunk-JKSMLAQN.js.map +0 -1
- package/dist/chunk-SJPRTSGY.js.map +0 -1
- /package/dist/{chunk-5JHSX3ZB.js.map → chunk-6F52WYB7.js.map} +0 -0
- /package/dist/{chunk-6MAVG6LO.js.map → chunk-KSOGK7SJ.js.map} +0 -0
- /package/dist/{client-3TNO43FO.js.map → client-VDCCDEHE.js.map} +0 -0
- /package/dist/{output-B3OCLLS5.js.map → output-JBYI5IVX.js.map} +0 -0
- /package/dist/{shared-J24NXBW4.js.map → shared-GWFXJXKJ.js.map} +0 -0
|
@@ -2,10 +2,10 @@ import {
|
|
|
2
2
|
handleConnectionError,
|
|
3
3
|
needsConnectionFix,
|
|
4
4
|
writeAdsJson
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-6F52WYB7.js";
|
|
6
6
|
import {
|
|
7
7
|
ApiError
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-GU7IWEBC.js";
|
|
9
9
|
import {
|
|
10
10
|
getEnv
|
|
11
11
|
} from "./chunk-DZUVUGEP.js";
|
|
@@ -108,4 +108,4 @@ export {
|
|
|
108
108
|
csvOrJson,
|
|
109
109
|
resolveEffectiveStatus
|
|
110
110
|
};
|
|
111
|
-
//# sourceMappingURL=chunk-
|
|
111
|
+
//# sourceMappingURL=chunk-KSOGK7SJ.js.map
|
package/dist/cli.js
CHANGED
|
@@ -58,7 +58,7 @@ import {
|
|
|
58
58
|
ulid,
|
|
59
59
|
validateCanvasDeep,
|
|
60
60
|
ytDlpBlockSignal
|
|
61
|
-
} from "./chunk-
|
|
61
|
+
} from "./chunk-HBDGUVUH.js";
|
|
62
62
|
import {
|
|
63
63
|
csvOrJson,
|
|
64
64
|
daysAgoIso,
|
|
@@ -67,7 +67,7 @@ import {
|
|
|
67
67
|
resolveAccountIdArg,
|
|
68
68
|
resolveEffectiveStatus,
|
|
69
69
|
todayIso
|
|
70
|
-
} from "./chunk-
|
|
70
|
+
} from "./chunk-KSOGK7SJ.js";
|
|
71
71
|
import {
|
|
72
72
|
buildQueryCacheKey,
|
|
73
73
|
cacheGet,
|
|
@@ -83,13 +83,14 @@ import {
|
|
|
83
83
|
writeAdsJson,
|
|
84
84
|
writeAdsOutput,
|
|
85
85
|
writeJsonEnvelope
|
|
86
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-6F52WYB7.js";
|
|
87
87
|
import {
|
|
88
88
|
ApiError,
|
|
89
89
|
apiGet,
|
|
90
|
+
apiGetWithHeaders,
|
|
90
91
|
apiPost,
|
|
91
92
|
validateConvexId
|
|
92
|
-
} from "./chunk-
|
|
93
|
+
} from "./chunk-GU7IWEBC.js";
|
|
93
94
|
import {
|
|
94
95
|
installStreamTaps,
|
|
95
96
|
logInvocation
|
|
@@ -2112,7 +2113,14 @@ var GOOGLE_ADS_LIMITS = {
|
|
|
2112
2113
|
// Google's published headers (`structured-snippet-headers.ts`), the longest
|
|
2113
2114
|
// of which is 29 characters. Only the values are capped.
|
|
2114
2115
|
structuredSnippetValuesMin: 3,
|
|
2115
|
-
structuredSnippetValuesMax: 10
|
|
2116
|
+
structuredSnippetValuesMax: 10,
|
|
2117
|
+
/**
|
|
2118
|
+
* Per VALUE, not per asset. Google rejects a 26-character value with a bare "Too long." at
|
|
2119
|
+
* `structured_snippet_asset.values[i]` — one red row inside an otherwise clean publish, plus
|
|
2120
|
+
* every link that depends on the asset. Confirmed live on v23: "Bases de datos vectoriales"
|
|
2121
|
+
* (26) rejected, its four neighbours accepted.
|
|
2122
|
+
*/
|
|
2123
|
+
structuredSnippetValueMax: 25
|
|
2116
2124
|
},
|
|
2117
2125
|
conversionAction: {
|
|
2118
2126
|
nameMax: 255
|
|
@@ -2272,9 +2280,24 @@ var ASSET_FIELD_TYPES = [
|
|
|
2272
2280
|
"HEADLINE",
|
|
2273
2281
|
"DESCRIPTION",
|
|
2274
2282
|
"LOGO",
|
|
2283
|
+
/**
|
|
2284
|
+
* Kept because a draft staged before {@link IMAGE_ASSET_LINK_MESSAGE} shipped may still carry one,
|
|
2285
|
+
* and because it is the field type an asset-group image occupies. It is refused on an
|
|
2286
|
+
* `assetLink.attach` — see that message for what Google actually does with it.
|
|
2287
|
+
*/
|
|
2275
2288
|
"MARKETING_IMAGE",
|
|
2276
2289
|
"BUSINESS_NAME"
|
|
2277
2290
|
];
|
|
2291
|
+
var IMAGE_ASSET_LINK_FIELD_TYPES = /* @__PURE__ */ new Set([
|
|
2292
|
+
"MARKETING_IMAGE",
|
|
2293
|
+
"SQUARE_MARKETING_IMAGE",
|
|
2294
|
+
"PORTRAIT_MARKETING_IMAGE",
|
|
2295
|
+
"AD_IMAGE"
|
|
2296
|
+
]);
|
|
2297
|
+
var IMAGE_ASSET_LINK_MESSAGE = "Google does not accept an image through an asset link. MARKETING_IMAGE is a Performance Max asset-group slot (use `asset-groups attach`), and AD_IMAGE \u2014 the field type Google names for a Search image extension \u2014 comes back UNSUPPORTED_FIELD_TYPE at campaign and ad group level alike. Image extensions are not reachable through the API: tell the user to add them in the Google Ads interface, and carry on with the rest of the job.";
|
|
2298
|
+
function isImageAssetLinkFieldType(fieldType) {
|
|
2299
|
+
return IMAGE_ASSET_LINK_FIELD_TYPES.has(fieldType);
|
|
2300
|
+
}
|
|
2278
2301
|
var ASSET_GROUP_ASSET_FIELD_TYPES = [
|
|
2279
2302
|
"MARKETING_IMAGE",
|
|
2280
2303
|
"SQUARE_MARKETING_IMAGE",
|
|
@@ -3122,7 +3145,7 @@ var structuredSnippetHeaderSchema = z4.string().min(1).transform((header, ctx) =
|
|
|
3122
3145
|
var structuredSnippetAssetSchema = z4.object({
|
|
3123
3146
|
type: z4.literal("structuredSnippet"),
|
|
3124
3147
|
header: structuredSnippetHeaderSchema,
|
|
3125
|
-
values: z4.array(z4.string().min(1)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax)
|
|
3148
|
+
values: z4.array(z4.string().min(1).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValueMax)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax)
|
|
3126
3149
|
});
|
|
3127
3150
|
var callToActionAssetSchema = z4.object({ type: z4.literal("callToAction"), callToAction: z4.string().min(1) });
|
|
3128
3151
|
var assetCreateSchema = z4.discriminatedUnion("type", [
|
|
@@ -3142,7 +3165,7 @@ var assetUpdateSchema = z4.object({
|
|
|
3142
3165
|
finalUrls: z4.array(httpsUrlSchema2).min(1).optional(),
|
|
3143
3166
|
calloutText: z4.string().min(1).max(GOOGLE_ADS_LIMITS.asset.calloutTextMax).optional(),
|
|
3144
3167
|
header: structuredSnippetHeaderSchema.optional(),
|
|
3145
|
-
values: z4.array(z4.string().min(1)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax).optional(),
|
|
3168
|
+
values: z4.array(z4.string().min(1).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValueMax)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax).optional(),
|
|
3146
3169
|
callToAction: z4.string().min(1).optional(),
|
|
3147
3170
|
text: z4.string().min(1).optional()
|
|
3148
3171
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
@@ -3150,7 +3173,11 @@ var assetLinkAttachSchema = z4.object({
|
|
|
3150
3173
|
level: z4.enum(["campaign", "adGroup", "customer"]),
|
|
3151
3174
|
parent: refSchema.optional(),
|
|
3152
3175
|
asset: refSchema,
|
|
3153
|
-
|
|
3176
|
+
// A plain string, not `z.enum(ASSET_FIELD_TYPES)`, so the image refusal below can answer with
|
|
3177
|
+
// the reason instead of an enum listing. An agent reaching for AD_IMAGE is following Google's
|
|
3178
|
+
// own documentation; "Invalid option: expected one of …" sends it to guess another field type,
|
|
3179
|
+
// which is the failure this whole refusal exists to end.
|
|
3180
|
+
fieldType: z4.string().min(1)
|
|
3154
3181
|
}).superRefine((value, ctx) => {
|
|
3155
3182
|
if (value.level !== "customer" && !value.parent) {
|
|
3156
3183
|
ctx.addIssue({
|
|
@@ -3159,6 +3186,15 @@ var assetLinkAttachSchema = z4.object({
|
|
|
3159
3186
|
message: `parent is required for a ${value.level}-level asset link (--parent-ref)`
|
|
3160
3187
|
});
|
|
3161
3188
|
}
|
|
3189
|
+
if (isImageAssetLinkFieldType(value.fieldType)) {
|
|
3190
|
+
ctx.addIssue({ code: z4.ZodIssueCode.custom, path: ["fieldType"], message: IMAGE_ASSET_LINK_MESSAGE });
|
|
3191
|
+
} else if (!ASSET_FIELD_TYPES.includes(value.fieldType)) {
|
|
3192
|
+
ctx.addIssue({
|
|
3193
|
+
code: z4.ZodIssueCode.custom,
|
|
3194
|
+
path: ["fieldType"],
|
|
3195
|
+
message: `expected one of ${ASSET_FIELD_TYPES.join(" | ")}`
|
|
3196
|
+
});
|
|
3197
|
+
}
|
|
3162
3198
|
});
|
|
3163
3199
|
var assetGroupCreateSchema = z4.object({
|
|
3164
3200
|
campaign: refSchema,
|
|
@@ -6183,7 +6219,10 @@ var testimonialsListRequestSchema = z20.object({
|
|
|
6183
6219
|
status: testimonialStatusSchema.optional(),
|
|
6184
6220
|
sentiment: testimonialSentimentSchema.optional(),
|
|
6185
6221
|
language: z20.string().min(2).max(5).optional(),
|
|
6186
|
-
limit: z20.coerce.number().int().positive().max(200).optional()
|
|
6222
|
+
limit: z20.coerce.number().int().positive().max(200).optional(),
|
|
6223
|
+
// Opaque, from the previous response's `X-Baker-Next-Cursor`. Absent on the
|
|
6224
|
+
// first page; its absence on a response means the corpus is exhausted.
|
|
6225
|
+
cursor: z20.string().optional()
|
|
6187
6226
|
});
|
|
6188
6227
|
var testimonialsListResponseSchema = z20.array(testimonialDocSchema);
|
|
6189
6228
|
var testimonialsGetRequestSchema = z20.object({ id: z20.string().min(1, "Missing id parameter") });
|
|
@@ -14780,7 +14819,7 @@ var assetsUpdateCommand = defineCommand30({
|
|
|
14780
14819
|
"final-urls": { type: "string", description: "Comma-separated sitelink final URLs" },
|
|
14781
14820
|
"callout-text": { type: "string", description: "Callout text" },
|
|
14782
14821
|
header: { type: "string", description: "Structured snippet header" },
|
|
14783
|
-
values: { type: "string", description: "Comma-separated structured snippet values" },
|
|
14822
|
+
values: { type: "string", description: "Comma-separated structured snippet values (3-10, max 25 characters each)" },
|
|
14784
14823
|
cta: { type: "string", description: "Call-to-action text" },
|
|
14785
14824
|
text: { type: "string", description: "Text asset content" }
|
|
14786
14825
|
},
|
|
@@ -14814,7 +14853,9 @@ var assetsCommand = defineCommand30({
|
|
|
14814
14853
|
"Stage an asset (text/image/sitelink/callout/structuredSnippet/\u2026)"
|
|
14815
14854
|
),
|
|
14816
14855
|
update: assetsUpdateCommand,
|
|
14817
|
-
attach: fileCreateCommand("attach", "google.assetLink.attach", "Attach an asset to a campaign/adGroup/customer"
|
|
14856
|
+
attach: fileCreateCommand("attach", "google.assetLink.attach", "Attach an asset to a campaign/adGroup/customer", [
|
|
14857
|
+
"Images are not attachable here at all \u2014 Google refuses every image field type through an asset link, at every level. Image extensions have to be added in the Google Ads interface; say so and carry on. Performance Max asset-group images are a different mechanism and do work (`asset-groups attach`)."
|
|
14858
|
+
]),
|
|
14818
14859
|
detach: statusCommand2("google.assetLink.detach", "asset link")
|
|
14819
14860
|
}
|
|
14820
14861
|
});
|
|
@@ -21014,10 +21055,10 @@ function duplicateCommand2(entity, label) {
|
|
|
21014
21055
|
replace: { type: "boolean", description: "Pause the original once the copy publishes" }
|
|
21015
21056
|
},
|
|
21016
21057
|
run: async ({ args }) => {
|
|
21017
|
-
const { apiPost: apiPost2 } = await import("./client-
|
|
21058
|
+
const { apiPost: apiPost2 } = await import("./client-VDCCDEHE.js");
|
|
21018
21059
|
const { requireChatId: requireChatId2 } = await import("./env-FWMZXMQK.js");
|
|
21019
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
21020
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
21060
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-JBYI5IVX.js");
|
|
21061
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-GWFXJXKJ.js");
|
|
21021
21062
|
try {
|
|
21022
21063
|
const accountId = bareAccountId2(args);
|
|
21023
21064
|
const chatId = requireChatId2();
|
|
@@ -50105,14 +50146,30 @@ function emptyReasonDetail(reason, sourceCount, activeFilters) {
|
|
|
50105
50146
|
}
|
|
50106
50147
|
return `${sourceCount} review source${sourceCount === 1 ? " is" : "s are"} connected but hold no testimonials yet \u2014 the source is there, the rows are not. Ingestion may still be running.`;
|
|
50107
50148
|
}
|
|
50149
|
+
function morePagesHint(resultCount, cursor) {
|
|
50150
|
+
const found = resultCount === 0 ? "This page held no matches" : `This page held ${resultCount}`;
|
|
50151
|
+
return `${found}, and the library continues past it \u2014 so this is one page, not the client's whole set. Do not report it as the corpus and do not conclude anything is missing from it. Read the next page with \`baker testimonials list --cursor ${cursor}\` (same filters), or go straight at the rows you want with \`baker testimonials search <query>\`.`;
|
|
50152
|
+
}
|
|
50153
|
+
function endOfWalkHint(command) {
|
|
50154
|
+
const noun = command === "search" ? "results" : "rows";
|
|
50155
|
+
return `That was the end of the library \u2014 no more ${noun} past the page before this one. Nothing here says the corpus is empty (the earlier pages held it), so answer from what those pages returned rather than re-reading.`;
|
|
50156
|
+
}
|
|
50108
50157
|
async function measureEmptyCorpus({
|
|
50109
50158
|
command,
|
|
50110
50159
|
resultCount,
|
|
50111
|
-
activeFilters
|
|
50160
|
+
activeFilters,
|
|
50161
|
+
nextCursor = null,
|
|
50162
|
+
continued = false
|
|
50112
50163
|
}) {
|
|
50164
|
+
if (nextCursor) {
|
|
50165
|
+
return { hints: [morePagesHint(resultCount, nextCursor)] };
|
|
50166
|
+
}
|
|
50113
50167
|
if (resultCount > 0) {
|
|
50114
50168
|
return { hints: [] };
|
|
50115
50169
|
}
|
|
50170
|
+
if (continued) {
|
|
50171
|
+
return { hints: [endOfWalkHint(command)] };
|
|
50172
|
+
}
|
|
50116
50173
|
if (activeFilters.length > 0) {
|
|
50117
50174
|
const reason2 = "filtered_out";
|
|
50118
50175
|
return {
|
|
@@ -50191,7 +50248,12 @@ registerSchema({
|
|
|
50191
50248
|
},
|
|
50192
50249
|
language: { type: "string", description: "Filter by language code (e.g. en, es)", required: false },
|
|
50193
50250
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false },
|
|
50194
|
-
limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
|
|
50251
|
+
limit: { type: "number", description: "Max results per page (default 50)", required: false, default: 50 },
|
|
50252
|
+
cursor: {
|
|
50253
|
+
type: "string",
|
|
50254
|
+
description: "Read the next page: pass meta.next_cursor from the previous call, with the same filters.",
|
|
50255
|
+
required: false
|
|
50256
|
+
}
|
|
50195
50257
|
}
|
|
50196
50258
|
});
|
|
50197
50259
|
var PARAM_NAMES = {
|
|
@@ -50202,7 +50264,8 @@ var PARAM_NAMES = {
|
|
|
50202
50264
|
sentiment: "sentiment",
|
|
50203
50265
|
language: "language",
|
|
50204
50266
|
tags: "tags",
|
|
50205
|
-
limit: "limit"
|
|
50267
|
+
limit: "limit",
|
|
50268
|
+
cursor: "cursor"
|
|
50206
50269
|
};
|
|
50207
50270
|
function buildListParams(args) {
|
|
50208
50271
|
const params = {};
|
|
@@ -50227,22 +50290,30 @@ var listCommand18 = defineCommand205({
|
|
|
50227
50290
|
sentiment: { type: "string", description: "Filter: positive|neutral|negative", required: false },
|
|
50228
50291
|
language: { type: "string", description: "Filter by language code (e.g. en, es)", required: false },
|
|
50229
50292
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false },
|
|
50230
|
-
limit: { type: "string", description: "Max results (default 50)", required: false },
|
|
50293
|
+
limit: { type: "string", description: "Max results per page (default 50)", required: false },
|
|
50294
|
+
cursor: { type: "string", description: "meta.next_cursor from the previous page", required: false },
|
|
50231
50295
|
output: { type: "string", description: "Output format: json|files|md", required: false, default: "json" },
|
|
50232
50296
|
fields: { type: "string", description: "Comma-separated field names to include", required: false },
|
|
50233
50297
|
full: { type: "boolean", description: "Include full metadata", required: false, default: false }
|
|
50234
50298
|
},
|
|
50235
50299
|
run: async ({ args }) => {
|
|
50236
50300
|
try {
|
|
50237
|
-
const data = await
|
|
50301
|
+
const { data, headers } = await apiGetWithHeaders(
|
|
50302
|
+
"/api/testimonials",
|
|
50303
|
+
buildListParams(args)
|
|
50304
|
+
);
|
|
50305
|
+
const nextCursor = headers.get("x-baker-next-cursor");
|
|
50238
50306
|
const { hints: hints2, empty } = await measureEmptyCorpus({
|
|
50239
50307
|
command: "list",
|
|
50240
50308
|
resultCount: data.length,
|
|
50241
|
-
activeFilters: activeFilterFlags(args, FILTER_FLAGS)
|
|
50309
|
+
activeFilters: activeFilterFlags(args, FILTER_FLAGS),
|
|
50310
|
+
nextCursor,
|
|
50311
|
+
continued: Boolean(args.cursor)
|
|
50242
50312
|
});
|
|
50243
50313
|
const outputFormat = args.output || "json";
|
|
50314
|
+
const meta = { ...empty ? { empty } : {}, ...nextCursor ? { next_cursor: nextCursor } : {} };
|
|
50244
50315
|
writeOutput(
|
|
50245
|
-
{ ok: true, data, ...
|
|
50316
|
+
{ ok: true, data, ...Object.keys(meta).length > 0 ? { meta } : {}, ...hints2.length > 0 ? { hints: hints2 } : {} },
|
|
50246
50317
|
outputFormat,
|
|
50247
50318
|
args.fields ? args.fields.split(",") : void 0,
|
|
50248
50319
|
args.full,
|