@koda-sl/baker-cli 0.121.0-dev.87e882ae1 → 0.121.0-dev.ea15b0199
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -10
- package/dist/cli.js +303 -18
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1142,7 +1142,7 @@ Notes:
|
|
|
1142
1142
|
- All write commands take `--file <json>` payloads; explicit flags override file keys. `baker schema ads.linkedin.campaigns.create` for exact args.
|
|
1143
1143
|
- Money flags (`--bid`, `--daily-budget`, `--total-budget`) require `--currency`.
|
|
1144
1144
|
- Creative media comes from the Baker library (`--image-id`/`--video-id` from `baker images`/`baker videos` — uploaded to LinkedIn at publish) or as LinkedIn URNs (`--image-urn`/`--video-urn`). Formats: `image|video|text|spotlight|follower|document|carousel|conversation|tla|jobs`; complex formats take `--file` with the full content object; conversation ads take `--file` with the message flow (`{message: {subject, body, senderName?, buttons[]}}` — buttons `NESTED` (with `nestedMessage`) or `LANDING_PAGE` (with `landingPageUrl`), ≤25 messages, bodies ≤500 chars, labels ≤25). Limits: headline ≤70, text-ad 25/75, intro soft-truncates at 600 chars. TLA sponsors an existing post via `--post-urn`.
|
|
1145
|
-
- Lead forms are file-first (`lead-forms create --file form.json`). Required: name, headline (≤60), privacyPolicyUrl, questions[] (≤12; playbook: ≤4 for completion). Each question is a predefined profile field (`{ name, predefinedField: "EMAIL" }` — Contact/Work/Company/Education/Demographic library) or a custom question (`{ name, questionType: "SINGLE_LINE_TEXT" | "MULTIPLE_CHOICE", options?: [...] }`; ≤3 custom, MULTIPLE_CHOICE needs 2–30 options). Also supported: `locale {country,language}`, `formImageId`/`formImageUrn` (banner), `consents[]` (≤5 disclosure checkboxes `{text, required}`), `hiddenFields[]` (≤20 `{name, value}` tracking fields), `legalDisclaimer`, `thankYou {message, cta, landingUrl | appointmentUrl}` (Calendly/Chili Piper booking link). The staged preview emits non-blocking best-practice warnings when a form has no qualifying question, no confirmation message/action, or no consent checkbox.
|
|
1145
|
+
- Lead forms are file-first (`lead-forms create --file form.json`). Required: name, headline (≤60), privacyPolicyUrl, questions[] (≤12; playbook: ≤4 for completion). Each question is a predefined profile field (`{ name, predefinedField: "EMAIL" }` — Contact/Work/Company/Education/Demographic library) or a custom question (`{ name, questionType: "SINGLE_LINE_TEXT" | "MULTIPLE_CHOICE", options?: [...] }`; ≤3 custom, MULTIPLE_CHOICE needs 2–30 options). Also supported: `locale {country,language}`, `formImageId`/`formImageUrn` (banner), `privacyPolicyText`, `consents[]` (≤5 disclosure checkboxes `{text, required}`), `hiddenFields[]` (≤20 `{name, value}` tracking fields), `legalDisclaimer`, `thankYou {message, cta, landingUrl | appointmentUrl}` (Calendly/Chili Piper booking link). The staged preview emits non-blocking best-practice warnings when a form has no qualifying question, no confirmation message/action, or no consent checkbox.
|
|
1146
1146
|
|
|
1147
1147
|
#### `audit` — playbook diagnostic
|
|
1148
1148
|
|
|
@@ -2504,22 +2504,26 @@ Rules:
|
|
|
2504
2504
|
|
|
2505
2505
|
### Marketing Tags (`baker tags`)
|
|
2506
2506
|
|
|
2507
|
-
|
|
2508
|
-
|
|
2509
|
-
**The CLI is read-only.** Every tag change (create, edit, delete) goes through the `request_tag_input` tool (`baker_ui` MCP server): the agent proposes one or more changes — each becomes a tab in one blocking approval form — pre-filling the non-secret fields it knows; the user reviews, edits, fills secret fields, and approves or skips each tab. Approved changes stage on the chat and apply when the chat is published; discarding the chat drops them.
|
|
2507
|
+
Manage the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, PostHog, …) as per-chat **staged** changes. Nothing touches production tags until the chat is published; discarding the chat drops the draft.
|
|
2510
2508
|
|
|
2511
2509
|
`BAKER_CHAT_ID` must be set.
|
|
2512
2510
|
|
|
2513
2511
|
```bash
|
|
2514
|
-
baker tags list
|
|
2515
|
-
baker tags
|
|
2512
|
+
baker tags list # effective view: production + staged, with secret status
|
|
2513
|
+
baker tags add clarity --set projectId=abcde12345 # client-only tag, no secrets needed
|
|
2514
|
+
baker tags add meta --set pixelId=123 --request-secret accessToken
|
|
2515
|
+
baker tags update <ref> --set pixelId=999 --clear testEventCode
|
|
2516
|
+
baker tags remove <ref> # stage deletion (tag_temp_* ref drops the staged create)
|
|
2517
|
+
baker tags draft # review staged ops
|
|
2518
|
+
baker tags draft remove <ref> # drop one staged op
|
|
2519
|
+
baker tags draft clear # drop everything staged
|
|
2516
2520
|
```
|
|
2517
2521
|
|
|
2518
2522
|
Notes:
|
|
2519
2523
|
|
|
2520
|
-
- **Secrets never travel through this CLI
|
|
2521
|
-
- Staged creates get a server-generated `tag_temp_*` ref
|
|
2522
|
-
-
|
|
2524
|
+
- **Secrets never travel through this CLI.** Secret fields (`accessToken`, `apiSecret`, `authorizationToken`, `apiKey`, `conversionToken`, `oauthProviderId`) are rejected in `--set`. Stage with `--request-secret <field>`; the user provides values via the secure tag form in the chat (`request_tag_input`), which writes them straight into the staged draft. Responses only ever name which secret fields are set/pending.
|
|
2525
|
+
- Staged creates get a server-generated `tag_temp_*` ref. Use it (or a real tag id) as flow side-effect `tagIds` — the published tag keeps resolving under the temp ref.
|
|
2526
|
+
- `update`/`remove` with a `tag_temp_*` ref amend/drop the staged create in place.
|
|
2523
2527
|
- Single-instance types (`code`, `posthog`, `datafast`) reject a second instance against the chat's effective view.
|
|
2524
2528
|
- Conflicts at publish (tag deleted in the dashboard, config invalid) skip the op with a recorded reason — they never block the publish.
|
|
2525
2529
|
|
|
@@ -4541,7 +4545,6 @@ This CLI is designed for AI agent consumption. Key patterns:
|
|
|
4541
4545
|
- **0.105.0**: `baker images ...`, `baker videos ...`, and `baker testimonials ...` commands now type their `/api/{images,videos,testimonials}/...` request/response payloads from the shared `@baker/api` contract package instead of hand-written local interfaces. No command, flag, or output-shape changes.
|
|
4542
4546
|
- **0.106.0**: `baker ads linkedin` gains staged write commands — `campaign-groups`/`campaigns`/`creatives` create|update|pause|resume|(archive|)duplicate, `audiences create|upload`, `conversions create|update`, `lead-forms create|update`, plus `draft [remove|clear]` for review/undo. Ops validate at stage time, apply on chat publish, and run simulated (`urn:li:simulated:*`) unless LinkedIn writes are enabled for the company.
|
|
4543
4547
|
- **0.116.0**: `lead-forms create` models the full Campaign Manager form — `locale`, form banner image (`formImageId`/`formImageUrn`), predefined profile-field questions (validated enum) vs custom questions (`SINGLE_LINE_TEXT`/`MULTIPLE_CHOICE` with `options`, ≤3 custom), `privacyPolicyText`, disclosure `consents[]` (≤5), tracking `hiddenFields[]` (≤20), and `thankYou` confirmation CTA + landing/appointment link. Staged preview surfaces best-practice warnings (no qualifying question, no confirmation, no consent). No breaking flag changes.
|
|
4544
|
-
- **0.121.0**: `lead-forms create` drops the `privacyPolicyText` field — LinkedIn's versioned lead-form API has no privacy-policy-text slot, so it was silently discarded on publish. Use `legalDisclaimer` (shown under the form) or `consents[]` (disclosure checkboxes) instead. (Companion backend fix: staged lead-form questions were serialized in a shape LinkedIn dropped — they now publish correctly, and the staged preview lists each question.)
|
|
4545
4548
|
- **0.119.0**: `draft amend`/`draft show` land on both `baker ads google` and `baker ads linkedin` — a generic JSON-merge-patch to update any staged op in place plus a full-payload receipt, replacing remove+recreate as the correction path. Google gains `assets update` and `asset-groups create|update` (Performance Max asset groups are now their own entity — `ads create --format performanceMaxAssetGroup` never worked and is gone); `ads create --format video` moves from a bare YouTube id to `--video-assets` refs (**breaking flag change** — stage the video as an asset first); `--format demandGen` gains `--image-assets`/`--square-image-assets`/`--logo-image-assets` and flag-building for headlines/descriptions. LinkedIn's `draft list` now renders a readable Campaign group ▸ Campaign ▸ Creative tree by default (`--json` for raw), `creatives update` gains `--campaign` (re-parent while staged), and `campaigns update` passes create-only fields (`--group`/`--type`/`--locale`/`--associated-entity`) through when amending a `li_temp_*` staged create instead of always stripping them.
|
|
4546
4549
|
|
|
4547
4550
|
## Publishing
|
package/dist/cli.js
CHANGED
|
@@ -848,6 +848,7 @@ var LINKEDIN_LIMITS = {
|
|
|
848
848
|
choiceOptionsMax: 30,
|
|
849
849
|
choiceOptionTextMax: 100,
|
|
850
850
|
thankYouMessageMax: 300,
|
|
851
|
+
privacyPolicyTextMax: 2e3,
|
|
851
852
|
legalDisclaimerMax: 2e3,
|
|
852
853
|
consentsMax: 5,
|
|
853
854
|
// Campaign Manager caps disclosure checkboxes at 5
|
|
@@ -1442,6 +1443,7 @@ var leadFormFields = {
|
|
|
1442
1443
|
/** Form language, e.g. { country: "US", language: "en" }. Defaults to the account locale on LinkedIn. */
|
|
1443
1444
|
locale: z2.object({ country: z2.string().length(2), language: z2.string().length(2) }).optional(),
|
|
1444
1445
|
privacyPolicyUrl: httpsUrlSchema,
|
|
1446
|
+
privacyPolicyText: z2.string().max(LEAD.privacyPolicyTextMax).optional(),
|
|
1445
1447
|
questions: z2.array(leadFormQuestionSchema).min(1).max(LEAD.questionsMax),
|
|
1446
1448
|
consents: z2.array(leadFormConsentSchema).max(LEAD.consentsMax).optional(),
|
|
1447
1449
|
hiddenFields: z2.array(leadFormHiddenFieldSchema).max(LEAD.hiddenFieldsMax).optional(),
|
|
@@ -1939,6 +1941,46 @@ var TAG_TYPES = [
|
|
|
1939
1941
|
"twitterAds"
|
|
1940
1942
|
];
|
|
1941
1943
|
var tagTypeSchema = z5.enum(TAG_TYPES);
|
|
1944
|
+
var TAG_SECRET_FIELDS = {
|
|
1945
|
+
meta: ["accessToken"],
|
|
1946
|
+
amplitude: [],
|
|
1947
|
+
googleAds: ["oauthProviderId", "customerAccountId", "customerId", "loginCustomerId"],
|
|
1948
|
+
tiktok: ["accessToken"],
|
|
1949
|
+
vwo: [],
|
|
1950
|
+
hotjar: [],
|
|
1951
|
+
clarity: [],
|
|
1952
|
+
pinterest: ["conversionToken"],
|
|
1953
|
+
code: [],
|
|
1954
|
+
googleAnalytics: ["apiSecret"],
|
|
1955
|
+
googleTagManager: ["authorizationToken"],
|
|
1956
|
+
hubspot: [],
|
|
1957
|
+
linkedinInsightTag: ["oauthProviderId"],
|
|
1958
|
+
onetrust: [],
|
|
1959
|
+
posthog: [],
|
|
1960
|
+
datafast: ["apiKey"],
|
|
1961
|
+
recaptcha: [],
|
|
1962
|
+
twitterAds: ["oauthProviderId"]
|
|
1963
|
+
};
|
|
1964
|
+
var TAG_REQUESTABLE_SECRET_FIELDS = {
|
|
1965
|
+
meta: ["accessToken"],
|
|
1966
|
+
amplitude: [],
|
|
1967
|
+
googleAds: ["oauthProviderId", "customerAccountId"],
|
|
1968
|
+
tiktok: ["accessToken"],
|
|
1969
|
+
vwo: [],
|
|
1970
|
+
hotjar: [],
|
|
1971
|
+
clarity: [],
|
|
1972
|
+
pinterest: ["conversionToken"],
|
|
1973
|
+
code: [],
|
|
1974
|
+
googleAnalytics: ["apiSecret"],
|
|
1975
|
+
googleTagManager: ["authorizationToken"],
|
|
1976
|
+
hubspot: [],
|
|
1977
|
+
linkedinInsightTag: ["oauthProviderId"],
|
|
1978
|
+
onetrust: [],
|
|
1979
|
+
posthog: [],
|
|
1980
|
+
datafast: ["apiKey"],
|
|
1981
|
+
recaptcha: [],
|
|
1982
|
+
twitterAds: ["oauthProviderId"]
|
|
1983
|
+
};
|
|
1942
1984
|
var tagDraftOpKindSchema = z5.enum(["create", "update", "delete"]);
|
|
1943
1985
|
var tagDraftOpViewSchema = z5.object({
|
|
1944
1986
|
/** `tag_temp_*` for staged creates; the real tag id for update/delete ops. */
|
|
@@ -1974,17 +2016,49 @@ var tagsEffectiveEntrySchema = z5.object({
|
|
|
1974
2016
|
});
|
|
1975
2017
|
var tagsListRequestSchema = z5.object({ chatId: z5.string() });
|
|
1976
2018
|
var tagsListResponseSchema = z5.object({ tags: z5.array(tagsEffectiveEntrySchema) });
|
|
2019
|
+
var tagsDraftStageRequestSchema = z5.discriminatedUnion("kind", [
|
|
2020
|
+
z5.object({
|
|
2021
|
+
kind: z5.literal("create"),
|
|
2022
|
+
chatId: z5.string(),
|
|
2023
|
+
type: tagTypeSchema,
|
|
2024
|
+
config: z5.record(z5.string(), z5.string()),
|
|
2025
|
+
requestSecrets: z5.array(z5.string()).optional(),
|
|
2026
|
+
summary: z5.string().optional()
|
|
2027
|
+
}),
|
|
2028
|
+
z5.object({
|
|
2029
|
+
kind: z5.literal("update"),
|
|
2030
|
+
chatId: z5.string(),
|
|
2031
|
+
ref: z5.string(),
|
|
2032
|
+
config: z5.record(z5.string(), z5.string()).optional(),
|
|
2033
|
+
clearFields: z5.array(z5.string()).optional(),
|
|
2034
|
+
requestSecrets: z5.array(z5.string()).optional(),
|
|
2035
|
+
summary: z5.string().optional()
|
|
2036
|
+
}),
|
|
2037
|
+
z5.object({
|
|
2038
|
+
kind: z5.literal("delete"),
|
|
2039
|
+
chatId: z5.string(),
|
|
2040
|
+
ref: z5.string()
|
|
2041
|
+
})
|
|
2042
|
+
]);
|
|
2043
|
+
var tagsDraftStageResponseSchema = z5.object({
|
|
2044
|
+
/** Null when the request dissolved a staged create (delete/clearing an unpublished temp ref). */
|
|
2045
|
+
op: tagDraftOpViewSchema.nullable(),
|
|
2046
|
+
/** True when a `delete <tag_temp_*>` dropped the staged create instead of staging a delete. */
|
|
2047
|
+
removedStagedCreate: z5.boolean().optional()
|
|
2048
|
+
});
|
|
1977
2049
|
var tagsDraftListRequestSchema = z5.object({ chatId: z5.string() });
|
|
1978
2050
|
var tagsDraftListResponseSchema = z5.object({
|
|
1979
2051
|
status: z5.enum(["active", "publishing", "applied", "discarded", "none"]),
|
|
1980
2052
|
ops: z5.array(tagDraftOpViewSchema)
|
|
1981
2053
|
});
|
|
2054
|
+
var tagsDraftRemoveRequestSchema = z5.object({ chatId: z5.string(), ref: z5.string() });
|
|
2055
|
+
var tagsDraftRemoveResponseSchema = z5.object({ removed: z5.boolean() });
|
|
2056
|
+
var tagsDraftClearRequestSchema = z5.object({ chatId: z5.string() });
|
|
2057
|
+
var tagsDraftClearResponseSchema = z5.object({ cleared: z5.number() });
|
|
1982
2058
|
var tagInputRequestSchema = z5.object({
|
|
1983
|
-
|
|
1984
|
-
// body; delete shows a confirm. No tag change bypasses this approval.
|
|
1985
|
-
mode: z5.enum(["create", "edit", "delete"]),
|
|
2059
|
+
mode: z5.enum(["create", "edit"]),
|
|
1986
2060
|
tagType: tagTypeSchema,
|
|
1987
|
-
/** Edit
|
|
2061
|
+
/** Edit mode — the real tag id or `tag_temp_*` ref being edited. */
|
|
1988
2062
|
ref: z5.string().optional(),
|
|
1989
2063
|
/** Non-secret values the agent proposes to prefill. Secret keys are stripped at every boundary. */
|
|
1990
2064
|
prefilledConfig: z5.record(z5.string(), z5.string()).optional(),
|
|
@@ -1993,9 +2067,6 @@ var tagInputRequestSchema = z5.object({
|
|
|
1993
2067
|
/** Short message shown above the form explaining why the input is needed. */
|
|
1994
2068
|
message: z5.string().optional()
|
|
1995
2069
|
});
|
|
1996
|
-
var tagChangeToolInputSchema = z5.object({
|
|
1997
|
-
changes: z5.array(tagInputRequestSchema).min(1).max(8)
|
|
1998
|
-
});
|
|
1999
2070
|
var tagInputResultSchema = z5.discriminatedUnion("status", [
|
|
2000
2071
|
z5.object({
|
|
2001
2072
|
status: z5.literal("submitted"),
|
|
@@ -2011,9 +2082,6 @@ var tagInputResultSchema = z5.discriminatedUnion("status", [
|
|
|
2011
2082
|
reason: z5.string().optional()
|
|
2012
2083
|
})
|
|
2013
2084
|
]);
|
|
2014
|
-
var tagChangeToolResultSchema = z5.object({
|
|
2015
|
-
results: z5.array(tagInputResultSchema)
|
|
2016
|
-
});
|
|
2017
2085
|
|
|
2018
2086
|
// ../api/src/testimonials.ts
|
|
2019
2087
|
import { z as z6 } from "zod";
|
|
@@ -9480,7 +9548,7 @@ var leadFormsCreateCommand = defineCommand38({
|
|
|
9480
9548
|
Required: name, headline (\u226460), privacyPolicyUrl, questions[] (\u226412; playbook: \u22644 for completion).
|
|
9481
9549
|
Each question is a predefined profile field ({ name, predefinedField: "EMAIL" }) or a custom question ({ name, questionType: "MULTIPLE_CHOICE", options: [...] }; \u22643 custom).
|
|
9482
9550
|
Best-practice fields the preview will nudge for if missing: 1-3 qualifying questions, consents[] (disclosure checkboxes), thankYou.message + thankYou.landingUrl|appointmentUrl.
|
|
9483
|
-
Also supported: locale, formImageId|formImageUrn, hiddenFields[], legalDisclaimer, thankYou.cta. Example: baker ads linkedin lead-forms create --file form.json`
|
|
9551
|
+
Also supported: locale, formImageId|formImageUrn, privacyPolicyText, hiddenFields[], legalDisclaimer, thankYou.cta. Example: baker ads linkedin lead-forms create --file form.json`
|
|
9484
9552
|
},
|
|
9485
9553
|
args: {
|
|
9486
9554
|
...accountArgs,
|
|
@@ -23916,6 +23984,10 @@ var schemaCommand = defineCommand147({
|
|
|
23916
23984
|
import { defineCommand as defineCommand148 } from "citty";
|
|
23917
23985
|
|
|
23918
23986
|
// src/commands/tags/shared.ts
|
|
23987
|
+
function failValidation3(message) {
|
|
23988
|
+
writeJson({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
23989
|
+
process.exit(1);
|
|
23990
|
+
}
|
|
23919
23991
|
function failApi3(err) {
|
|
23920
23992
|
if (err instanceof ApiError) {
|
|
23921
23993
|
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
@@ -23928,6 +24000,53 @@ function failApi3(err) {
|
|
|
23928
24000
|
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
23929
24001
|
process.exit(1);
|
|
23930
24002
|
}
|
|
24003
|
+
function parseTagType(value) {
|
|
24004
|
+
const parsed = tagTypeSchema.safeParse(value);
|
|
24005
|
+
if (!parsed.success) {
|
|
24006
|
+
failValidation3(`Unknown tag type "${value}". Valid types: ${TAG_TYPES.join(", ")}.`);
|
|
24007
|
+
}
|
|
24008
|
+
return parsed.data;
|
|
24009
|
+
}
|
|
24010
|
+
function toArray(value) {
|
|
24011
|
+
if (value === void 0 || typeof value === "boolean") {
|
|
24012
|
+
return [];
|
|
24013
|
+
}
|
|
24014
|
+
return Array.isArray(value) ? value : [value];
|
|
24015
|
+
}
|
|
24016
|
+
function parseSetArgs(value) {
|
|
24017
|
+
const config = {};
|
|
24018
|
+
for (const entry of toArray(value)) {
|
|
24019
|
+
const eq = entry.indexOf("=");
|
|
24020
|
+
if (eq <= 0) {
|
|
24021
|
+
failValidation3(`--set expects key=value, got "${entry}".`);
|
|
24022
|
+
}
|
|
24023
|
+
config[entry.slice(0, eq)] = entry.slice(eq + 1);
|
|
24024
|
+
}
|
|
24025
|
+
return config;
|
|
24026
|
+
}
|
|
24027
|
+
function parseListArg(value) {
|
|
24028
|
+
return toArray(value).flatMap(
|
|
24029
|
+
(entry) => entry.split(",").map((item) => item.trim()).filter((item) => item !== "")
|
|
24030
|
+
);
|
|
24031
|
+
}
|
|
24032
|
+
function rejectSecretSets(type, config) {
|
|
24033
|
+
const secretFields = TAG_SECRET_FIELDS[type];
|
|
24034
|
+
const offending = Object.keys(config).filter((key) => secretFields.includes(key));
|
|
24035
|
+
if (offending.length > 0) {
|
|
24036
|
+
failValidation3(
|
|
24037
|
+
`${offending.join(", ")} ${offending.length === 1 ? "is a secret field" : "are secret fields"} \u2014 never pass secret values on the command line. Re-run with --request-secret ${offending[0]} and the user will provide it via the secure tag form in the chat.`
|
|
24038
|
+
);
|
|
24039
|
+
}
|
|
24040
|
+
}
|
|
24041
|
+
function validateRequestSecrets(type, fields) {
|
|
24042
|
+
const requestable = TAG_REQUESTABLE_SECRET_FIELDS[type];
|
|
24043
|
+
const invalid = fields.filter((field) => !requestable.includes(field));
|
|
24044
|
+
if (invalid.length > 0) {
|
|
24045
|
+
failValidation3(
|
|
24046
|
+
`Cannot request ${invalid.join(", ")} for a ${type} tag. Requestable secret fields: ${requestable.length > 0 ? requestable.join(", ") : "(none \u2014 this type has no secret fields)"}.`
|
|
24047
|
+
);
|
|
24048
|
+
}
|
|
24049
|
+
}
|
|
23931
24050
|
function renderEffectiveEntry(entry) {
|
|
23932
24051
|
const parts = [entry.ref, entry.type];
|
|
23933
24052
|
if (entry.identifier !== void 0) {
|
|
@@ -23949,11 +24068,47 @@ function renderEffectiveEntry(entry) {
|
|
|
23949
24068
|
// src/commands/tags/index.ts
|
|
23950
24069
|
registerSchema({
|
|
23951
24070
|
command: "tags.list",
|
|
23952
|
-
description: "Effective marketing tags for this chat: production tags overlaid with the
|
|
24071
|
+
description: "Effective marketing tags for this chat: production tags overlaid with the ops staged in this chat's draft. The printed refs (tag ids or tag_temp_*) are exactly what flow side-effect tagIds should reference.",
|
|
23953
24072
|
args: {
|
|
23954
24073
|
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list", required: false }
|
|
23955
24074
|
}
|
|
23956
24075
|
});
|
|
24076
|
+
registerSchema({
|
|
24077
|
+
command: "tags.add",
|
|
24078
|
+
description: "Stage creation of a marketing tag (applies when the chat publishes). Set every non-secret field with --set; request secret fields (API keys, tokens, OAuth connections) with --request-secret \u2014 the user provides them via the secure tag form in the chat, never through the CLI.",
|
|
24079
|
+
args: {
|
|
24080
|
+
type: {
|
|
24081
|
+
type: "positional",
|
|
24082
|
+
description: "Tag type (see `baker tags list` output / meta, googleAnalytics, clarity, \u2026)",
|
|
24083
|
+
required: true
|
|
24084
|
+
},
|
|
24085
|
+
set: { type: "string", description: "Repeatable key=value for non-secret config fields", required: false },
|
|
24086
|
+
"request-secret": {
|
|
24087
|
+
type: "string",
|
|
24088
|
+
description: "Repeatable secret field name to request from the user",
|
|
24089
|
+
required: false
|
|
24090
|
+
}
|
|
24091
|
+
}
|
|
24092
|
+
});
|
|
24093
|
+
registerSchema({
|
|
24094
|
+
command: "tags.update",
|
|
24095
|
+
description: "Stage an update to a tag by ref (real tag id, or tag_temp_* to amend a create staged in this chat). --set patches fields, --clear removes them, --request-secret asks the user for secret values.",
|
|
24096
|
+
args: {
|
|
24097
|
+
ref: { type: "positional", description: "Tag ref from `baker tags list`", required: true },
|
|
24098
|
+
set: { type: "string", description: "Repeatable key=value for non-secret config fields", required: false },
|
|
24099
|
+
clear: { type: "string", description: "Repeatable field name to clear", required: false },
|
|
24100
|
+
"request-secret": {
|
|
24101
|
+
type: "string",
|
|
24102
|
+
description: "Repeatable secret field name to request from the user",
|
|
24103
|
+
required: false
|
|
24104
|
+
}
|
|
24105
|
+
}
|
|
24106
|
+
});
|
|
24107
|
+
registerSchema({
|
|
24108
|
+
command: "tags.remove",
|
|
24109
|
+
description: "Stage deletion of a tag by ref (applies on publish). Removing a tag_temp_* ref drops the staged create instead.",
|
|
24110
|
+
args: { ref: { type: "positional", description: "Tag ref from `baker tags list`", required: true } }
|
|
24111
|
+
});
|
|
23957
24112
|
async function listTags(json) {
|
|
23958
24113
|
try {
|
|
23959
24114
|
const chatId = requireChatId();
|
|
@@ -23963,7 +24118,7 @@ async function listTags(json) {
|
|
|
23963
24118
|
return;
|
|
23964
24119
|
}
|
|
23965
24120
|
if (response.tags.length === 0) {
|
|
23966
|
-
process.stdout.write("No tags configured.
|
|
24121
|
+
process.stdout.write("No tags configured. Stage one with `baker tags add <type> --set key=value`.\n");
|
|
23967
24122
|
return;
|
|
23968
24123
|
}
|
|
23969
24124
|
process.stdout.write(`${response.tags.map(renderEffectiveEntry).join("\n")}
|
|
@@ -23982,6 +24137,84 @@ var listCommand7 = defineCommand148({
|
|
|
23982
24137
|
await listTags(args.json === true);
|
|
23983
24138
|
}
|
|
23984
24139
|
});
|
|
24140
|
+
async function stage(body) {
|
|
24141
|
+
try {
|
|
24142
|
+
const response = await apiPost("/api/tags/draft/stage", body);
|
|
24143
|
+
writeJson({ ok: true, data: response });
|
|
24144
|
+
} catch (err) {
|
|
24145
|
+
failApi3(err);
|
|
24146
|
+
}
|
|
24147
|
+
}
|
|
24148
|
+
var addCommand2 = defineCommand148({
|
|
24149
|
+
meta: {
|
|
24150
|
+
name: "add",
|
|
24151
|
+
description: "Stage a new tag (applies on publish). Secret fields are never passed here \u2014 use --request-secret and the user fills them in the chat's secure tag form. Example: baker tags add meta --set pixelId=123456 --request-secret accessToken"
|
|
24152
|
+
},
|
|
24153
|
+
args: {
|
|
24154
|
+
type: {
|
|
24155
|
+
type: "positional",
|
|
24156
|
+
description: "Tag type (meta, googleAnalytics, googleAds, clarity, \u2026)",
|
|
24157
|
+
required: true
|
|
24158
|
+
},
|
|
24159
|
+
set: { type: "string", description: "key=value for a non-secret config field (repeatable)" },
|
|
24160
|
+
"request-secret": { type: "string", description: "Secret field name the user should provide (repeatable)" }
|
|
24161
|
+
},
|
|
24162
|
+
run: async ({ args }) => {
|
|
24163
|
+
const type = parseTagType(args.type);
|
|
24164
|
+
const config = parseSetArgs(args.set);
|
|
24165
|
+
const requestSecrets = parseListArg(args["request-secret"]);
|
|
24166
|
+
rejectSecretSets(type, config);
|
|
24167
|
+
validateRequestSecrets(type, requestSecrets);
|
|
24168
|
+
const chatId = requireChatId();
|
|
24169
|
+
await stage({
|
|
24170
|
+
kind: "create",
|
|
24171
|
+
chatId,
|
|
24172
|
+
type,
|
|
24173
|
+
config,
|
|
24174
|
+
...requestSecrets.length > 0 ? { requestSecrets } : {}
|
|
24175
|
+
});
|
|
24176
|
+
}
|
|
24177
|
+
});
|
|
24178
|
+
var updateCommand3 = defineCommand148({
|
|
24179
|
+
meta: {
|
|
24180
|
+
name: "update",
|
|
24181
|
+
description: "Stage an update to a tag. A tag_temp_* ref amends the create staged in this chat. Example: baker tags update <ref> --set pixelId=999 --clear testEventCode"
|
|
24182
|
+
},
|
|
24183
|
+
args: {
|
|
24184
|
+
ref: { type: "positional", description: "Tag ref from `baker tags list`", required: true },
|
|
24185
|
+
set: { type: "string", description: "key=value patch for a non-secret config field (repeatable)" },
|
|
24186
|
+
clear: { type: "string", description: "Field name to clear (repeatable)" },
|
|
24187
|
+
"request-secret": { type: "string", description: "Secret field name the user should provide (repeatable)" }
|
|
24188
|
+
},
|
|
24189
|
+
run: async ({ args }) => {
|
|
24190
|
+
const config = parseSetArgs(args.set);
|
|
24191
|
+
const clearFields = parseListArg(args.clear);
|
|
24192
|
+
const requestSecrets = parseListArg(args["request-secret"]);
|
|
24193
|
+
if (Object.keys(config).length === 0 && clearFields.length === 0 && requestSecrets.length === 0) {
|
|
24194
|
+
failValidation3("Nothing to update \u2014 pass --set, --clear, or --request-secret.");
|
|
24195
|
+
}
|
|
24196
|
+
const chatId = requireChatId();
|
|
24197
|
+
await stage({
|
|
24198
|
+
kind: "update",
|
|
24199
|
+
chatId,
|
|
24200
|
+
ref: args.ref,
|
|
24201
|
+
...Object.keys(config).length > 0 ? { config } : {},
|
|
24202
|
+
...clearFields.length > 0 ? { clearFields } : {},
|
|
24203
|
+
...requestSecrets.length > 0 ? { requestSecrets } : {}
|
|
24204
|
+
});
|
|
24205
|
+
}
|
|
24206
|
+
});
|
|
24207
|
+
var removeCommand5 = defineCommand148({
|
|
24208
|
+
meta: {
|
|
24209
|
+
name: "remove",
|
|
24210
|
+
description: "Stage deletion of a tag (applies on publish). Removing a tag_temp_* ref drops the staged create instead. Example: baker tags remove <ref>"
|
|
24211
|
+
},
|
|
24212
|
+
args: { ref: { type: "positional", description: "Tag ref from `baker tags list`", required: true } },
|
|
24213
|
+
run: async ({ args }) => {
|
|
24214
|
+
const chatId = requireChatId();
|
|
24215
|
+
await stage({ kind: "delete", chatId, ref: args.ref });
|
|
24216
|
+
}
|
|
24217
|
+
});
|
|
23985
24218
|
async function listDraft3() {
|
|
23986
24219
|
try {
|
|
23987
24220
|
const chatId = requireChatId();
|
|
@@ -23991,10 +24224,55 @@ async function listDraft3() {
|
|
|
23991
24224
|
failApi3(err);
|
|
23992
24225
|
}
|
|
23993
24226
|
}
|
|
24227
|
+
var draftListCommand = defineCommand148({
|
|
24228
|
+
meta: { name: "list", description: "Review the tag ops staged in this chat. Example: baker tags draft" },
|
|
24229
|
+
run: async () => {
|
|
24230
|
+
await listDraft3();
|
|
24231
|
+
}
|
|
24232
|
+
});
|
|
24233
|
+
var draftRemoveCommand = defineCommand148({
|
|
24234
|
+
meta: {
|
|
24235
|
+
name: "remove",
|
|
24236
|
+
description: "Drop one staged tag op by ref. Example: baker tags draft remove tag_temp_abc123"
|
|
24237
|
+
},
|
|
24238
|
+
args: { ref: { type: "positional", description: "Staged op ref", required: true } },
|
|
24239
|
+
run: async ({ args }) => {
|
|
24240
|
+
try {
|
|
24241
|
+
const chatId = requireChatId();
|
|
24242
|
+
const response = await apiPost("/api/tags/draft/remove", {
|
|
24243
|
+
chatId,
|
|
24244
|
+
ref: args.ref
|
|
24245
|
+
});
|
|
24246
|
+
writeJson({ ok: true, data: response });
|
|
24247
|
+
} catch (err) {
|
|
24248
|
+
failApi3(err);
|
|
24249
|
+
}
|
|
24250
|
+
}
|
|
24251
|
+
});
|
|
24252
|
+
var draftClearCommand = defineCommand148({
|
|
24253
|
+
meta: {
|
|
24254
|
+
name: "clear",
|
|
24255
|
+
description: "Drop ALL tag ops staged in this chat \u2014 nothing will apply on publish. Example: baker tags draft clear"
|
|
24256
|
+
},
|
|
24257
|
+
run: async () => {
|
|
24258
|
+
try {
|
|
24259
|
+
const chatId = requireChatId();
|
|
24260
|
+
const response = await apiPost("/api/tags/draft/clear", { chatId });
|
|
24261
|
+
writeJson({ ok: true, data: response });
|
|
24262
|
+
} catch (err) {
|
|
24263
|
+
failApi3(err);
|
|
24264
|
+
}
|
|
24265
|
+
}
|
|
24266
|
+
});
|
|
23994
24267
|
var draftCommand3 = defineCommand148({
|
|
23995
24268
|
meta: {
|
|
23996
24269
|
name: "draft",
|
|
23997
|
-
description: "Review the tag
|
|
24270
|
+
description: "Review and edit the tag ops staged in this chat BEFORE publish. Subcommands: list (default), remove, clear. Ops never touch production tags until the chat is published."
|
|
24271
|
+
},
|
|
24272
|
+
subCommands: {
|
|
24273
|
+
list: draftListCommand,
|
|
24274
|
+
remove: draftRemoveCommand,
|
|
24275
|
+
clear: draftClearCommand
|
|
23998
24276
|
},
|
|
23999
24277
|
run: async () => {
|
|
24000
24278
|
await listDraft3();
|
|
@@ -24003,18 +24281,25 @@ var draftCommand3 = defineCommand148({
|
|
|
24003
24281
|
var tagsCommand3 = defineCommand148({
|
|
24004
24282
|
meta: {
|
|
24005
24283
|
name: "tags",
|
|
24006
|
-
description: `
|
|
24284
|
+
description: `Manage the client's marketing/analytics tags (Meta pixel, GA4, Google Ads, GTM, Clarity, Hotjar, \u2026) as per-chat STAGED changes \u2014 nothing touches production until the chat is published.
|
|
24007
24285
|
|
|
24008
|
-
|
|
24286
|
+
Secrets: secret fields (accessToken, apiSecret, authorizationToken, apiKey, conversionToken, oauthProviderId) are NEVER typed into chat or passed to this CLI. Stage with --request-secret <field>, then invoke the request_tag_input tool (baker_ui) so the user fills them in the dashboard's secure tag form.
|
|
24009
24287
|
|
|
24010
24288
|
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.
|
|
24011
24289
|
|
|
24012
24290
|
Examples:
|
|
24013
|
-
baker tags list
|
|
24014
|
-
baker tags
|
|
24291
|
+
baker tags list # production + staged, with secret status
|
|
24292
|
+
baker tags add clarity --set projectId=abcde12345 # client-only tag, no secrets needed
|
|
24293
|
+
baker tags add meta --set pixelId=123 --request-secret accessToken
|
|
24294
|
+
baker tags update <ref> --set pixelId=999 --clear testEventCode
|
|
24295
|
+
baker tags remove <ref> # stage deletion (temp ref = drop the create)
|
|
24296
|
+
baker tags draft # review staged ops before finishing`
|
|
24015
24297
|
},
|
|
24016
24298
|
subCommands: {
|
|
24017
24299
|
list: listCommand7,
|
|
24300
|
+
add: addCommand2,
|
|
24301
|
+
update: updateCommand3,
|
|
24302
|
+
remove: removeCommand5,
|
|
24018
24303
|
draft: draftCommand3
|
|
24019
24304
|
},
|
|
24020
24305
|
run: async () => {
|