@koda-sl/baker-cli 0.365.0-dev.846b2e487 → 0.366.0-dev.1010f7728
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 +8 -18
- package/dist/{chunk-VVQLRFCI.js → chunk-A2LM5EDF.js} +2 -2
- package/dist/chunk-A2LM5EDF.js.map +1 -0
- package/dist/{chunk-SR56QQTD.js → chunk-WXSUOPOU.js} +50 -2
- package/dist/chunk-WXSUOPOU.js.map +1 -0
- package/dist/cli.js +149 -272
- package/dist/cli.js.map +1 -1
- package/dist/{output-OSSBUGT7.js → output-M2DBCZFG.js} +2 -2
- package/dist/{shared-MSI4UMZ3.js → shared-3VVQPSFH.js} +9 -3
- package/package.json +1 -1
- package/dist/chunk-SR56QQTD.js.map +0 -1
- package/dist/chunk-VVQLRFCI.js.map +0 -1
- /package/dist/{output-OSSBUGT7.js.map → output-M2DBCZFG.js.map} +0 -0
- /package/dist/{shared-MSI4UMZ3.js.map → shared-3VVQPSFH.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -71,14 +71,19 @@ import {
|
|
|
71
71
|
ytDlpBlockSignal
|
|
72
72
|
} from "./chunk-K2IKLMCM.js";
|
|
73
73
|
import {
|
|
74
|
+
FIELDS_ARG,
|
|
75
|
+
NAME_CONTAINS_ARG,
|
|
74
76
|
csvOrJson,
|
|
75
77
|
daysAgoIso,
|
|
76
78
|
getDateRange,
|
|
77
79
|
handleMetaError,
|
|
80
|
+
parseFieldsArg,
|
|
81
|
+
projectFields,
|
|
82
|
+
projectListRows,
|
|
78
83
|
resolveAccountIdArg,
|
|
79
84
|
resolveEffectiveStatus,
|
|
80
85
|
todayIso
|
|
81
|
-
} from "./chunk-
|
|
86
|
+
} from "./chunk-WXSUOPOU.js";
|
|
82
87
|
import {
|
|
83
88
|
buildQueryCacheKey,
|
|
84
89
|
cacheGet,
|
|
@@ -94,7 +99,7 @@ import {
|
|
|
94
99
|
writeAdsJson,
|
|
95
100
|
writeAdsOutput,
|
|
96
101
|
writeJsonEnvelope
|
|
97
|
-
} from "./chunk-
|
|
102
|
+
} from "./chunk-A2LM5EDF.js";
|
|
98
103
|
import {
|
|
99
104
|
ApiError,
|
|
100
105
|
apiGet,
|
|
@@ -4394,7 +4399,7 @@ var CAPABILITY_SURFACE_SPECS = {
|
|
|
4394
4399
|
provider: "google-analytics",
|
|
4395
4400
|
resourceNoun: "property",
|
|
4396
4401
|
// Like Tag Manager: staged during the chat, applied for real at publish,
|
|
4397
|
-
//
|
|
4402
|
+
// every time. A connection made before writes existed is
|
|
4398
4403
|
// read-only until it is reconnected, and `access` does NOT say so — it
|
|
4399
4404
|
// reports what the Baker app asks Google for, which is now app-wide
|
|
4400
4405
|
// read-write. The per-company signal is `scopeBlockers`, which compares
|
|
@@ -8538,7 +8543,7 @@ var SURFACE_RULES = [
|
|
|
8538
8543
|
];
|
|
8539
8544
|
var FAN_OUT_NUDGE_THRESHOLD = 2;
|
|
8540
8545
|
var FAN_OUT_THRESHOLD = 3;
|
|
8541
|
-
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
|
|
8546
|
+
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.';
|
|
8542
8547
|
function looksExecutable(name, description) {
|
|
8543
8548
|
const foldedName = fold(name);
|
|
8544
8549
|
const full = `${foldedName}
|
|
@@ -8789,7 +8794,7 @@ var claimCommand = defineCommand({
|
|
|
8789
8794
|
import { defineCommand as defineCommand2 } from "citty";
|
|
8790
8795
|
registerSchema({
|
|
8791
8796
|
command: "actions.complete",
|
|
8792
|
-
description: "Stage completion of an action (it was
|
|
8797
|
+
description: "Stage completion of an action (it was done). Accepts a real action ID (must be claimed) or a temp ID from the same draft. The action becomes completed when the chat is published. --note is required and must describe what you actually did to resolve it (what changed, where, and the outcome) \u2014 it is the client-facing record surfaced by `baker actions log`. Don't use this to drop an action you no longer want \u2014 to remove a staged op use `actions draft remove`, to close an unwanted published action use `actions discard`.",
|
|
8793
8798
|
args: {
|
|
8794
8799
|
id: { type: "string", description: "Action ID or temp ID", required: true },
|
|
8795
8800
|
note: {
|
|
@@ -12529,7 +12534,7 @@ var PLANNER_FIELD_DESCRIPTIONS = {
|
|
|
12529
12534
|
// src/commands/ads/google/keywords/discover.ts
|
|
12530
12535
|
registerSchema({
|
|
12531
12536
|
command: "ads.google.keywords.discover",
|
|
12532
|
-
description: 'Discover keyword ideas from seed keywords or URLs. Returns { source: "keyword_planner_estimate", keywords, total_results, next_page_token? }.
|
|
12537
|
+
description: 'Discover keyword ideas from seed keywords or URLs. Returns { source: "keyword_planner_estimate", keywords, total_results, next_page_token? }. These are Keyword Planner estimates of total market search volume, not this account\'s data: every row carries a source column and every metric is prefixed planner_ (planner_avg_monthly_searches, planner_competition, ...). Never report a planner_ number as impressions, clicks or any account metric \u2014 read those with `baker ads google query` against the account. If --location and --language are omitted, they default to United States (2840) and English (1000); `query_context` shows which were used.',
|
|
12533
12538
|
args: {
|
|
12534
12539
|
"customer-id": {
|
|
12535
12540
|
type: "string",
|
|
@@ -12540,12 +12545,12 @@ registerSchema({
|
|
|
12540
12545
|
url: { type: "string", description: "URL to extract keyword ideas from", required: false },
|
|
12541
12546
|
location: {
|
|
12542
12547
|
type: "string",
|
|
12543
|
-
description: "Geo target ID.
|
|
12548
|
+
description: "Geo target ID. Defaults to 2840 (United States) if omitted \u2014 always set this for non-US markets",
|
|
12544
12549
|
required: false
|
|
12545
12550
|
},
|
|
12546
12551
|
language: {
|
|
12547
12552
|
type: "string",
|
|
12548
|
-
description: "Language ID.
|
|
12553
|
+
description: "Language ID. Defaults to 1000 (English) if omitted \u2014 always set this for non-English markets",
|
|
12549
12554
|
required: false
|
|
12550
12555
|
},
|
|
12551
12556
|
limit: { type: "string", description: "Max keyword ideas (default: 20, max: 100)", required: false, default: 20 },
|
|
@@ -12706,7 +12711,7 @@ var locationsCommand = defineCommand25({
|
|
|
12706
12711
|
import { defineCommand as defineCommand26 } from "citty";
|
|
12707
12712
|
registerSchema({
|
|
12708
12713
|
command: "ads.google.keywords.metrics",
|
|
12709
|
-
description: 'Get historical market metrics for specific keywords. Returns { source: "keyword_planner_estimate", historical_metrics: [...] }.
|
|
12714
|
+
description: 'Get historical market metrics for specific keywords. Returns { source: "keyword_planner_estimate", historical_metrics: [...] }. These are Keyword Planner estimates of total market search volume, not this account\'s data: every row carries a source column and every metric is prefixed planner_ (planner_avg_monthly_searches, planner_competition, ...). Never report a planner_ number as impressions, clicks or any account metric \u2014 read those with `baker ads google query` against the account. If --location and --language are omitted, they default to United States (2840) and English (1000); `query_context` shows which were used.',
|
|
12710
12715
|
args: {
|
|
12711
12716
|
"customer-id": {
|
|
12712
12717
|
type: "string",
|
|
@@ -12716,12 +12721,12 @@ registerSchema({
|
|
|
12716
12721
|
keywords: { type: "string", description: "Comma-separated keywords to analyze (max 200)", required: true },
|
|
12717
12722
|
location: {
|
|
12718
12723
|
type: "string",
|
|
12719
|
-
description: "Geo target ID.
|
|
12724
|
+
description: "Geo target ID. Defaults to 2840 (United States) if omitted \u2014 always set this for non-US markets",
|
|
12720
12725
|
required: false
|
|
12721
12726
|
},
|
|
12722
12727
|
language: {
|
|
12723
12728
|
type: "string",
|
|
12724
|
-
description: "Language ID.
|
|
12729
|
+
description: "Language ID. Defaults to 1000 (English) if omitted \u2014 always set this for non-English markets",
|
|
12725
12730
|
required: false
|
|
12726
12731
|
},
|
|
12727
12732
|
output: { type: "string", description: "Format: json|csv|jsonl|md", required: false, default: "json" },
|
|
@@ -13635,7 +13640,7 @@ function completenessWarnings(read) {
|
|
|
13635
13640
|
return [
|
|
13636
13641
|
{
|
|
13637
13642
|
code: "TRUNCATED_RESULTS",
|
|
13638
|
-
message: `INCOMPLETE READ \u2014 ${cause} These rows are a page, not the data set.
|
|
13643
|
+
message: `INCOMPLETE READ \u2014 ${cause} These rows are a page, not the data set. Don't count, total or describe them as the whole picture. ${fix}`
|
|
13639
13644
|
}
|
|
13640
13645
|
];
|
|
13641
13646
|
}
|
|
@@ -18212,6 +18217,7 @@ var fileArg = {
|
|
|
18212
18217
|
description: "The op payload: a JSON file path, or the JSON object inline (flags override it)"
|
|
18213
18218
|
}
|
|
18214
18219
|
};
|
|
18220
|
+
var STAGED_WRITE = "Stages only \u2014 applies to the live account when the chat is published. Field contract: `baker capabilities google-ads`.";
|
|
18215
18221
|
var BUDGET_DEMAND_HINTS = [
|
|
18216
18222
|
"Check this daily budget against the search volume you measured for these keywords, not against the plan you had before you measured it. A budget many times larger than the traffic the target pool can absorb never spends, and reads to the client as a number nobody costed.",
|
|
18217
18223
|
"No volume measured yet? Pull it first \u2014 `baker ads google query --preset search-terms` for what the account already captures, `baker ads google keywords metrics` for the market estimate (those are planner figures, never account performance)."
|
|
@@ -18223,11 +18229,14 @@ var budgetsCommand = defineCommand31({
|
|
|
18223
18229
|
// first. A budget several times what the target pool can absorb never spends, and reads to the
|
|
18224
18230
|
// client as a plan nobody costed.
|
|
18225
18231
|
create: defineCommand31({
|
|
18226
|
-
meta: {
|
|
18232
|
+
meta: {
|
|
18233
|
+
name: "create",
|
|
18234
|
+
description: `Stage a daily campaign budget; campaigns reference it by the g_temp_* ref it returns (--budget-ref). ${STAGED_WRITE}`
|
|
18235
|
+
},
|
|
18227
18236
|
args: {
|
|
18228
18237
|
...customerIdArg,
|
|
18229
18238
|
...fileArg,
|
|
18230
|
-
name: { type: "string", description:
|
|
18239
|
+
name: { type: "string", description: 'Budget name as shown in Google Ads (e.g. "Brand search \u2014 daily")' },
|
|
18231
18240
|
amount: { type: "string", description: "Daily amount in major currency units (e.g. 50)" },
|
|
18232
18241
|
delivery: { type: "string", description: "STANDARD | ACCELERATED" },
|
|
18233
18242
|
shared: { type: "boolean", description: "Explicitly shared budget" }
|
|
@@ -18244,11 +18253,14 @@ var budgetsCommand = defineCommand31({
|
|
|
18244
18253
|
}
|
|
18245
18254
|
}),
|
|
18246
18255
|
update: defineCommand31({
|
|
18247
|
-
meta: {
|
|
18256
|
+
meta: {
|
|
18257
|
+
name: "update",
|
|
18258
|
+
description: `Stage a change to an existing budget's name, daily amount or delivery. ${STAGED_WRITE}`
|
|
18259
|
+
},
|
|
18248
18260
|
args: {
|
|
18249
18261
|
...customerIdArg,
|
|
18250
18262
|
...fileArg,
|
|
18251
|
-
name: { type: "string" },
|
|
18263
|
+
name: { type: "string", description: "New budget name as shown in Google Ads" },
|
|
18252
18264
|
amount: { type: "string", description: "New daily amount in major currency units" },
|
|
18253
18265
|
delivery: { type: "string" }
|
|
18254
18266
|
},
|
|
@@ -18457,11 +18469,14 @@ var campaignsCommand = defineCommand31({
|
|
|
18457
18469
|
meta: { name: "campaigns", description: "Stage campaign create/update/pause/resume/remove" },
|
|
18458
18470
|
subCommands: {
|
|
18459
18471
|
create: defineCommand31({
|
|
18460
|
-
meta: {
|
|
18472
|
+
meta: {
|
|
18473
|
+
name: "create",
|
|
18474
|
+
description: `Stage a new campaign on a budget (--budget-ref); it is created paused unless --status ENABLED. ${STAGED_WRITE}`
|
|
18475
|
+
},
|
|
18461
18476
|
args: {
|
|
18462
18477
|
...customerIdArg,
|
|
18463
18478
|
...fileArg,
|
|
18464
|
-
name: { type: "string" },
|
|
18479
|
+
name: { type: "string", description: 'Campaign name, unique in the account (e.g. "Search \u2014 Brand \u2014 ES")' },
|
|
18465
18480
|
"channel-type": {
|
|
18466
18481
|
type: "string",
|
|
18467
18482
|
description: "SEARCH | DISPLAY | SHOPPING | VIDEO | PERFORMANCE_MAX | DEMAND_GEN | \u2026"
|
|
@@ -18524,11 +18539,14 @@ var campaignsCommand = defineCommand31({
|
|
|
18524
18539
|
}
|
|
18525
18540
|
}),
|
|
18526
18541
|
update: defineCommand31({
|
|
18527
|
-
meta: {
|
|
18542
|
+
meta: {
|
|
18543
|
+
name: "update",
|
|
18544
|
+
description: `Stage a change to an existing campaign's name, budget, bidding or settings. ${STAGED_WRITE}`
|
|
18545
|
+
},
|
|
18528
18546
|
args: {
|
|
18529
18547
|
...customerIdArg,
|
|
18530
18548
|
...fileArg,
|
|
18531
|
-
name: { type: "string" },
|
|
18549
|
+
name: { type: "string", description: "New campaign name, unique in the account" },
|
|
18532
18550
|
"budget-ref": { type: "string" },
|
|
18533
18551
|
"bidding-strategy": {
|
|
18534
18552
|
type: "string",
|
|
@@ -18608,15 +18626,21 @@ var adGroupsCommand = defineCommand31({
|
|
|
18608
18626
|
meta: { name: "ad-groups", description: "Stage ad group create/update/pause/resume/remove" },
|
|
18609
18627
|
subCommands: {
|
|
18610
18628
|
create: defineCommand31({
|
|
18611
|
-
meta: {
|
|
18629
|
+
meta: {
|
|
18630
|
+
name: "create",
|
|
18631
|
+
description: `Stage a new ad group in a campaign (--campaign-ref); it is created paused unless --status ENABLED. ${STAGED_WRITE}`
|
|
18632
|
+
},
|
|
18612
18633
|
args: {
|
|
18613
18634
|
...customerIdArg,
|
|
18614
18635
|
...fileArg,
|
|
18615
|
-
name: {
|
|
18636
|
+
name: {
|
|
18637
|
+
type: "string",
|
|
18638
|
+
description: 'Ad group name, unique within its campaign (e.g. "Solar panels \u2014 price")'
|
|
18639
|
+
},
|
|
18616
18640
|
"campaign-ref": { type: "string", description: "Campaign ref (g_temp_*, id, or resource name)" },
|
|
18617
18641
|
type: {
|
|
18618
18642
|
type: "string",
|
|
18619
|
-
description: "Ad group type \u2014 leave it out and Google derives it from the campaign's channel, which is
|
|
18643
|
+
description: "Ad group type \u2014 leave it out and Google derives it from the campaign's channel, which is required on a Demand Gen campaign (there is no Demand Gen ad group type). SEARCH_STANDARD | DISPLAY_STANDARD | SHOPPING_PRODUCT_ADS | VIDEO_*"
|
|
18620
18644
|
},
|
|
18621
18645
|
"cpc-bid": { type: "string", description: "Default CPC bid in major units" },
|
|
18622
18646
|
status: { type: "string" }
|
|
@@ -18634,11 +18658,14 @@ var adGroupsCommand = defineCommand31({
|
|
|
18634
18658
|
}
|
|
18635
18659
|
}),
|
|
18636
18660
|
update: defineCommand31({
|
|
18637
|
-
meta: {
|
|
18661
|
+
meta: {
|
|
18662
|
+
name: "update",
|
|
18663
|
+
description: `Stage a change to an existing ad group's name, default CPC bid or status. ${STAGED_WRITE}`
|
|
18664
|
+
},
|
|
18638
18665
|
args: {
|
|
18639
18666
|
...customerIdArg,
|
|
18640
18667
|
...fileArg,
|
|
18641
|
-
name: { type: "string" },
|
|
18668
|
+
name: { type: "string", description: "New ad group name, unique within its campaign" },
|
|
18642
18669
|
"cpc-bid": { type: "string" },
|
|
18643
18670
|
status: { type: "string" }
|
|
18644
18671
|
},
|
|
@@ -19416,7 +19443,7 @@ var assetsCommand = defineCommand31({
|
|
|
19416
19443
|
),
|
|
19417
19444
|
update: assetsUpdateCommand,
|
|
19418
19445
|
attach: fileCreateCommand("attach", "google.assetLink.attach", "Attach an asset to a campaign/adGroup/customer", [
|
|
19419
|
-
"Image extensions cannot be linked \u2014 Google refuses AD_IMAGE and every marketing-image field type at every level, on every API version \u2014 but stage the link anyway: it is skipped rather than stored, the rest of a batch is unaffected, and it is what records the handover Task naming those images and that campaign. That is the
|
|
19446
|
+
"Image extensions cannot be linked \u2014 Google refuses AD_IMAGE and every marketing-image field type at every level, on every API version \u2014 but stage the link anyway: it is skipped rather than stored, the rest of a batch is unaffected, and it is what records the handover Task naming those images and that campaign. That is the only record when the images are already in the account and you are uploading nothing. Then tell the user they are already in their Google Ads account and only need adding to the campaign under Assets; do not stage a second Task with `baker actions`. Two images that do link: the advertiser's logo through this command (`BUSINESS_LOGO`, campaign or customer level, on a verified account), and Performance Max asset-group images, which are a different mechanism (`asset-groups attach`)."
|
|
19420
19447
|
]),
|
|
19421
19448
|
detach: statusCommand2("google.assetLink.detach", "asset link", [
|
|
19422
19449
|
"Pass the resource name Google gave you \u2014 `SELECT campaign_asset.resource_name \u2026 FROM campaign_asset WHERE campaign_asset.status != 'REMOVED'` (or `ad_group_asset` / `customer_asset`) \u2014 never one composed out of an owner id and an asset id. A link's own status is not its campaign's and not its ad group's, so an inventory read scoped only to those lists extensions that are already detached, and a link between an asset and an owner that never held it parses fine and exists nowhere. Either way the target is read back before it is staged, and a detach of a link that is not there is refused here rather than at publish."
|
|
@@ -19426,14 +19453,14 @@ var assetsCommand = defineCommand31({
|
|
|
19426
19453
|
var audienceModeArg = {
|
|
19427
19454
|
mode: {
|
|
19428
19455
|
type: "string",
|
|
19429
|
-
description: "observation = the audience only adjusts bids, reach is unchanged | targeting = the ads are shown
|
|
19456
|
+
description: "observation = the audience only adjusts bids, reach is unchanged | targeting = the ads are shown only to people in the audiences attached to this campaign/ad group"
|
|
19430
19457
|
}
|
|
19431
19458
|
};
|
|
19432
19459
|
function requireAudienceMode(value) {
|
|
19433
19460
|
const mode = typeof value === "string" ? value.toLowerCase() : void 0;
|
|
19434
19461
|
if (mode !== "observation" && mode !== "targeting") {
|
|
19435
19462
|
failWriteValidation(
|
|
19436
|
-
`pass --mode observation (the audience adjusts bids and reach is unchanged \u2014 the Google Ads interface's default) or --mode targeting (the ads are shown
|
|
19463
|
+
`pass --mode observation (the audience adjusts bids and reach is unchanged \u2014 the Google Ads interface's default) or --mode targeting (the ads are shown only to people in the audiences on this campaign/ad group). There is no safe default: the API's own default is targeting, which can cut a campaign's reach to the size of the list.`
|
|
19437
19464
|
);
|
|
19438
19465
|
}
|
|
19439
19466
|
return mode === "observation" ? "OBSERVATION" : "TARGETING";
|
|
@@ -22976,27 +23003,6 @@ function compactRow(row) {
|
|
|
22976
23003
|
...destination.postUrn ? { postUrn: destination.postUrn } : {}
|
|
22977
23004
|
};
|
|
22978
23005
|
}
|
|
22979
|
-
function readPath2(row, path48) {
|
|
22980
|
-
let current = row;
|
|
22981
|
-
for (const segment of path48.split(".")) {
|
|
22982
|
-
const record = asRecord(current);
|
|
22983
|
-
if (!record) return void 0;
|
|
22984
|
-
current = record[segment];
|
|
22985
|
-
}
|
|
22986
|
-
return current;
|
|
22987
|
-
}
|
|
22988
|
-
function projectFields(rows, paths) {
|
|
22989
|
-
return rows.map((row) => {
|
|
22990
|
-
const projected = {};
|
|
22991
|
-
for (const path48 of paths) {
|
|
22992
|
-
const value = readPath2(row, path48);
|
|
22993
|
-
if (value !== void 0) {
|
|
22994
|
-
projected[path48] = value;
|
|
22995
|
-
}
|
|
22996
|
-
}
|
|
22997
|
-
return projected;
|
|
22998
|
-
});
|
|
22999
|
-
}
|
|
23000
23006
|
function unmappedShapeWarnings(rows, rawContentReturned) {
|
|
23001
23007
|
const unmapped = rows.filter((row) => row.content && creativeDestination(row.content).format === "unknown");
|
|
23002
23008
|
if (unmapped.length === 0) return [];
|
|
@@ -23010,12 +23016,9 @@ function unmappedShapeWarnings(rows, rawContentReturned) {
|
|
|
23010
23016
|
}
|
|
23011
23017
|
function shapeRows(rows, args5) {
|
|
23012
23018
|
const raw = rows;
|
|
23013
|
-
const
|
|
23014
|
-
if (
|
|
23015
|
-
|
|
23016
|
-
if (paths.length > 0) {
|
|
23017
|
-
return { rows: projectFields(raw, paths), fields: paths };
|
|
23018
|
-
}
|
|
23019
|
+
const paths = parseFieldsArg(args5.fields);
|
|
23020
|
+
if (paths) {
|
|
23021
|
+
return { rows: projectFields(raw, paths), fields: paths };
|
|
23019
23022
|
}
|
|
23020
23023
|
if (args5.full) return { rows: raw };
|
|
23021
23024
|
return { rows: rows.map(compactRow), fields: [...COMPACT_COLUMNS] };
|
|
@@ -23927,7 +23930,7 @@ Performance \u2014 the workhorse (3 axes):
|
|
|
23927
23930
|
baker ads linkedin analytics --list-intents
|
|
23928
23931
|
baker ads linkedin analytics --list-pivots
|
|
23929
23932
|
|
|
23930
|
-
Staged writes (
|
|
23933
|
+
Staged writes (never hit LinkedIn directly \u2014 ops stage on this chat's draft,
|
|
23931
23934
|
show in the dashboard as pending LinkedIn changes, and apply when the chat is
|
|
23932
23935
|
published, at which point they reach the real ad account):
|
|
23933
23936
|
baker ads linkedin account url-params \u2014 UTMs for every ad in the account
|
|
@@ -26359,8 +26362,8 @@ Example: baker ads meta ads retag 120252158038400247 --url-tags "utm_source={{si
|
|
|
26359
26362
|
run: async ({ args: args5 }) => {
|
|
26360
26363
|
const { apiPost: apiPost2 } = await import("./client-3DGBQ2H3.js");
|
|
26361
26364
|
const { requireChatId: requireChatId2 } = await import("./env-BSG7TY5Y.js");
|
|
26362
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
26363
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
26365
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-M2DBCZFG.js");
|
|
26366
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-3VVQPSFH.js");
|
|
26364
26367
|
try {
|
|
26365
26368
|
const accountId = bareAccountId2(args5);
|
|
26366
26369
|
const urlTags = typeof args5["url-tags"] === "string" ? args5["url-tags"] : void 0;
|
|
@@ -26507,8 +26510,8 @@ function duplicateCommand2(entity, label) {
|
|
|
26507
26510
|
run: async ({ args: args5 }) => {
|
|
26508
26511
|
const { apiPost: apiPost2 } = await import("./client-3DGBQ2H3.js");
|
|
26509
26512
|
const { requireChatId: requireChatId2 } = await import("./env-BSG7TY5Y.js");
|
|
26510
|
-
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-
|
|
26511
|
-
const { handleMetaError: handleMetaError2 } = await import("./shared-
|
|
26513
|
+
const { writeJsonEnvelope: writeJsonEnvelope2 } = await import("./output-M2DBCZFG.js");
|
|
26514
|
+
const { handleMetaError: handleMetaError2 } = await import("./shared-3VVQPSFH.js");
|
|
26512
26515
|
try {
|
|
26513
26516
|
const accountId = bareAccountId2(args5);
|
|
26514
26517
|
const chatId = requireChatId2();
|
|
@@ -26547,6 +26550,8 @@ var listArgs4 = {
|
|
|
26547
26550
|
"campaign-id": { type: "string", description: "Filter to one campaign" },
|
|
26548
26551
|
"effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
|
|
26549
26552
|
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
|
|
26553
|
+
"name-contains": NAME_CONTAINS_ARG,
|
|
26554
|
+
fields: FIELDS_ARG,
|
|
26550
26555
|
limit: { type: "string", description: "Max rows (default 1000)" },
|
|
26551
26556
|
"no-cache": NO_CACHE_ARG,
|
|
26552
26557
|
output: { type: "string", description: "Output format", default: "json" }
|
|
@@ -26565,15 +26570,17 @@ var listCommand4 = defineCommand58({
|
|
|
26565
26570
|
if (args5["adset-id"]) params["adset-id"] = args5["adset-id"];
|
|
26566
26571
|
if (args5["campaign-id"]) params["campaign-id"] = args5["campaign-id"];
|
|
26567
26572
|
if (effectiveStatus) params["effective-status"] = effectiveStatus;
|
|
26573
|
+
if (args5["name-contains"]) params["name-contains"] = args5["name-contains"];
|
|
26568
26574
|
if (args5.limit) params.limit = args5.limit;
|
|
26569
26575
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
26570
26576
|
const data = await apiGet("/api/ads/meta/ads", params);
|
|
26577
|
+
const { rows, fields } = projectListRows(data, args5);
|
|
26571
26578
|
const fmt = csvOrJson(args5);
|
|
26572
26579
|
if (fmt !== "json") {
|
|
26573
|
-
writeAdsOutput(
|
|
26580
|
+
writeAdsOutput(rows, fmt, fields);
|
|
26574
26581
|
return;
|
|
26575
26582
|
}
|
|
26576
|
-
writeAdsJson({ ok: true, data });
|
|
26583
|
+
writeAdsJson({ ok: true, data: rows });
|
|
26577
26584
|
} catch (err) {
|
|
26578
26585
|
handleMetaError(err);
|
|
26579
26586
|
}
|
|
@@ -26590,6 +26597,7 @@ The dashboard shows effective_status (e.g. WITH_ISSUES, PENDING_REVIEW, DISAPPRO
|
|
|
26590
26597
|
Examples:
|
|
26591
26598
|
baker ads meta ads --account-id act_123
|
|
26592
26599
|
baker ads meta ads --adset-id 6123
|
|
26600
|
+
baker ads meta ads --account-id act_123 --all-statuses --name-contains octubre --fields id,name,adset_id,creative.id \u2014 find one ad by name
|
|
26593
26601
|
baker ads meta ads create --name "Ad 1" --adset meta_temp_as01 --creative meta_temp_cr01
|
|
26594
26602
|
baker ads meta ads pause 6123 --account-id act_123
|
|
26595
26603
|
baker ads meta ads retag 6123 --set "utm_medium=paid-social" \u2014 fix tracking on a LIVE ad, keeping everything else`
|
|
@@ -26619,6 +26627,8 @@ var listArgs5 = {
|
|
|
26619
26627
|
"campaign-id": { type: "string", description: "Filter to one campaign" },
|
|
26620
26628
|
"effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
|
|
26621
26629
|
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter" },
|
|
26630
|
+
"name-contains": NAME_CONTAINS_ARG,
|
|
26631
|
+
fields: FIELDS_ARG,
|
|
26622
26632
|
limit: { type: "string", description: "Max rows (default 1000)" },
|
|
26623
26633
|
"no-cache": NO_CACHE_ARG,
|
|
26624
26634
|
output: { type: "string", description: "Output format", default: "json" }
|
|
@@ -26636,15 +26646,17 @@ var listCommand5 = defineCommand59({
|
|
|
26636
26646
|
const params = { "account-id": accountId };
|
|
26637
26647
|
if (args5["campaign-id"]) params["campaign-id"] = args5["campaign-id"];
|
|
26638
26648
|
if (effectiveStatus) params["effective-status"] = effectiveStatus;
|
|
26649
|
+
if (args5["name-contains"]) params["name-contains"] = args5["name-contains"];
|
|
26639
26650
|
if (args5.limit) params.limit = args5.limit;
|
|
26640
26651
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
26641
26652
|
const data = await apiGet("/api/ads/meta/adsets", params);
|
|
26653
|
+
const { rows, fields } = projectListRows(data, args5);
|
|
26642
26654
|
const fmt = csvOrJson(args5);
|
|
26643
26655
|
if (fmt !== "json") {
|
|
26644
|
-
writeAdsOutput(
|
|
26656
|
+
writeAdsOutput(rows, fmt, fields);
|
|
26645
26657
|
return;
|
|
26646
26658
|
}
|
|
26647
|
-
writeAdsJson({ ok: true, data });
|
|
26659
|
+
writeAdsJson({ ok: true, data: rows });
|
|
26648
26660
|
} catch (err) {
|
|
26649
26661
|
handleMetaError(err);
|
|
26650
26662
|
}
|
|
@@ -26659,6 +26671,7 @@ Defaults to ACTIVE only. Write subcommands stage ops on this chat's draft.
|
|
|
26659
26671
|
Examples:
|
|
26660
26672
|
baker ads meta adsets --account-id act_123
|
|
26661
26673
|
baker ads meta adsets --campaign-id 6123456789
|
|
26674
|
+
baker ads meta adsets --account-id act_123 --all-statuses --name-contains retargeting --fields id,name,campaign_id \u2014 find one ad set by name
|
|
26662
26675
|
baker ads meta adsets create --campaign meta_temp_ab12 --name "US 25-54" --optimization-goal LINK_CLICKS --billing-event IMPRESSIONS --daily-budget 50 --targeting-file t.json`
|
|
26663
26676
|
},
|
|
26664
26677
|
subCommands: {
|
|
@@ -26770,6 +26783,8 @@ var listArgs7 = {
|
|
|
26770
26783
|
"account-id": { type: "string", description: "Meta ad account ID" },
|
|
26771
26784
|
"effective-status": { type: "string", description: "Comma-separated effective_status filter (overrides default)" },
|
|
26772
26785
|
"all-statuses": { type: "boolean", description: "Drop the default ACTIVE filter and return everything" },
|
|
26786
|
+
"name-contains": NAME_CONTAINS_ARG,
|
|
26787
|
+
fields: FIELDS_ARG,
|
|
26773
26788
|
limit: { type: "string", description: "Max rows (default 1000)" },
|
|
26774
26789
|
"no-cache": NO_CACHE_ARG,
|
|
26775
26790
|
output: { type: "string", description: "Output format", default: "json" }
|
|
@@ -26786,15 +26801,17 @@ var listCommand7 = defineCommand62({
|
|
|
26786
26801
|
try {
|
|
26787
26802
|
const params = { "account-id": accountId };
|
|
26788
26803
|
if (effectiveStatus) params["effective-status"] = effectiveStatus;
|
|
26804
|
+
if (args5["name-contains"]) params["name-contains"] = args5["name-contains"];
|
|
26789
26805
|
if (args5.limit) params.limit = args5.limit;
|
|
26790
26806
|
if (args5["no-cache"]) params["skip-cache"] = "true";
|
|
26791
26807
|
const data = await apiGet("/api/ads/meta/campaigns", params);
|
|
26808
|
+
const { rows, fields } = projectListRows(data, args5);
|
|
26792
26809
|
const fmt = csvOrJson(args5);
|
|
26793
26810
|
if (fmt !== "json") {
|
|
26794
|
-
writeAdsOutput(
|
|
26811
|
+
writeAdsOutput(rows, fmt, fields);
|
|
26795
26812
|
return;
|
|
26796
26813
|
}
|
|
26797
|
-
writeAdsJson({ ok: true, data });
|
|
26814
|
+
writeAdsJson({ ok: true, data: rows });
|
|
26798
26815
|
} catch (err) {
|
|
26799
26816
|
handleMetaError(err);
|
|
26800
26817
|
}
|
|
@@ -26813,6 +26830,7 @@ The dashboard shows effective_status: ACTIVE, PAUSED, DELETED, ARCHIVED, IN_PROC
|
|
|
26813
26830
|
Examples:
|
|
26814
26831
|
baker ads meta campaigns --account-id act_123 # ACTIVE only
|
|
26815
26832
|
baker ads meta campaigns --account-id act_123 --all-statuses # everything
|
|
26833
|
+
baker ads meta campaigns --account-id act_123 --all-statuses --name-contains Q3 --fields id,name,status # find one by name
|
|
26816
26834
|
baker ads meta campaigns create --account-id act_123 --name "Q3" --objective OUTCOME_LEADS --daily-budget 50
|
|
26817
26835
|
baker ads meta campaigns pause 6123 --account-id act_123`
|
|
26818
26836
|
},
|
|
@@ -33025,7 +33043,7 @@ var sourcesResponseSchema = z40.strictObject({
|
|
|
33025
33043
|
import { defineCommand as defineCommand98 } from "citty";
|
|
33026
33044
|
|
|
33027
33045
|
// src/commands/hubspot/refusal.ts
|
|
33028
|
-
var CONTINUE = "
|
|
33046
|
+
var CONTINUE = "Keep going with the wider task: say which part you could not read, skip it, and finish everything else. If the `request_connection` call fails, say so plainly and give the exact route: dashboard \u2192 Brain \u2192 Integrations \u2192 Tools \u2192 HubSpot.";
|
|
33029
33047
|
function hubspotRefusal(code, message) {
|
|
33030
33048
|
if (code === "NOT_FOUND" && message.includes(HUBSPOT_NOT_CONNECTED_MESSAGE)) {
|
|
33031
33049
|
return {
|
|
@@ -33041,7 +33059,7 @@ function hubspotRefusal(code, message) {
|
|
|
33041
33059
|
code: "HUBSPOT_RECONNECT_REQUIRED",
|
|
33042
33060
|
fix: {
|
|
33043
33061
|
action: "request_connection",
|
|
33044
|
-
explanation: 'AGENT INSTRUCTION: HubSpot
|
|
33062
|
+
explanation: 'AGENT INSTRUCTION: HubSpot is connected \u2014 do not tell the user it is missing, and do not retry. Its access does not cover this read (it was connected before Baker asked for it, or the access was withdrawn), and only the user can widen it. Call the `request_connection` tool with { platform: "hubspot", reason } naming what you need to read; the card offers them a Reconnect button. Run this command again once they have. ' + CONTINUE
|
|
33045
33063
|
}
|
|
33046
33064
|
};
|
|
33047
33065
|
}
|
|
@@ -34544,8 +34562,8 @@ import { defineCommand as defineCommand105 } from "citty";
|
|
|
34544
34562
|
import { defineCommand as defineCommand100 } from "citty";
|
|
34545
34563
|
|
|
34546
34564
|
// src/commands/avatars/casting.ts
|
|
34547
|
-
var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate`, `baker studio animate`, `baker canvas scaffold-ad`, `baker canvas scaffold-static-ad` and `baker canvas scaffold-video`.
|
|
34548
|
-
var VERBATIM_RULE = "When you do write the description yourself \u2014 a hand-built canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription`
|
|
34565
|
+
var CAST_FLAG_RULE = "Cast with `--avatar <handle>` on `baker studio generate`, `baker studio animate`, `baker canvas scaffold-ad`, `baker canvas scaffold-static-ad` and `baker canvas scaffold-video`. Don't hand-roll it by pasting `subjectDescription` into the prompt or by passing the sheet through `--reference` \u2014 both render something that merely resembles them. `--avatar` is also the only thing that carries their accent and delivery into a clip. An avatar has no pinned voice by design (ADR 0005): the video model renders the voice, and the region on their profile is the only thing steering it \u2014 without the flag the clip is performed flat, in whatever accent the model picks on its own. `baker canvas scaffold-video` casts only their face: a reproduced video keeps the source ad's accent direction, so never tell the user it speaks in theirs.";
|
|
34566
|
+
var VERBATIM_RULE = "When you do write the description yourself \u2014 a hand-built canvas node, a landing image, anywhere `--avatar` does not exist \u2014 copy `subjectDescription` verbatim, word for word. Re-phrasing it per generation is the other reason a face drifts across a set.";
|
|
34549
34567
|
var VIDEO_ROUTING = "In video, a photoreal presenter renders on `google/veo-3.1` (or `google/veo-3.1-fast`), never `bytedance/seedance-2.0` \u2014 it refuses photoreal human faces, AI-generated ones included. Reproducing a video the user gave you: `baker canvas scaffold-video \u2026 --avatar <handle> --real-face`.";
|
|
34550
34568
|
function castingHints(avatar) {
|
|
34551
34569
|
if (avatar.status === "generating" && avatar.needsFace) {
|
|
@@ -34639,7 +34657,7 @@ function creationHints(created, profile) {
|
|
|
34639
34657
|
const casting = created.likeness === "synthetic" && created.faceChoice === "auto" ? [
|
|
34640
34658
|
`No one was asked about this face \u2014 \`--face auto\` cast it and it is kept. Do not tell the user they chose it, and do not offer them a pick you are not going to run. If this should have been theirs, say so and create it again with \`--face ask\`.`
|
|
34641
34659
|
] : created.likeness === "synthetic" ? [
|
|
34642
|
-
`The user is being shown real portraits of @${created.handle} right now, and the turn waits for them to pick \u2014 this happens on its own, triggered by the command you just ran.
|
|
34660
|
+
`The user is being shown real portraits of @${created.handle} right now, and the turn waits for them to pick \u2014 this happens on its own, triggered by the command you just ran. The picker is already showing, so don't call request_avatar_casting, don't ask about the face in a question of your own, and don't wait or poll: whatever you do next simply runs after they have chosen. Say what you created and get on with the work.`
|
|
34643
34661
|
] : [];
|
|
34644
34662
|
const hints2 = [
|
|
34645
34663
|
...casting,
|
|
@@ -34725,7 +34743,7 @@ function avatarFailureFix(err, handle, context = {}) {
|
|
|
34725
34743
|
if (err.code === "CONFLICT" && handle) {
|
|
34726
34744
|
return {
|
|
34727
34745
|
action: "use_the_existing_avatar",
|
|
34728
|
-
explanation: `@${handle} already exists in
|
|
34746
|
+
explanation: `@${handle} already exists in this company \u2014 avatars belong to the company, not to a chat, so one made in an earlier conversation is still here and still castable. If it is the person you want, use it as it is: \`--avatar ${handle}\`. Read it first with \`baker avatars get ${handle} --full\` and say who they already are. Do not create the same person under a second name: the user would be told about one avatar and handed another, and the company ends up with duplicates nobody chose a face for. If you genuinely need a different person, ask the user what to call them.`
|
|
34729
34747
|
};
|
|
34730
34748
|
}
|
|
34731
34749
|
return void 0;
|
|
@@ -34827,7 +34845,7 @@ registerSchema({
|
|
|
34827
34845
|
},
|
|
34828
34846
|
subject: {
|
|
34829
34847
|
type: "string",
|
|
34830
|
-
description: "The identity, reused
|
|
34848
|
+
description: "The identity, reused verbatim in every render: one person, plainly described (age, build, hair, skin, face). No scene, no lighting, no camera, no wardrobe \u2014 those belong in the render prompt or in --wardrobe/--setting.",
|
|
34831
34849
|
required: true
|
|
34832
34850
|
},
|
|
34833
34851
|
persona: { type: "string", description: "Who they are on camera \u2014 role, attitude, energy", required: false },
|
|
@@ -35055,7 +35073,7 @@ async function waitForSheet(first, read, sleep4 = (ms) => new Promise((resolve5)
|
|
|
35055
35073
|
// src/commands/avatars/get.ts
|
|
35056
35074
|
registerSchema({
|
|
35057
35075
|
command: "avatars.get",
|
|
35058
|
-
description: "Read one avatar by handle \u2014 the command you run before casting it. Returns its status (only `ready` can be cast), the identity sheet URL every render must be grounded on via --reference, and the subject description that goes into the prompt
|
|
35076
|
+
description: "Read one avatar by handle \u2014 the command you run before casting it. Returns its status (only `ready` can be cast), the identity sheet URL every render must be grounded on via --reference, and the subject description that goes into the prompt verbatim. Add --full for the whole written profile (persona, speech, motion, wardrobe, setting, guardrails, voice, source photos).",
|
|
35059
35077
|
args: {
|
|
35060
35078
|
handle: { type: "string", description: 'Avatar handle, e.g. "marta" (lowercase, dashes)', required: true },
|
|
35061
35079
|
full: {
|
|
@@ -35304,7 +35322,7 @@ How casting works:
|
|
|
35304
35322
|
2. baker avatars get <handle> Status, identity sheet URL, subject description
|
|
35305
35323
|
3. baker images generate "<scene>" --reference <sheetUrl>
|
|
35306
35324
|
Ground the render on the identity sheet, never on a source photo,
|
|
35307
|
-
and copy the subject description into the prompt
|
|
35325
|
+
and copy the subject description into the prompt verbatim
|
|
35308
35326
|
|
|
35309
35327
|
Examples:
|
|
35310
35328
|
baker avatars list --status ready --output md
|
|
@@ -36164,7 +36182,7 @@ async function unresolvedAction(css, unresolved) {
|
|
|
36164
36182
|
const { plans } = planFontAdoption(await astroSources(), unresolved);
|
|
36165
36183
|
if (plans.length > 0) {
|
|
36166
36184
|
const names = plans.map((p) => `"${p.family}"`).join(", ");
|
|
36167
|
-
return `Run \`baker brand fonts adopt\` \u2014 ${names} is already self-hosted by ${plans[0]?.files.join(", ")}, so it only needs moving into ${GLOBAL_CSS} to load on every page.
|
|
36185
|
+
return `Run \`baker brand fonts adopt\` \u2014 ${names} is already self-hosted by ${plans[0]?.files.join(", ")}, so it only needs moving into ${GLOBAL_CSS} to load on every page. Don't fetch it: that downloads a different build of the same face.`;
|
|
36168
36186
|
}
|
|
36169
36187
|
const viaFontsource = [...fontsourceFamilies(css)];
|
|
36170
36188
|
const stranded = unresolved.filter((family) => viaFontsource.includes(family.toLowerCase()));
|
|
@@ -38630,7 +38648,7 @@ function emitScreenScene(i, scene, lengths, out, surfaceIngests, nodes, clips) {
|
|
|
38630
38648
|
type: "ingest",
|
|
38631
38649
|
params: {
|
|
38632
38650
|
source: "path",
|
|
38633
|
-
path: `[TODO: supply the
|
|
38651
|
+
path: `[TODO: supply the real screen for "${label}" \u2014 never AI-generate a UI. Capture a clean, text-free screenshot with \`baker images screenshot https://<brand-domain>/<path>\` (image-library skill); spoken/overlay text rides the overlay layer, not the screenshot. Wire it in as a path relative to this canvas file, never an absolute path]`,
|
|
38634
38652
|
expect: "image"
|
|
38635
38653
|
}
|
|
38636
38654
|
});
|
|
@@ -38682,7 +38700,7 @@ function emitGraphicScene(i, scene, lengths, out, surfaceIngests, nodes, clips)
|
|
|
38682
38700
|
type: "ingest",
|
|
38683
38701
|
params: {
|
|
38684
38702
|
source: "path",
|
|
38685
|
-
path: `[TODO: supply the
|
|
38703
|
+
path: `[TODO: supply the real designed graphic for "${label}" \u2014 never AI-generate a designed panel (its typography/wordmarks garble). Rebuild it as brand HTML in video-overlay-composition (the scene's text/logos are already seeded there), export the design from the brand kit, or source stock art; text rides the overlay layer, not this plate. Wire it in as a path relative to this canvas file, never an absolute path]`,
|
|
38686
38704
|
expect: "image"
|
|
38687
38705
|
}
|
|
38688
38706
|
});
|
|
@@ -39765,13 +39783,13 @@ function uiSurfaceStub(blueprint, run, clock) {
|
|
|
39765
39783
|
}) : [];
|
|
39766
39784
|
const isGraphic = scenes.some((s) => (compositeRegionsOf(s) ?? []).some((r) => r.kind === "static_graphic"));
|
|
39767
39785
|
const sourcing = isGraphic ? [
|
|
39768
|
-
" Build it as a
|
|
39786
|
+
" Build it as a real designed panel; never AI-generate its text/typography:",
|
|
39769
39787
|
" hand-build a brand-accurate HTML block in this composition (restyle the seeded",
|
|
39770
39788
|
" overlay text, or `npx hyperframes add <block>` for a ready-made card),",
|
|
39771
39789
|
" \u2014 OR drop the design asset (export it from the brand kit) as panel.png here;",
|
|
39772
39790
|
" then nest it as a timed clip:"
|
|
39773
39791
|
] : [
|
|
39774
|
-
" Build it as a
|
|
39792
|
+
" Build it as a real surface, never AI: capture the live page \u2014",
|
|
39775
39793
|
" baker images screenshot https://<brand-domain>/<path> (image-library skill)",
|
|
39776
39794
|
" \u2014 OR hand-build a brand-accurate HTML screen; then frame it in a phone mockup:",
|
|
39777
39795
|
" npx hyperframes add phone-scroll (writes compositions/phone-scroll.html)",
|
|
@@ -40173,7 +40191,7 @@ function buildModelConstraints() {
|
|
|
40173
40191
|
};
|
|
40174
40192
|
}
|
|
40175
40193
|
return {
|
|
40176
|
-
note: "Switching a clip's video model has cross-node constraints.
|
|
40194
|
+
note: "Switching a clip's video model has cross-node constraints. All video_generate nodes in one canvas must share one aspect_ratio (the composite silently crops otherwise). Each model's `duration` is a hard enum and differs per model \u2014 a scene longer than the model's max must be split. `person_generation` differs (Veo requires \"allow_all\"). `baker canvas validate` gates duration, aspect agreement, per-model params, and a scene-span-vs-model-max advisory before any billed run \u2014 run it after every model edit.",
|
|
40177
40195
|
models
|
|
40178
40196
|
};
|
|
40179
40197
|
}
|
|
@@ -40248,7 +40266,7 @@ function buildMotionBoard(blueprint) {
|
|
|
40248
40266
|
});
|
|
40249
40267
|
}
|
|
40250
40268
|
var VIDEO_GUIDE = [
|
|
40251
|
-
"Scaffolded by `baker canvas scaffold-video` \u2014 a runnable reproduction of your reference video, built like an editing timeline. It is a sequence of clear SHOTS separated at COMPLETE BREAKS (hard cuts): two adjacent presenter shots at a cut are TWO clips, never glued into one take. What stays continuous is the VOICE \u2014 a voiceover narration is ONE read across the b-roll it plays over, and a b-roll CUTAWAY between two on-camera moments leaves the presenter shot continuous with the insert sliced in \u2014 and each person keeps ONE brand voice (all their clips' native audio re-voiced in a single per-speaker pass), so timbre holds across the cuts. A presenter shot is ONE Seedance clip (native lip-sync + audio); a pure-voiceover stretch is one ElevenLabs tts read; a sub-2s flash is a still hold. A single shot too long for one clip splits into takes that share a boundary frame \u2014 the spine drops the duplicated frame (`--seam-dedup head|tail|off`). Every clip gets a CLEAN-PLATE start AND end keyframe (no baked text), RECAST to your dropped reference assets \u2014 Seedance interpolates real in-shot motion between them. Each frame grounds
|
|
40269
|
+
"Scaffolded by `baker canvas scaffold-video` \u2014 a runnable reproduction of your reference video, built like an editing timeline. It is a sequence of clear SHOTS separated at COMPLETE BREAKS (hard cuts): two adjacent presenter shots at a cut are TWO clips, never glued into one take. What stays continuous is the VOICE \u2014 a voiceover narration is ONE read across the b-roll it plays over, and a b-roll CUTAWAY between two on-camera moments leaves the presenter shot continuous with the insert sliced in \u2014 and each person keeps ONE brand voice (all their clips' native audio re-voiced in a single per-speaker pass), so timbre holds across the cuts. A presenter shot is ONE Seedance clip (native lip-sync + audio); a pure-voiceover stretch is one ElevenLabs tts read; a sub-2s flash is a still hold. A single shot too long for one clip splits into takes that share a boundary frame \u2014 the spine drops the duplicated frame (`--seam-dedup head|tail|off`). Every clip gets a CLEAN-PLATE start AND end keyframe (no baked text), RECAST to your dropped reference assets \u2014 Seedance interpolates real in-shot motion between them. Each frame grounds only on its own extracted frame + el_* slots (never another generated frame), so all frames render in PARALLEL (no cross-frame cascade). A SPLIT-SCREEN / PICTURE-IN-PICTURE / KEYED-PRESENTER scene is reproduced as one clip PER REGION, stacked or overlaid (see `metadata.todo.composition`). On-screen text/graphics are a separate HTML overlay layer you paint; audio is the voice + SFX + a ducked music bed, normalized stereo. It is a STARTING POINT, not a locked render: add, delete, reorder, split, merge, or re-time scenes freely \u2014 see `metadata.todo.full_flexibility`.",
|
|
40252
40270
|
"",
|
|
40253
40271
|
"WHAT TO DO NEXT:",
|
|
40254
40272
|
"0. RE-CRAFT THE SCRIPT FIRST (don't clone). This reference already won in-market, but copying a video is much harder than a static: the hook is targeting and may not transfer, and the message must become TRUE for our brand. Work the `metadata.todo.script_recraft` checklist \u2014 for each scene judge its role (hook/body/CTA), decide keep/cut/reorder/replace, and re-author every line for OUR customer's pain + OUR offer. See `references/script-craft.md` (hook/body/CTA framework) and the `meta-ads-playbook` skill. Most of the work lives here.",
|
|
@@ -40289,7 +40307,7 @@ function recraftTextForRole(role, sceneIndex) {
|
|
|
40289
40307
|
return `[RECRAFT HOOK \u2014 the #1 decision, but usually the LIGHTEST move wins: this hook already worked, so default to KEEPING it and building on top (swap only the specifics for ours). REBUILD only when it doesn't transfer \u2014 a claim we lack or a different funnel/awareness stage \u2014 by reaching for its deeper INNER MECHANIC and delivering that truthfully. DIAGNOSE (device + mechanic + stage) \u2192 DECIDE keep/adapt/rebuild \u2192 hold the opener to criteria: ${HOOK_OPENER_CRITERIA}. It lives on s${sceneIndex}_start (first frame) + the scene-${sceneIndex} overlay. See references/hook-craft.md + meta-ads-playbook \xA710/\xA739.]`;
|
|
40290
40308
|
}
|
|
40291
40309
|
if (role === "cta") {
|
|
40292
|
-
return "[RECRAFT CTA \u2014 the close: state what to do (where to go) + what to expect (offer/bundle/discount) for OUR brand; soft for top-of-funnel, hard for bottom.
|
|
40310
|
+
return "[RECRAFT CTA \u2014 the close: state what to do (where to go) + what to expect (offer/bundle/discount) for OUR brand; soft for top-of-funnel, hard for bottom. Don't render the reference's words. See references/script-craft.md.]";
|
|
40293
40311
|
}
|
|
40294
40312
|
return `[RECRAFT: rewrite this ${role} for OUR brand \u2014 true claims only; do NOT render the reference's words. See references/script-craft.md + meta-ads-playbook.]`;
|
|
40295
40313
|
}
|
|
@@ -40372,7 +40390,7 @@ function buildVideoTodo(report, overlayCount, floatingCount, opts, blueprint) {
|
|
|
40372
40390
|
// ALWAYS-ON safety net: the scaffold can only seed what the deconstruct
|
|
40373
40391
|
// cataloged, and it under-detects on-image graphics + sound cues. Never trust
|
|
40374
40392
|
// "none detected" — re-watch the reference and fill the gaps with the right tool.
|
|
40375
|
-
completeness_check: 'The scaffold mirrors the deconstruct\'s catalog, which UNDER-DETECTS \u2014 never trust a 0 count. Re-watch the reference frame-by-frame and add anything missing: (1) ON-IMAGE GRAPHICS not in floating_elements (dollar/coin icons, emojis, checkmarks, rating stars, price tags, arrows, progress bars, app UI) \u2192 source each with `baker images icon "<desc>"` / `baker images sticker` / `baker images gif` / `baker images logo <domain>` and add it as an <img class="ov pos-* " data-start data-dur> in video-overlay-composition/index.html (
|
|
40393
|
+
completeness_check: 'The scaffold mirrors the deconstruct\'s catalog, which UNDER-DETECTS \u2014 never trust a 0 count. Re-watch the reference frame-by-frame and add anything missing: (1) ON-IMAGE GRAPHICS not in floating_elements (dollar/coin icons, emojis, checkmarks, rating stars, price tags, arrows, progress bars, app UI) \u2192 source each with `baker images icon "<desc>"` / `baker images sticker` / `baker images gif` / `baker images logo <domain>` and add it as an <img class="ov pos-* " data-start data-dur> in video-overlay-composition/index.html (never bake graphics into the frame plates). (2) SOUND CUES not in sound_effects (cha-ching/coin, whoosh, ding, pop, notification, keyboard) \u2192 add a `sound_effect` node (eleven_text_to_sound) and wire it onto `audio_mix` at its timestamp. (3) RECURRING people/animals/products/logos/sets with no el_* slot \u2192 add an `ingest` [TODO] slot and reference it from the frames they appear in. (4) Burned-in captions/text not in text_overlays \u2192 add an <img>-free <div class="ov"> in index.html. (5) ONE person playing MULTIPLE personas/wardrobes (skeptic vs believer, before vs after, two outfits) collapsed into a single el_* slot \u2192 split into one el_* slot PER look, each linked as the SAME individual via `same_as` so every outfit has its own reference image but the face/identity stays identical.',
|
|
40376
40394
|
scenes_clamped_to_15s: report.clamped_scenes,
|
|
40377
40395
|
oversize_scenes: report.oversize_scenes.length > 0 ? {
|
|
40378
40396
|
scenes: report.oversize_scenes,
|
|
@@ -42063,9 +42081,9 @@ ${describeRewrites(healed.rewrites)}
|
|
|
42063
42081
|
}) : null;
|
|
42064
42082
|
const hints2 = reviewable ? review === null ? [RENDER_REVIEW_UNAVAILABLE] : review.length === 0 ? ["Reviewed the finished video frame by frame \u2014 nothing to report."] : [
|
|
42065
42083
|
`Reviewed the finished video and found ${review.length} thing${review.length === 1 ? "" : "s"} to fix. Show the user what is wrong before you show them the ad.`,
|
|
42066
|
-
...review.map((f) => `${f.severity === "blocking" ? "
|
|
42084
|
+
...review.map((f) => `${f.severity === "blocking" ? "Must fix" : "Should fix"} \u2014 ${f.what}`),
|
|
42067
42085
|
...review.some((f) => f.scene) ? [
|
|
42068
|
-
`Anything about the
|
|
42086
|
+
`Anything about the mark or the closing card is a composition fix, not a re-render: both are drawn as HTML by the caption composition, so editing it and re-running re-renders only that pass \u2014 the clips are cached and no generation is paid for again. Fix them, then re-run: edit the \`show\` of the scene files named above \u2014 a shot with a person handling equipment is where limbs and objects go wrong, so widen it, move the hands out of frame, or drop the person from that beat. Re-running re-renders only the scenes you edited; everything else comes from the cache (this run: ${result.stats?.cached_nodes ?? 0} of ${result.stats?.total_nodes ?? 0} nodes were cached).`
|
|
42069
42087
|
] : []
|
|
42070
42088
|
] : [];
|
|
42071
42089
|
if (poster) {
|
|
@@ -42969,12 +42987,12 @@ function fail3(code, message) {
|
|
|
42969
42987
|
process.exit(2);
|
|
42970
42988
|
}
|
|
42971
42989
|
function resolveModels(args5) {
|
|
42972
|
-
const
|
|
42990
|
+
const pick = (flag, kind, fallback) => args5[flag] ? String(args5[flag]) : resolveModel(kind, fallback);
|
|
42973
42991
|
return {
|
|
42974
|
-
describeModel:
|
|
42975
|
-
selectModel:
|
|
42976
|
-
layoutModel:
|
|
42977
|
-
genModel:
|
|
42992
|
+
describeModel: pick("describe-model", "image_describe", "~google/gemini-pro-latest"),
|
|
42993
|
+
selectModel: pick("select-model", "text_generate", "~google/gemini-flash-latest"),
|
|
42994
|
+
layoutModel: pick("layout-model", "text_generate", "~google/gemini-flash-latest"),
|
|
42995
|
+
genModel: pick("gen-model", "image_generate", "openai/gpt-image-2")
|
|
42978
42996
|
};
|
|
42979
42997
|
}
|
|
42980
42998
|
var PLATFORM_PLACEMENTS = {
|
|
@@ -44175,7 +44193,7 @@ registerSchema({
|
|
|
44175
44193
|
spec: {
|
|
44176
44194
|
type: "string",
|
|
44177
44195
|
required: true,
|
|
44178
|
-
description: 'Path to the ad spec JSON. Shape: { format?, market?, brand?, cast?, end_card?, voice?, music?, beats: [{ say, show, on_camera?, cast? }] }. Fill `brand` from src/brand/BRAND.md \u2014 `palette` (its hex tokens, most important first) and `logo` (the repo path to the mark) are what make the ad look like the client rather than like stock. `market` is where the ad is
|
|
44196
|
+
description: 'Path to the ad spec JSON. Shape: { format?, market?, brand?, cast?, end_card?, voice?, music?, beats: [{ say, show, on_camera?, cast? }] }. Fill `brand` from src/brand/BRAND.md \u2014 `palette` (its hex tokens, most important first) and `logo` (the repo path to the mark) are what make the ad look like the client rather than like stock. `market` is where the ad is set; omitted, it is inferred from the voice language, and getting it wrong is what fills a Spanish ad with British houses. `say` is one clause ending in its own punctuation \u2014 it becomes a caption card verbatim, so two sentences in one beat produce a card holding both. `show` is the shot brief for that line. Set `on_camera` only when that beat\'s subject talks to camera. A beat\'s `sound` is one sound effect for that shot, and most beats should not have one. Add it only where the picture shows a specific physical event the viewer expects to hear (a drill tightening a bolt, a door closing, rain on glass, a graphic snapping in). On a talking head or a calm lifestyle shot it reads as a stock sting, and one per beat turns a quiet ad into a noisy one. A music bed plus two well-placed effects beats eight. `voiceover: false` makes it a MUSIC-LED ad: nobody speaks, the `say` lines become on-screen text captioned straight from the script, and it needs `music`. `cast` is WHO the ad is about, and an ad with people in it needs one: `{ "avatar": "marta" }` names a cast avatar (`baker avatars list`) and every beat is grounded on that avatar\'s identity sheet with its subject description copied verbatim \u2014 the same face here as in the rest of the company\'s work. Without it each beat invents its own stranger, which is how one 28-second ad came back with five different men playing one customer. `{ "description": "..." }` is the fallback when there is no avatar, and it scaffolds a canvas that asks you to drop a photo before it can run \u2014 so prefer the avatar. A beat sets `cast: false` for a shot they are not in. `end_card` is on by default whenever `brand` is set: the last beat becomes a flat brand plate with the mark and a call to action drawn over it. `{ "cta": "..." }` sets the button copy \u2014 there is no default, so with no `cta` the card carries the mark alone, and a `cta` repeating the closing line is dropped rather than printed twice. `false` keeps the footage.'
|
|
44179
44197
|
},
|
|
44180
44198
|
avatar: {
|
|
44181
44199
|
type: "string",
|
|
@@ -44214,7 +44232,7 @@ function avatarVoiceBrief(avatar) {
|
|
|
44214
44232
|
var scaffoldAdCommand = defineCommand115({
|
|
44215
44233
|
meta: {
|
|
44216
44234
|
name: "scaffold-ad",
|
|
44217
|
-
description: 'Build a captioned vertical ad from a written spec, with no reference video to copy.\n\nA spec is a list of BEATS. One beat = one line the voice says + one shot on screen while it is said. That pairing is what keeps the ad honest: the caption card IS the line, so it can never end mid-sentence, and the picture can never drift from the read.\n\nTwo rules the spec enforces for you, both learned the expensive way:\n \u2022 `say` is
|
|
44235
|
+
description: 'Build a captioned vertical ad from a written spec, with no reference video to copy.\n\nA spec is a list of BEATS. One beat = one line the voice says + one shot on screen while it is said. That pairing is what keeps the ad honest: the caption card IS the line, so it can never end mid-sentence, and the picture can never drift from the read.\n\nTwo rules the spec enforces for you, both learned the expensive way:\n \u2022 `say` is one clause with its own punctuation. Two sentences in a beat is rejected.\n \u2022 Nobody in shot is speaking unless you set `on_camera` on that beat. A person visibly talking under a voice that is not theirs reads as bad dubbing.\n\nExample spec:\n { "beats": [\n { "say": "Mientras t\xFA sigues pagando la luz a precio de oro,", "show": "Aerial over Spanish rooftops at golden hour." },\n { "say": "Haz clic en el enlace y reserva tu estudio gratuito.", "show": "A couple walking home at sunset." } ] }\n\nWho is in it:\n `cast: { avatar: "marta" }` grounds every beat on that avatar\'s face \u2014 cast one with `baker avatars`.\n Without a cast, each beat invents its own person and the ad reads as a stock-footage collage.\n\nHow it ends: with a `brand`, the last beat closes on a flat brand plate carrying the mark and the CTA.\n\nThen: baker canvas run <the canvas it writes>'
|
|
44218
44236
|
},
|
|
44219
44237
|
args: {
|
|
44220
44238
|
spec: { type: "string", required: true, description: "Path to the ad spec JSON" },
|
|
@@ -44400,7 +44418,7 @@ var scaffoldAdCommand = defineCommand115({
|
|
|
44400
44418
|
message: resolvedLogo ? `The brand mark at \`${resolvedLogo}\` does not exist, so this ad has no logo to put on screen.` : `This company has no brand mark in \`${BRAND_DIR}/logos/\`, so an ad cannot carry its logo.`,
|
|
44401
44419
|
// What the lookup against their own domain actually found, so the agent does not
|
|
44402
44420
|
// go looking for the mark somewhere else — which is the loop this replaced.
|
|
44403
|
-
fix: `${fetched?.reason ?? ""}
|
|
44421
|
+
fix: `${fetched?.reason ?? ""} Don't draw one or take one from an icon library: the mark is drawn on every frame and on the closing card, so a stand-in ships an ad wearing the wrong brand.`
|
|
44404
44422
|
}
|
|
44405
44423
|
});
|
|
44406
44424
|
process.exit(1);
|
|
@@ -44581,7 +44599,7 @@ var scaffoldAdCommand = defineCommand115({
|
|
|
44581
44599
|
// getting it wrong is expensive: the pictures cost a fraction of the clips, and
|
|
44582
44600
|
// every defect anyone has caught in a finished ad was visible in them. The run
|
|
44583
44601
|
// stops, the frames are shown, and continuing pays for none of them twice.
|
|
44584
|
-
handle || staged ? `Wrote ${spec.data.beats.length} beats to ${outPath}. Run it with \`baker canvas run ${outPath} --until frames --brief "<what they asked for, their words>"\`,
|
|
44602
|
+
handle || staged ? `Wrote ${spec.data.beats.length} beats to ${outPath}. Run it with \`baker canvas run ${outPath} --until frames --brief "<what they asked for, their words>"\`, show the frames it returns and ask whether to carry on \u2014 then continue with the \`--run-id\` it gives you. This ad carries ${handle ? "a person's face" : "the client's mark"}, and a wrong look costs a whole ad to discover after the clips.` : `Wrote ${spec.data.beats.length} beats to ${outPath}. Run it with \`baker canvas run ${outPath} --brief "<what they asked for, their words>"\`.`,
|
|
44585
44603
|
...autoCast ? [
|
|
44586
44604
|
`Cast \`${autoCast}\` \u2014 the spec described this person in words, and they are one of this company's avatars. Every shot now grounds on their identity sheet and carries their accent and delivery. Write \`"cast": { "avatar": "` + autoCast + '" }` yourself next time; a written description is for someone the company does not have.'
|
|
44587
44605
|
] : [],
|
|
@@ -44623,7 +44641,7 @@ var scaffoldAdCommand = defineCommand115({
|
|
|
44623
44641
|
// ad with no palette and no mark anywhere in 28 seconds.
|
|
44624
44642
|
`This spec has no ${!spec.data.brand?.palette?.length ? "`brand.palette`" : ""}${!spec.data.brand?.palette?.length && !spec.data.brand?.logo ? " and no " : ""}${!spec.data.brand?.logo ? "`brand.logo`" : ""}. Read src/brand/BRAND.md and re-run with them \u2014 a name alone leaves the ad in someone else's clothes.`
|
|
44625
44643
|
],
|
|
44626
|
-
"`brand.palette` colours the closing plate the card sits on; `brand.logo` is the repo path to the mark, and it is
|
|
44644
|
+
"`brand.palette` colours the closing plate the card sits on; `brand.logo` is the repo path to the mark, and it is drawn by the composition \u2014 top-left throughout and large on the closing card \u2014 never generated as a picture, so it cannot come back garbled. Give it whenever the client has one."
|
|
44627
44645
|
]
|
|
44628
44646
|
});
|
|
44629
44647
|
}
|
|
@@ -44631,7 +44649,7 @@ var scaffoldAdCommand = defineCommand115({
|
|
|
44631
44649
|
function shortBeatHint(short, total) {
|
|
44632
44650
|
if (short.length === 0) return [];
|
|
44633
44651
|
return [
|
|
44634
|
-
`Beat${short.length === 1 ? "" : "s"} ${short.join(", ")} of ${total} ${short.length === 1 ? "is" : "are"} a fragment rather than a line \u2014 a few words with a whole shot to themselves. Every beat's picture holds for at least ${SPOKEN_CLIP_DURATION_S}s, so a two-word beat buys six seconds of somebody standing still, and a run of those plays as a slideshow rather than an ad.
|
|
44652
|
+
`Beat${short.length === 1 ? "" : "s"} ${short.join(", ")} of ${total} ${short.length === 1 ? "is" : "are"} a fragment rather than a line \u2014 a few words with a whole shot to themselves. Every beat's picture holds for at least ${SPOKEN_CLIP_DURATION_S}s, so a two-word beat buys six seconds of somebody standing still, and a run of those plays as a slideshow rather than an ad. Don't pad them. Merge them into the beat before or after, so each one carries a whole thought. Edit the spec and re-run this command.`
|
|
44635
44653
|
];
|
|
44636
44654
|
}
|
|
44637
44655
|
async function fetchClientLogo() {
|
|
@@ -45008,18 +45026,18 @@ function parseBudget(raw) {
|
|
|
45008
45026
|
throw new Error(`--budget must be one of ${VIDEO_BUDGETS.join(", ")} (got "${v}")`);
|
|
45009
45027
|
}
|
|
45010
45028
|
function resolveModels2(args5) {
|
|
45011
|
-
const
|
|
45029
|
+
const pick = (flag, kind, fallback) => args5[flag] ? String(args5[flag]) : resolveModel2(kind, fallback);
|
|
45012
45030
|
return {
|
|
45013
45031
|
// Flash by default: ~2-3× faster per call than Pro, which keeps each
|
|
45014
45032
|
// per-scene deconstruct step well inside the action time budget and turns a
|
|
45015
45033
|
// ~9 min run into ~3-4 min. Override with --deconstruct-model
|
|
45016
45034
|
// ~google/gemini-pro-latest for the densest extraction when speed matters less.
|
|
45017
|
-
deconstructModel:
|
|
45018
|
-
selectModel:
|
|
45035
|
+
deconstructModel: pick("deconstruct-model", "video_deconstruct", "~google/gemini-flash-latest"),
|
|
45036
|
+
selectModel: pick("select-model", "text_generate", "~google/gemini-flash-latest"),
|
|
45019
45037
|
// Default to the strongest image model (matches the static-ad scaffold); the
|
|
45020
45038
|
// frame generators need the most faithful text/identity reproduction. Override
|
|
45021
45039
|
// with --image-model for a cheaper/faster pass.
|
|
45022
|
-
imageModel:
|
|
45040
|
+
imageModel: pick("image-model", "image_generate", "openai/gpt-image-2.5-sunburst")
|
|
45023
45041
|
};
|
|
45024
45042
|
}
|
|
45025
45043
|
function hasPhotorealCast(elements) {
|
|
@@ -45651,7 +45669,7 @@ function capabilityHints(surfaces, tools = []) {
|
|
|
45651
45669
|
const immediate = surfaces.filter((surface) => surface.writeMode === "applies-at-publish");
|
|
45652
45670
|
if (immediate.length > 0) {
|
|
45653
45671
|
hints2.push(
|
|
45654
|
-
`REAL AND IMMEDIATE: ${immediate.map((s) => s.label).join(", ")}
|
|
45672
|
+
`REAL AND IMMEDIATE: ${immediate.map((s) => s.label).join(", ")}. Completing this chat changes the user's live setup, so agree the changes before you finish, not after.`
|
|
45655
45673
|
);
|
|
45656
45674
|
}
|
|
45657
45675
|
const blocked = surfaces.filter((surface) => surface.blockers.length > 0);
|
|
@@ -50773,12 +50791,12 @@ function handleError5(err) {
|
|
|
50773
50791
|
...readOnly ? {
|
|
50774
50792
|
fix: {
|
|
50775
50793
|
action: "reconnect",
|
|
50776
|
-
explanation: "The Google Analytics connection is read-only. Ask the user to reconnect Google Analytics \u2014 in the dashboard, Brain \u2192 Integrations \u2192 Tools \u2192 Google Analytics \u2014 which grants the permission to change the property. Reporting (`baker ga4 query`, `baker ga4 audit`) keeps working meanwhile, so carry on with the rest of the job and tell them what you will set up once they have reconnected.
|
|
50794
|
+
explanation: "The Google Analytics connection is read-only. Ask the user to reconnect Google Analytics \u2014 in the dashboard, Brain \u2192 Integrations \u2192 Tools \u2192 Google Analytics \u2014 which grants the permission to change the property. Reporting (`baker ga4 query`, `baker ga4 audit`) keeps working meanwhile, so carry on with the rest of the job and tell them what you will set up once they have reconnected. Don't claim anything was configured."
|
|
50777
50795
|
}
|
|
50778
50796
|
} : err.code === "UNAUTHORIZED" || err.code === "FORBIDDEN" && isNotConnectedError(err.code, err.message) ? {
|
|
50779
50797
|
fix: {
|
|
50780
50798
|
action: "request_connection",
|
|
50781
|
-
explanation: 'Google Analytics is not connected for this company yet, or no property has been picked.
|
|
50799
|
+
explanation: 'Google Analytics is not connected for this company yet, or no property has been picked. Don\'t send the user to Settings \u2014 call the `request_connection` tool with { platform: "google-analytics", reason } and they connect and pick their property from inside the chat. If the `request_connection` call fails, say so plainly and give the exact route: dashboard \u2192 Brain \u2192 Integrations \u2192 Tools \u2192 Google Analytics. Then carry on with the rest of the task.'
|
|
50782
50800
|
}
|
|
50783
50801
|
} : {},
|
|
50784
50802
|
retryable: RETRYABLE_CODES.has(err.code)
|
|
@@ -51651,7 +51669,7 @@ Examples:
|
|
|
51651
51669
|
});
|
|
51652
51670
|
registerSchema({
|
|
51653
51671
|
command: "ga4.audience",
|
|
51654
|
-
description: "Create an audience (a saved group of users, e.g. everyone who started a demo but never finished), rename one, or archive one. Read `baker ga4 config` first to see the audiences that already exist.
|
|
51672
|
+
description: "Create an audience (a saved group of users, e.g. everyone who started a demo but never finished), rename one, or archive one. Read `baker ga4 config` first to see the audiences that already exist. Everything that defines who is in an audience \u2014 the conditions, the membership window \u2014 is frozen the moment it is created and cannot be edited afterwards, so get it right in one go. An audience only reaches Google Ads for retargeting if the property is linked to a Google Ads account.",
|
|
51655
51673
|
args: {
|
|
51656
51674
|
json: { type: "string", description: createJsonDescription("audience"), required: false },
|
|
51657
51675
|
file: { type: "string", description: "Path to a JSON file with the definition", required: false },
|
|
@@ -58452,7 +58470,7 @@ var addCommand = defineCommand172({
|
|
|
58452
58470
|
"This page was already in the library, so nothing was re-studied \u2014 its sections are searchable now.",
|
|
58453
58471
|
`Search it with: baker landing inspiration search --domain ${new URL(data.canonicalUrl).hostname}`
|
|
58454
58472
|
] : [
|
|
58455
|
-
"Studying this page takes a few minutes.
|
|
58473
|
+
"Studying this page takes a few minutes. Don't wait on it \u2014 carry on, and search for it later in the turn or in a later one.",
|
|
58456
58474
|
"Meanwhile, search what is already in the library: baker landing inspiration search '<what you want to see>' --scope all",
|
|
58457
58475
|
"This is saved and being studied now, so it is listed as already applied \u2014 tell the user it is kept whatever they do with this chat.",
|
|
58458
58476
|
// The save is not a promise that the study succeeded. An agent that
|
|
@@ -61798,7 +61816,7 @@ var variantCommand = defineCommand181({
|
|
|
61798
61816
|
change: String(args5.change)
|
|
61799
61817
|
});
|
|
61800
61818
|
const hints2 = [
|
|
61801
|
-
forks.length === 0 ? `MISSING --fork: ${variantSlug2} currently renders exactly ${controlSlug}, so a test between them cannot conclude anything. Re-run with --fork <Component>, or copy the one section your change touches into src/pages/${variantSlug2}/_components/ and repoint that import.` : `Edit
|
|
61819
|
+
forks.length === 0 ? `MISSING --fork: ${variantSlug2} currently renders exactly ${controlSlug}, so a test between them cannot conclude anything. Re-run with --fork <Component>, or copy the one section your change touches into src/pages/${variantSlug2}/_components/ and repoint that import.` : `Edit only src/pages/${variantSlug2}/_components/${forks.join(", ")} \u2014 every other section is shared with ${controlSlug}, and editing a shared one changes both arms at once.`,
|
|
61802
61820
|
`Preview both at http://localhost:4321/${controlSlug}/ and http://localhost:4321/${variantSlug2}/ and check they differ only where you meant. Once it is live, only the first of those two answers \u2014 a variant has no public address of its own, and \`/${controlSlug}/?_bkr_preview=b\` is how anyone looks at it.`,
|
|
61803
61821
|
`Then: baker experiment start --landing ${controlSlug} --variant ${variantSlug2} --because "${String(args5.because)}" --change "${String(args5.change)}". Nothing is split until the session is published \u2014 the person publishing is the review of this page.`
|
|
61804
61822
|
];
|
|
@@ -62115,7 +62133,7 @@ import { defineCommand as defineCommand196 } from "citty";
|
|
|
62115
62133
|
import { defineCommand as defineCommand184 } from "citty";
|
|
62116
62134
|
|
|
62117
62135
|
// src/commands/research/hints.ts
|
|
62118
|
-
var AD_COPY_ROUTE = 'This returns competing
|
|
62136
|
+
var AD_COPY_ROUTE = 'This returns competing domains and their SERP economics \u2014 no ad copy, no headlines, no creative. For the actual copy of a competitor\'s ads: `baker winning-ads advertisers "<brand>"` \u2192 `baker winning-ads search "<brief>" --advertiser-id <id>` \u2192 `baker winning-ads content <adId>` (`primary_text` / `headline` / `cta`, plus the spoken transcript and on-screen text for video). That corpus is Meta and LinkedIn only \u2014 Google SERP ad copy is not available through any Baker command, so do not keep querying for it here.';
|
|
62119
62137
|
function noSerpRowsHints(rowCount2, queryContext) {
|
|
62120
62138
|
if (rowCount2 > 0) {
|
|
62121
62139
|
return [];
|
|
@@ -62286,7 +62304,7 @@ function handleResearchError(err, fixFor) {
|
|
|
62286
62304
|
// src/commands/research/advertisers.ts
|
|
62287
62305
|
registerSchema({
|
|
62288
62306
|
command: "research.advertisers",
|
|
62289
|
-
description: 'Returns a ranked list of
|
|
62307
|
+
description: 'Returns a ranked list of domains competing for one keyword in Google, with avg SERP position, relevance rating, estimated traffic value and visibility \u2014 domain economics, not ad copy. Despite the name it returns no headlines, descriptions or creative of any kind; for a competitor\'s actual ad copy use `baker winning-ads content <adId>` (Meta/LinkedIn only \u2014 Google SERP ad copy is not available anywhere in Baker). Start here: `baker research advertisers "<keyword>" --location <cc> --language <code>` to size up who owns a term. If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.',
|
|
62290
62308
|
args: {
|
|
62291
62309
|
keyword: {
|
|
62292
62310
|
type: "string",
|
|
@@ -62295,12 +62313,12 @@ registerSchema({
|
|
|
62295
62313
|
},
|
|
62296
62314
|
location: {
|
|
62297
62315
|
type: "string",
|
|
62298
|
-
description: "Country code (us, uk, es, de...) or DataForSEO location code.
|
|
62316
|
+
description: "Country code (us, uk, es, de...) or DataForSEO location code. Defaults to us (United States) if omitted \u2014 always set this for non-US markets",
|
|
62299
62317
|
required: false
|
|
62300
62318
|
},
|
|
62301
62319
|
language: {
|
|
62302
62320
|
type: "string",
|
|
62303
|
-
description: "Language code or name (en, es, spanish...).
|
|
62321
|
+
description: "Language code or name (en, es, spanish...). Defaults to en (English) if omitted \u2014 always set this for non-English markets",
|
|
62304
62322
|
required: false
|
|
62305
62323
|
},
|
|
62306
62324
|
limit: { type: "string", description: "Max results (default: 20, max: 100)", required: false },
|
|
@@ -62373,17 +62391,17 @@ Examples:
|
|
|
62373
62391
|
import { defineCommand as defineCommand185 } from "citty";
|
|
62374
62392
|
registerSchema({
|
|
62375
62393
|
command: "research.autocomplete",
|
|
62376
|
-
description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for.
|
|
62394
|
+
description: "Get Google Autocomplete suggestions for a seed keyword. Useful for keyword expansion and discovering what people actually search for. If --location and --language are omitted, defaults to United States (us) and English (en).",
|
|
62377
62395
|
args: {
|
|
62378
62396
|
keyword: { type: "string", description: "Seed keyword (positional)", required: true },
|
|
62379
62397
|
location: {
|
|
62380
62398
|
type: "string",
|
|
62381
|
-
description: "Country code (us, uk, es, de...) or DataForSEO location code.
|
|
62399
|
+
description: "Country code (us, uk, es, de...) or DataForSEO location code. Defaults to us (United States) if omitted",
|
|
62382
62400
|
required: false
|
|
62383
62401
|
},
|
|
62384
62402
|
language: {
|
|
62385
62403
|
type: "string",
|
|
62386
|
-
description: "Language code or name.
|
|
62404
|
+
description: "Language code or name. Defaults to en (English) if omitted",
|
|
62387
62405
|
required: false
|
|
62388
62406
|
},
|
|
62389
62407
|
limit: { type: "string", description: "Max suggestions (default: 10, max: 20)", required: false },
|
|
@@ -62669,7 +62687,7 @@ Examples:
|
|
|
62669
62687
|
import { defineCommand as defineCommand188 } from "citty";
|
|
62670
62688
|
registerSchema({
|
|
62671
62689
|
command: "research.intent",
|
|
62672
|
-
description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate.
|
|
62690
|
+
description: "Classify Google Search intent for keywords. Determines if someone searching is looking to buy, research, or navigate. If --language is omitted, defaults to English (en). The response includes a query_context object showing which language was used.",
|
|
62673
62691
|
args: {
|
|
62674
62692
|
keywords: {
|
|
62675
62693
|
type: "string",
|
|
@@ -62678,7 +62696,7 @@ registerSchema({
|
|
|
62678
62696
|
},
|
|
62679
62697
|
language: {
|
|
62680
62698
|
type: "string",
|
|
62681
|
-
description: "Language code or name.
|
|
62699
|
+
description: "Language code or name. Defaults to en (English) if omitted \u2014 always set this for non-English keywords",
|
|
62682
62700
|
required: false
|
|
62683
62701
|
},
|
|
62684
62702
|
"no-cache": { type: "boolean", description: "Skip server cache, hit API directly", required: false }
|
|
@@ -62740,18 +62758,18 @@ Examples:
|
|
|
62740
62758
|
import { defineCommand as defineCommand189 } from "citty";
|
|
62741
62759
|
registerSchema({
|
|
62742
62760
|
command: "research.keyword-gap",
|
|
62743
|
-
description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities.
|
|
62761
|
+
description: "Find keywords a competitor ranks for (organic or paid) that you don't. Discovers expansion opportunities. If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
|
|
62744
62762
|
args: {
|
|
62745
62763
|
competitor: { type: "string", description: "Competitor domain (positional 1)", required: true },
|
|
62746
62764
|
ours: { type: "string", description: "Your domain (positional 2)", required: true },
|
|
62747
62765
|
location: {
|
|
62748
62766
|
type: "string",
|
|
62749
|
-
description: "Country code (us, uk, es, de...) or DataForSEO location code.
|
|
62767
|
+
description: "Country code (us, uk, es, de...) or DataForSEO location code. Defaults to us (United States); set it for any non-US market.",
|
|
62750
62768
|
required: false
|
|
62751
62769
|
},
|
|
62752
62770
|
language: {
|
|
62753
62771
|
type: "string",
|
|
62754
|
-
description: "Language code or name.
|
|
62772
|
+
description: "Language code or name. Defaults to en (English) if omitted \u2014 always set this for non-English markets",
|
|
62755
62773
|
required: false
|
|
62756
62774
|
},
|
|
62757
62775
|
type: { type: "string", description: "Filter: paid, organic, all (default: all)", required: false },
|
|
@@ -62891,7 +62909,7 @@ function keywordMetricsFields(options) {
|
|
|
62891
62909
|
// src/commands/research/keyword-metrics.ts
|
|
62892
62910
|
registerSchema({
|
|
62893
62911
|
command: "research.keyword-metrics",
|
|
62894
|
-
description: "Search volume, CPC, top-of-page bids and competition for a list of keywords
|
|
62912
|
+
description: "Search volume, CPC, top-of-page bids and competition for a list of keywords you name \u2014 no domain needed. This is the command for sizing a market you have keywords for; the domain-bound ones (keywords-for-site, keyword-gap) answer a different question and should not be used as a workaround for this one. The volume is Google's Keyword Planner figure resold by DataForSEO, so it is not a second opinion on `baker ads google keywords metrics` \u2014 for that, pass --clickstream. If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
|
|
62895
62913
|
args: {
|
|
62896
62914
|
keywords: {
|
|
62897
62915
|
type: "string",
|
|
@@ -62900,12 +62918,12 @@ registerSchema({
|
|
|
62900
62918
|
},
|
|
62901
62919
|
location: {
|
|
62902
62920
|
type: "string",
|
|
62903
|
-
description: "Country code (us, mx, br, es...) or DataForSEO location code.
|
|
62921
|
+
description: "Country code (us, mx, br, es...) or DataForSEO location code. Defaults to us (United States) if omitted \u2014 always set this for non-US markets",
|
|
62904
62922
|
required: false
|
|
62905
62923
|
},
|
|
62906
62924
|
language: {
|
|
62907
62925
|
type: "string",
|
|
62908
|
-
description: "Language code or name.
|
|
62926
|
+
description: "Language code or name. Defaults to en (English) if omitted \u2014 always set this for non-English markets",
|
|
62909
62927
|
required: false
|
|
62910
62928
|
},
|
|
62911
62929
|
clickstream: {
|
|
@@ -63006,17 +63024,17 @@ Examples:
|
|
|
63006
63024
|
import { defineCommand as defineCommand191 } from "citty";
|
|
63007
63025
|
registerSchema({
|
|
63008
63026
|
command: "research.keywords-for-site",
|
|
63009
|
-
description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only.
|
|
63027
|
+
description: "Get keywords a competitor targets in Google. Use --type paid to see only paid keywords, --type organic for organic only. If --location and --language are omitted, defaults to United States (us) and English (en). The response includes a query_context object showing which location/language were used.",
|
|
63010
63028
|
args: {
|
|
63011
63029
|
target: { type: "string", description: "Domain or URL to analyze (positional)", required: true },
|
|
63012
63030
|
location: {
|
|
63013
63031
|
type: "string",
|
|
63014
|
-
description: "Country code (us, uk, es, de...) or DataForSEO location code.
|
|
63032
|
+
description: "Country code (us, uk, es, de...) or DataForSEO location code. Defaults to us (United States) if omitted \u2014 always set this for non-US markets",
|
|
63015
63033
|
required: false
|
|
63016
63034
|
},
|
|
63017
63035
|
language: {
|
|
63018
63036
|
type: "string",
|
|
63019
|
-
description: "Language code or name.
|
|
63037
|
+
description: "Language code or name. Defaults to en (English) if omitted \u2014 always set this for non-English markets",
|
|
63020
63038
|
required: false
|
|
63021
63039
|
},
|
|
63022
63040
|
sort: {
|
|
@@ -63199,17 +63217,17 @@ Examples:
|
|
|
63199
63217
|
import { defineCommand as defineCommand194 } from "citty";
|
|
63200
63218
|
registerSchema({
|
|
63201
63219
|
command: "research.relevant-pages",
|
|
63202
|
-
description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic.
|
|
63220
|
+
description: "Get the top pages of a competitor domain with organic traffic and ranking data. Shows which pages drive the most traffic. If --location and --language are omitted, defaults to United States (us) and English (en).",
|
|
63203
63221
|
args: {
|
|
63204
63222
|
target: { type: "string", description: "Domain to analyze (positional, e.g. competitor.com)", required: true },
|
|
63205
63223
|
location: {
|
|
63206
63224
|
type: "string",
|
|
63207
|
-
description: "Country code (us, uk, es, de...) or DataForSEO location code.
|
|
63225
|
+
description: "Country code (us, uk, es, de...) or DataForSEO location code. Defaults to us (United States) if omitted",
|
|
63208
63226
|
required: false
|
|
63209
63227
|
},
|
|
63210
63228
|
language: {
|
|
63211
63229
|
type: "string",
|
|
63212
|
-
description: "Language code or name.
|
|
63230
|
+
description: "Language code or name. Defaults to en (English) if omitted",
|
|
63213
63231
|
required: false
|
|
63214
63232
|
},
|
|
63215
63233
|
limit: { type: "string", description: "Max results (default: 20, max: 1000)", required: false },
|
|
@@ -65847,7 +65865,7 @@ function handleError8(err) {
|
|
|
65847
65865
|
...err.code === "UNAUTHORIZED" || err.code === "FORBIDDEN" && isNotConnectedError(err.code, err.message) ? {
|
|
65848
65866
|
fix: {
|
|
65849
65867
|
action: "request_connection",
|
|
65850
|
-
explanation: 'Tag Manager is not connected for this company yet, or no container has been picked.
|
|
65868
|
+
explanation: 'Tag Manager is not connected for this company yet, or no container has been picked. Don\'t send the user to Settings \u2014 call the `request_connection` tool with { platform: "google-tag-manager", reason } and they connect and pick their containers (they can pick more than one) from inside the chat. `request_tag_input` is not the route: that form manages the container snippet on the site, not what runs inside the container, and it cannot pick a container. If the `request_connection` call fails, say so plainly and give the exact route: dashboard \u2192 Brain \u2192 Integrations \u2192 Tools \u2192 Google Tag Manager, where they also pick the container. Then carry on with the rest of the task.'
|
|
65851
65869
|
}
|
|
65852
65870
|
} : {},
|
|
65853
65871
|
retryable: RETRYABLE_CODES2.has(err.code)
|
|
@@ -66361,7 +66379,7 @@ ${configLines.join("\n")}` : header;
|
|
|
66361
66379
|
function tagsListHints() {
|
|
66362
66380
|
return [
|
|
66363
66381
|
"To create, edit or delete a tag, call the `request_tag_input` approval form (tool `mcp__baker_ui__request_tag_input`, server `baker_ui`) \u2014 it is attached to every turn of this chat and collects any secret values itself.",
|
|
66364
|
-
"
|
|
66382
|
+
"Don't conclude the form is unavailable from your tool list: call it, and report what it returns. And never file a tag change as a Task \u2014 a Task hands the user work this chat can stage in one call."
|
|
66365
66383
|
];
|
|
66366
66384
|
}
|
|
66367
66385
|
|
|
@@ -66433,7 +66451,7 @@ var tagsCommand4 = defineCommand222({
|
|
|
66433
66451
|
name: "tags",
|
|
66434
66452
|
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.
|
|
66435
66453
|
|
|
66436
|
-
This command is
|
|
66454
|
+
This command is read-only. Every tag change \u2014 create, edit, or delete \u2014 goes through the request_tag_input tool (baker_ui MCP server): propose one or more changes (each becomes a tab in one approval form), pre-fill the non-secret fields you know, and the user reviews, fills secrets, and approves or skips each. Secret values (accessToken, apiSecret, authorizationToken, apiKey, conversionToken, OAuth connections) never pass through the CLI or the chat.
|
|
66437
66455
|
|
|
66438
66456
|
Refs: \`baker tags list\` prints each tag's ref \u2014 a real tag id, or tag_temp_* for creates staged in this chat. Use these refs as flow side-effect tagIds; they keep resolving after publish.
|
|
66439
66457
|
|
|
@@ -69224,141 +69242,6 @@ function outputFlagForCommand(root, argv) {
|
|
|
69224
69242
|
});
|
|
69225
69243
|
}
|
|
69226
69244
|
|
|
69227
|
-
// src/field-mask.ts
|
|
69228
|
-
var FLAG2 = "--fields";
|
|
69229
|
-
function parseFields(value) {
|
|
69230
|
-
return value.split(",").map((field) => field.trim()).filter((field) => field.length > 0);
|
|
69231
|
-
}
|
|
69232
|
-
function takeFieldsFlag(root, argv) {
|
|
69233
|
-
const end = argv.indexOf("--");
|
|
69234
|
-
const limit = end === -1 ? argv.length : end;
|
|
69235
|
-
const taken = /* @__PURE__ */ new Set();
|
|
69236
|
-
const fields = [];
|
|
69237
|
-
for (let index = 0; index < limit; index++) {
|
|
69238
|
-
const token = argv[index] ?? "";
|
|
69239
|
-
if (token.startsWith(`${FLAG2}=`)) {
|
|
69240
|
-
taken.add(index);
|
|
69241
|
-
fields.push(...parseFields(token.slice(FLAG2.length + 1)));
|
|
69242
|
-
} else if (token === FLAG2) {
|
|
69243
|
-
const value = argv[index + 1];
|
|
69244
|
-
if (value === void 0 || index + 1 >= limit || value.startsWith("-")) {
|
|
69245
|
-
return { argv, fields: null };
|
|
69246
|
-
}
|
|
69247
|
-
taken.add(index).add(index + 1);
|
|
69248
|
-
fields.push(...parseFields(value));
|
|
69249
|
-
index++;
|
|
69250
|
-
}
|
|
69251
|
-
}
|
|
69252
|
-
if (taken.size === 0 || fields.length === 0) {
|
|
69253
|
-
return { argv, fields: null };
|
|
69254
|
-
}
|
|
69255
|
-
const command = commandFor(root, argv);
|
|
69256
|
-
if (!command || declaredArg(command, "fields") || PRINTS_NO_ENVELOPE.has(commandPathOf(root, argv))) {
|
|
69257
|
-
return { argv, fields: null };
|
|
69258
|
-
}
|
|
69259
|
-
return { argv: argv.filter((_, index) => !taken.has(index)), fields };
|
|
69260
|
-
}
|
|
69261
|
-
function isRecord3(value) {
|
|
69262
|
-
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
69263
|
-
}
|
|
69264
|
-
function pick(record, fields) {
|
|
69265
|
-
return Object.fromEntries(fields.filter((field) => field in record).map((field) => [field, record[field]]));
|
|
69266
|
-
}
|
|
69267
|
-
function wrappedList(data, fields) {
|
|
69268
|
-
if (fields.some((field) => field in data)) {
|
|
69269
|
-
return null;
|
|
69270
|
-
}
|
|
69271
|
-
const lists = Object.entries(data).filter(
|
|
69272
|
-
([, value]) => Array.isArray(value) && value.length > 0 && value.every((item) => isRecord3(item))
|
|
69273
|
-
);
|
|
69274
|
-
return lists.length === 1 ? lists[0]?.[0] ?? null : null;
|
|
69275
|
-
}
|
|
69276
|
-
function recordsOf(data, fields) {
|
|
69277
|
-
if (Array.isArray(data)) {
|
|
69278
|
-
return data;
|
|
69279
|
-
}
|
|
69280
|
-
if (!isRecord3(data)) {
|
|
69281
|
-
return [];
|
|
69282
|
-
}
|
|
69283
|
-
const list = wrappedList(data, fields);
|
|
69284
|
-
return list === null ? [data] : data[list];
|
|
69285
|
-
}
|
|
69286
|
-
function maskData(data, fields) {
|
|
69287
|
-
const mask = (item) => isRecord3(item) ? pick(item, fields) : item;
|
|
69288
|
-
if (Array.isArray(data)) {
|
|
69289
|
-
return data.map(mask);
|
|
69290
|
-
}
|
|
69291
|
-
if (!isRecord3(data)) {
|
|
69292
|
-
return data;
|
|
69293
|
-
}
|
|
69294
|
-
const list = wrappedList(data, fields);
|
|
69295
|
-
return list === null ? pick(data, fields) : { ...data, [list]: recordsOf(data, fields).map(mask) };
|
|
69296
|
-
}
|
|
69297
|
-
function unmatchedHint(data, fields) {
|
|
69298
|
-
const records = recordsOf(data, fields).filter(isRecord3);
|
|
69299
|
-
if (records.length === 0) {
|
|
69300
|
-
return null;
|
|
69301
|
-
}
|
|
69302
|
-
const carried = new Set(records.flatMap((record) => Object.keys(record)));
|
|
69303
|
-
const unmatched = fields.filter((field) => !carried.has(field));
|
|
69304
|
-
if (unmatched.length === 0) {
|
|
69305
|
-
return null;
|
|
69306
|
-
}
|
|
69307
|
-
return `--fields matched nothing for: ${unmatched.join(", ")}. The keys data carries are: ${[...carried].join(", ")}.`;
|
|
69308
|
-
}
|
|
69309
|
-
function reprint(text2, envelope) {
|
|
69310
|
-
const indent = text2.trimStart().startsWith("{\n") ? 2 : void 0;
|
|
69311
|
-
return `${JSON.stringify(envelope, null, indent)}${text2.endsWith("\n") ? "\n" : ""}`;
|
|
69312
|
-
}
|
|
69313
|
-
function withHint(envelope, hint) {
|
|
69314
|
-
return { ...envelope, hints: [...Array.isArray(envelope.hints) ? envelope.hints : [], hint] };
|
|
69315
|
-
}
|
|
69316
|
-
var NO_DATA_HINT = "--fields was not applied: this result carries no data to narrow, so it is printed whole. Drop --fields on this command.";
|
|
69317
|
-
function maskPrintedEnvelope(text2, fields) {
|
|
69318
|
-
let parsed;
|
|
69319
|
-
try {
|
|
69320
|
-
parsed = JSON.parse(text2);
|
|
69321
|
-
} catch {
|
|
69322
|
-
return null;
|
|
69323
|
-
}
|
|
69324
|
-
if (!isRecord3(parsed) || typeof parsed.ok !== "boolean") {
|
|
69325
|
-
return null;
|
|
69326
|
-
}
|
|
69327
|
-
if (parsed.ok !== true || "dryRun" in parsed) {
|
|
69328
|
-
return text2;
|
|
69329
|
-
}
|
|
69330
|
-
if (!("data" in parsed)) {
|
|
69331
|
-
return reprint(text2, withHint(parsed, NO_DATA_HINT));
|
|
69332
|
-
}
|
|
69333
|
-
const masked = { ...parsed, data: maskData(parsed.data, fields) };
|
|
69334
|
-
const miss = unmatchedHint(parsed.data, fields);
|
|
69335
|
-
return reprint(text2, miss ? withHint(masked, miss) : masked);
|
|
69336
|
-
}
|
|
69337
|
-
function maskWrites(stream, fields) {
|
|
69338
|
-
let applied = false;
|
|
69339
|
-
const write = stream.write.bind(stream);
|
|
69340
|
-
stream.write = (chunk, ...rest) => {
|
|
69341
|
-
const masked = typeof chunk === "string" ? maskPrintedEnvelope(chunk, fields) : null;
|
|
69342
|
-
if (masked === null) {
|
|
69343
|
-
return write(chunk, ...rest);
|
|
69344
|
-
}
|
|
69345
|
-
applied = true;
|
|
69346
|
-
return write(masked, ...rest);
|
|
69347
|
-
};
|
|
69348
|
-
return { sawEnvelope: () => applied };
|
|
69349
|
-
}
|
|
69350
|
-
function installFieldMask(fields, commandPath) {
|
|
69351
|
-
const mask = maskWrites(process.stdout, fields);
|
|
69352
|
-
process.on("exit", () => {
|
|
69353
|
-
if (!mask.sawEnvelope()) {
|
|
69354
|
-
process.stderr.write(
|
|
69355
|
-
`--fields was not applied: \`baker ${commandPath}\` printed no JSON envelope to narrow. Add --output json to get the envelope, or drop --fields.
|
|
69356
|
-
`
|
|
69357
|
-
);
|
|
69358
|
-
}
|
|
69359
|
-
});
|
|
69360
|
-
}
|
|
69361
|
-
|
|
69362
69245
|
// src/version.ts
|
|
69363
69246
|
import { readFileSync as readFileSync21 } from "fs";
|
|
69364
69247
|
function packageJsonUrl() {
|
|
@@ -69385,7 +69268,6 @@ var main = defineCommand248({
|
|
|
69385
69268
|
Auth: Set BAKER_API_KEY (starts with bk_) and BAKER_API_URL environment variables.
|
|
69386
69269
|
Chat: Set BAKER_CHAT_ID for action and scheduled-agent commands that stage changes against a chat.
|
|
69387
69270
|
Output: All commands return JSON envelopes: { ok: true, data: ... } or { ok: false, error: { code, message } }.
|
|
69388
|
-
Fields: --fields a,b,c keeps only those keys of each record in data, on every command that prints the envelope.
|
|
69389
69271
|
Formats: --output json works on every command that prints the envelope. --output md|files only where 'baker schema <command>' lists output. (The research commands additionally accept csv and jsonl.)
|
|
69390
69272
|
Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
69391
69273
|
},
|
|
@@ -69425,11 +69307,6 @@ installStreamTaps();
|
|
|
69425
69307
|
logInvocation(process.argv.slice(2));
|
|
69426
69308
|
installExitRecord();
|
|
69427
69309
|
process.argv = [...process.argv.slice(0, 2), ...outputFlagForCommand(main, process.argv.slice(2))];
|
|
69428
|
-
var fieldsFlag = takeFieldsFlag(main, process.argv.slice(2));
|
|
69429
|
-
if (fieldsFlag.fields) {
|
|
69430
|
-
process.argv = [...process.argv.slice(0, 2), ...fieldsFlag.argv];
|
|
69431
|
-
installFieldMask(fieldsFlag.fields, commandPathOf(main, fieldsFlag.argv));
|
|
69432
|
-
}
|
|
69433
69310
|
refuseUnknownFlags(main, process.argv.slice(2));
|
|
69434
69311
|
process.argv = [...process.argv.slice(0, 2), ...cacheFlagForParser(process.argv.slice(2))];
|
|
69435
69312
|
runMain(main);
|