@koda-sl/baker-cli 0.122.0-dev.3a1b48e85 → 0.122.0-dev.4a85b9f30
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 +69 -37
- package/dist/{chunk-MWFJ5NOP.js → chunk-43KBQLP5.js} +476 -104
- package/dist/chunk-43KBQLP5.js.map +1 -0
- package/dist/cli.js +1934 -1251
- package/dist/cli.js.map +1 -1
- package/dist/engine/index.d.ts +91 -0
- package/dist/engine/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-MWFJ5NOP.js.map +0 -1
package/dist/cli.js
CHANGED
|
@@ -1,19 +1,28 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
+
BackendClient,
|
|
3
4
|
ELEVENLABS_MAX_MUSIC_LENGTH_MS,
|
|
4
5
|
IMAGE_GENERATE_MODELS,
|
|
5
6
|
LayerExecutionError,
|
|
6
7
|
MODEL_REGISTRY,
|
|
8
|
+
REF_PREFIX,
|
|
7
9
|
SEEDANCE_DURATIONS,
|
|
8
10
|
ValidationError,
|
|
11
|
+
collectAssetRefLikes,
|
|
9
12
|
createEngineFromEnv,
|
|
10
13
|
defaultRegistry,
|
|
11
14
|
describeFailureReason,
|
|
12
15
|
elementMentionKeywords,
|
|
13
16
|
generateCatalog,
|
|
17
|
+
isPersistedAssetRef,
|
|
18
|
+
parseRefExpr,
|
|
19
|
+
requireCredentialsFromEnv,
|
|
14
20
|
resolveConcurrency,
|
|
21
|
+
sha256Hex,
|
|
22
|
+
toModelSafeImage,
|
|
23
|
+
ulid,
|
|
15
24
|
validateCanvasDeep
|
|
16
|
-
} from "./chunk-
|
|
25
|
+
} from "./chunk-43KBQLP5.js";
|
|
17
26
|
import {
|
|
18
27
|
csvOrJson,
|
|
19
28
|
daysAgoIso,
|
|
@@ -49,7 +58,7 @@ import {
|
|
|
49
58
|
import "./chunk-5WRI5ZAA.js";
|
|
50
59
|
|
|
51
60
|
// src/cli.ts
|
|
52
|
-
import { defineCommand as
|
|
61
|
+
import { defineCommand as defineCommand160, runMain } from "citty";
|
|
53
62
|
|
|
54
63
|
// src/commands/actions/index.ts
|
|
55
64
|
import { defineCommand as defineCommand18 } from "citty";
|
|
@@ -848,6 +857,7 @@ var LINKEDIN_LIMITS = {
|
|
|
848
857
|
choiceOptionsMax: 30,
|
|
849
858
|
choiceOptionTextMax: 100,
|
|
850
859
|
thankYouMessageMax: 300,
|
|
860
|
+
privacyPolicyTextMax: 2e3,
|
|
851
861
|
legalDisclaimerMax: 2e3,
|
|
852
862
|
consentsMax: 5,
|
|
853
863
|
// Campaign Manager caps disclosure checkboxes at 5
|
|
@@ -1442,6 +1452,7 @@ var leadFormFields = {
|
|
|
1442
1452
|
/** Form language, e.g. { country: "US", language: "en" }. Defaults to the account locale on LinkedIn. */
|
|
1443
1453
|
locale: z2.object({ country: z2.string().length(2), language: z2.string().length(2) }).optional(),
|
|
1444
1454
|
privacyPolicyUrl: httpsUrlSchema,
|
|
1455
|
+
privacyPolicyText: z2.string().max(LEAD.privacyPolicyTextMax).optional(),
|
|
1445
1456
|
questions: z2.array(leadFormQuestionSchema).min(1).max(LEAD.questionsMax),
|
|
1446
1457
|
consents: z2.array(leadFormConsentSchema).max(LEAD.consentsMax).optional(),
|
|
1447
1458
|
hiddenFields: z2.array(leadFormHiddenFieldSchema).max(LEAD.hiddenFieldsMax).optional(),
|
|
@@ -1916,241 +1927,142 @@ var imagesIngestResponseSchema = z4.object({
|
|
|
1916
1927
|
contentHash: z4.string()
|
|
1917
1928
|
});
|
|
1918
1929
|
|
|
1919
|
-
// ../api/src/tags.ts
|
|
1920
|
-
import { z as z5 } from "zod";
|
|
1921
|
-
var TAG_TYPES = [
|
|
1922
|
-
"meta",
|
|
1923
|
-
"amplitude",
|
|
1924
|
-
"googleAds",
|
|
1925
|
-
"tiktok",
|
|
1926
|
-
"vwo",
|
|
1927
|
-
"hotjar",
|
|
1928
|
-
"clarity",
|
|
1929
|
-
"pinterest",
|
|
1930
|
-
"code",
|
|
1931
|
-
"googleAnalytics",
|
|
1932
|
-
"googleTagManager",
|
|
1933
|
-
"hubspot",
|
|
1934
|
-
"linkedinInsightTag",
|
|
1935
|
-
"onetrust",
|
|
1936
|
-
"posthog",
|
|
1937
|
-
"datafast",
|
|
1938
|
-
"recaptcha",
|
|
1939
|
-
"twitterAds"
|
|
1940
|
-
];
|
|
1941
|
-
var tagTypeSchema = z5.enum(TAG_TYPES);
|
|
1942
|
-
var tagDraftOpKindSchema = z5.enum(["create", "update", "delete"]);
|
|
1943
|
-
var tagDraftOpViewSchema = z5.object({
|
|
1944
|
-
/** `tag_temp_*` for staged creates; the real tag id for update/delete ops. */
|
|
1945
|
-
ref: z5.string(),
|
|
1946
|
-
kind: tagDraftOpKindSchema,
|
|
1947
|
-
type: tagTypeSchema,
|
|
1948
|
-
/** Present on update/delete ops — the real tag this op targets. */
|
|
1949
|
-
tagId: z5.string().optional(),
|
|
1950
|
-
/** Non-secret config (create: full; update: the staged patch). Secrets are structurally absent. */
|
|
1951
|
-
config: z5.record(z5.string(), z5.string()),
|
|
1952
|
-
/** Update only — fields the op explicitly clears. */
|
|
1953
|
-
clearFields: z5.array(z5.string()).optional(),
|
|
1954
|
-
/** Names of secret fields already provided via the dashboard secure form. Never values. */
|
|
1955
|
-
secretsSet: z5.array(z5.string()),
|
|
1956
|
-
/** Names of secret fields still awaiting user input. */
|
|
1957
|
-
secretsPending: z5.array(z5.string()),
|
|
1958
|
-
summary: z5.string(),
|
|
1959
|
-
stagedAt: z5.number()
|
|
1960
|
-
});
|
|
1961
|
-
var tagsEffectiveEntrySchema = z5.object({
|
|
1962
|
-
/** Real tag id, or `tag_temp_*` for staged creates. Use as flow side-effect `tagIds` value. */
|
|
1963
|
-
ref: z5.string(),
|
|
1964
|
-
tagId: z5.string().optional(),
|
|
1965
|
-
type: tagTypeSchema,
|
|
1966
|
-
/** Value of the type's identifying field, when set. */
|
|
1967
|
-
identifier: z5.string().optional(),
|
|
1968
|
-
/** Redacted config; for staged updates, production config with the patch merged. */
|
|
1969
|
-
config: z5.record(z5.string(), z5.string()),
|
|
1970
|
-
/** Absent = live production tag with no staged changes in this chat. */
|
|
1971
|
-
staged: tagDraftOpKindSchema.optional(),
|
|
1972
|
-
secretsSet: z5.array(z5.string()),
|
|
1973
|
-
secretsPending: z5.array(z5.string())
|
|
1974
|
-
});
|
|
1975
|
-
var tagsListRequestSchema = z5.object({ chatId: z5.string() });
|
|
1976
|
-
var tagsListResponseSchema = z5.object({ tags: z5.array(tagsEffectiveEntrySchema) });
|
|
1977
|
-
var tagsDraftListRequestSchema = z5.object({ chatId: z5.string() });
|
|
1978
|
-
var tagsDraftListResponseSchema = z5.object({
|
|
1979
|
-
status: z5.enum(["active", "publishing", "applied", "discarded", "none"]),
|
|
1980
|
-
ops: z5.array(tagDraftOpViewSchema)
|
|
1981
|
-
});
|
|
1982
|
-
var tagInputRequestSchema = z5.object({
|
|
1983
|
-
// Every tag change is a tab in the approval form: create/edit show the full
|
|
1984
|
-
// body; delete shows a confirm. No tag change bypasses this approval.
|
|
1985
|
-
mode: z5.enum(["create", "edit", "delete"]),
|
|
1986
|
-
tagType: tagTypeSchema,
|
|
1987
|
-
/** Edit/delete mode — the real tag id or `tag_temp_*` ref being changed. */
|
|
1988
|
-
ref: z5.string().optional(),
|
|
1989
|
-
/** Non-secret values the agent proposes to prefill. Secret keys are stripped at every boundary. */
|
|
1990
|
-
prefilledConfig: z5.record(z5.string(), z5.string()).optional(),
|
|
1991
|
-
/** Secret field names the agent asks the user to provide. */
|
|
1992
|
-
requestedSecretFields: z5.array(z5.string()).optional(),
|
|
1993
|
-
/** Short message shown above the form explaining why the input is needed. */
|
|
1994
|
-
message: z5.string().optional()
|
|
1995
|
-
});
|
|
1996
|
-
var tagChangeToolInputSchema = z5.object({
|
|
1997
|
-
changes: z5.array(tagInputRequestSchema).min(1).max(8)
|
|
1998
|
-
});
|
|
1999
|
-
var tagInputResultSchema = z5.discriminatedUnion("status", [
|
|
2000
|
-
z5.object({
|
|
2001
|
-
status: z5.literal("submitted"),
|
|
2002
|
-
ref: z5.string(),
|
|
2003
|
-
type: tagTypeSchema,
|
|
2004
|
-
/** Identifying field name → value (non-secret), when the type has one. */
|
|
2005
|
-
identifier: z5.record(z5.string(), z5.string()).optional(),
|
|
2006
|
-
secretFieldsSet: z5.array(z5.string()),
|
|
2007
|
-
note: z5.string().optional()
|
|
2008
|
-
}),
|
|
2009
|
-
z5.object({
|
|
2010
|
-
status: z5.literal("declined"),
|
|
2011
|
-
reason: z5.string().optional()
|
|
2012
|
-
})
|
|
2013
|
-
]);
|
|
2014
|
-
var tagChangeToolResultSchema = z5.object({
|
|
2015
|
-
results: z5.array(tagInputResultSchema)
|
|
2016
|
-
});
|
|
2017
|
-
|
|
2018
1930
|
// ../api/src/testimonials.ts
|
|
2019
|
-
import { z as
|
|
2020
|
-
var testimonialSourceTypeSchema =
|
|
2021
|
-
var testimonialStatusSchema =
|
|
2022
|
-
var testimonialSentimentSchema =
|
|
2023
|
-
var testimonialDocSchema =
|
|
2024
|
-
_id:
|
|
2025
|
-
_creationTime:
|
|
2026
|
-
companyId:
|
|
2027
|
-
sourceId:
|
|
1931
|
+
import { z as z5 } from "zod";
|
|
1932
|
+
var testimonialSourceTypeSchema = z5.enum(["google", "trustpilot"]);
|
|
1933
|
+
var testimonialStatusSchema = z5.enum(["pending", "processing", "ready", "error"]);
|
|
1934
|
+
var testimonialSentimentSchema = z5.enum(["positive", "neutral", "negative"]);
|
|
1935
|
+
var testimonialDocSchema = z5.object({
|
|
1936
|
+
_id: z5.string(),
|
|
1937
|
+
_creationTime: z5.number(),
|
|
1938
|
+
companyId: z5.string(),
|
|
1939
|
+
sourceId: z5.string(),
|
|
2028
1940
|
sourceType: testimonialSourceTypeSchema,
|
|
2029
|
-
reviewText:
|
|
2030
|
-
reviewTitle:
|
|
2031
|
-
searchText:
|
|
2032
|
-
reviewerName:
|
|
2033
|
-
reviewerImageUrl:
|
|
2034
|
-
reviewerImageId:
|
|
2035
|
-
reviewerLocation:
|
|
2036
|
-
rating:
|
|
2037
|
-
reviewDate:
|
|
2038
|
-
ownerAnswer:
|
|
2039
|
-
mediaUrls:
|
|
2040
|
-
imageIds:
|
|
2041
|
-
videoIds:
|
|
2042
|
-
sourceUrl:
|
|
2043
|
-
rawData:
|
|
2044
|
-
tags:
|
|
2045
|
-
highlight:
|
|
2046
|
-
language:
|
|
2047
|
-
summary:
|
|
1941
|
+
reviewText: z5.string(),
|
|
1942
|
+
reviewTitle: z5.string().optional(),
|
|
1943
|
+
searchText: z5.string().optional(),
|
|
1944
|
+
reviewerName: z5.string().optional(),
|
|
1945
|
+
reviewerImageUrl: z5.string().optional(),
|
|
1946
|
+
reviewerImageId: z5.string().optional(),
|
|
1947
|
+
reviewerLocation: z5.string().optional(),
|
|
1948
|
+
rating: z5.number().optional(),
|
|
1949
|
+
reviewDate: z5.number().optional(),
|
|
1950
|
+
ownerAnswer: z5.string().optional(),
|
|
1951
|
+
mediaUrls: z5.array(z5.string()).optional(),
|
|
1952
|
+
imageIds: z5.array(z5.string()).optional(),
|
|
1953
|
+
videoIds: z5.array(z5.string()).optional(),
|
|
1954
|
+
sourceUrl: z5.string().optional(),
|
|
1955
|
+
rawData: z5.unknown().optional(),
|
|
1956
|
+
tags: z5.array(z5.string()),
|
|
1957
|
+
highlight: z5.string().optional(),
|
|
1958
|
+
language: z5.string().optional(),
|
|
1959
|
+
summary: z5.string().optional(),
|
|
2048
1960
|
sentiment: testimonialSentimentSchema.optional(),
|
|
2049
|
-
textEmbedding:
|
|
2050
|
-
externalId:
|
|
2051
|
-
contentHash:
|
|
1961
|
+
textEmbedding: z5.array(z5.number()).optional(),
|
|
1962
|
+
externalId: z5.string().optional(),
|
|
1963
|
+
contentHash: z5.string().optional(),
|
|
2052
1964
|
status: testimonialStatusSchema,
|
|
2053
|
-
errorMessage:
|
|
2054
|
-
createdAt:
|
|
2055
|
-
updatedAt:
|
|
1965
|
+
errorMessage: z5.string().optional(),
|
|
1966
|
+
createdAt: z5.number(),
|
|
1967
|
+
updatedAt: z5.number()
|
|
2056
1968
|
});
|
|
2057
|
-
var testimonialsListRequestSchema =
|
|
1969
|
+
var testimonialsListRequestSchema = z5.object({
|
|
2058
1970
|
source: testimonialSourceTypeSchema.optional(),
|
|
2059
|
-
rating_min:
|
|
2060
|
-
rating_max:
|
|
2061
|
-
tags:
|
|
1971
|
+
rating_min: z5.coerce.number().int().min(1).max(5).optional(),
|
|
1972
|
+
rating_max: z5.coerce.number().int().min(1).max(5).optional(),
|
|
1973
|
+
tags: z5.string().transform((s) => s.split(",").filter(Boolean)).optional(),
|
|
2062
1974
|
status: testimonialStatusSchema.optional(),
|
|
2063
1975
|
sentiment: testimonialSentimentSchema.optional(),
|
|
2064
|
-
language:
|
|
2065
|
-
limit:
|
|
2066
|
-
});
|
|
2067
|
-
var testimonialsListResponseSchema =
|
|
2068
|
-
var testimonialsGetRequestSchema =
|
|
2069
|
-
var testimonialsSearchRequestSchema =
|
|
2070
|
-
query:
|
|
2071
|
-
limit:
|
|
1976
|
+
language: z5.string().min(2).max(5).optional(),
|
|
1977
|
+
limit: z5.coerce.number().int().positive().max(200).optional()
|
|
1978
|
+
});
|
|
1979
|
+
var testimonialsListResponseSchema = z5.array(testimonialDocSchema);
|
|
1980
|
+
var testimonialsGetRequestSchema = z5.object({ id: z5.string().min(1, "Missing id parameter") });
|
|
1981
|
+
var testimonialsSearchRequestSchema = z5.object({
|
|
1982
|
+
query: z5.string().min(1),
|
|
1983
|
+
limit: z5.coerce.number().int().positive().max(100).optional(),
|
|
2072
1984
|
source: testimonialSourceTypeSchema.optional(),
|
|
2073
|
-
rating_min:
|
|
2074
|
-
rating_max:
|
|
2075
|
-
tags:
|
|
1985
|
+
rating_min: z5.coerce.number().int().min(1).max(5).optional(),
|
|
1986
|
+
rating_max: z5.coerce.number().int().min(1).max(5).optional(),
|
|
1987
|
+
tags: z5.array(z5.string()).optional(),
|
|
2076
1988
|
status: testimonialStatusSchema.optional(),
|
|
2077
1989
|
sentiment: testimonialSentimentSchema.optional(),
|
|
2078
|
-
language:
|
|
1990
|
+
language: z5.string().min(2).max(5).optional()
|
|
2079
1991
|
}).refine(
|
|
2080
1992
|
(data) => data.rating_min === void 0 || data.rating_max === void 0 || data.rating_min <= data.rating_max,
|
|
2081
1993
|
{ message: "rating_min must be less than or equal to rating_max" }
|
|
2082
1994
|
);
|
|
2083
|
-
var testimonialsSearchResponseSchema =
|
|
2084
|
-
var testimonialsOutscraperWebhookResponseSchema =
|
|
2085
|
-
ok:
|
|
2086
|
-
note:
|
|
1995
|
+
var testimonialsSearchResponseSchema = z5.array(testimonialDocSchema);
|
|
1996
|
+
var testimonialsOutscraperWebhookResponseSchema = z5.object({
|
|
1997
|
+
ok: z5.literal(true),
|
|
1998
|
+
note: z5.string().optional()
|
|
2087
1999
|
});
|
|
2088
2000
|
|
|
2089
2001
|
// ../api/src/videos.ts
|
|
2090
|
-
import { z as
|
|
2091
|
-
var videoStatusSchema =
|
|
2092
|
-
var videoTranscriptSegmentSchema =
|
|
2093
|
-
text:
|
|
2094
|
-
startSecond:
|
|
2095
|
-
endSecond:
|
|
2096
|
-
});
|
|
2097
|
-
var videoSceneSchema =
|
|
2098
|
-
title:
|
|
2099
|
-
description:
|
|
2100
|
-
startSecond:
|
|
2101
|
-
endSecond:
|
|
2102
|
-
thumbnailTime:
|
|
2103
|
-
});
|
|
2104
|
-
var videoDocSchema =
|
|
2105
|
-
_id:
|
|
2106
|
-
_creationTime:
|
|
2107
|
-
companyId:
|
|
2108
|
-
muxAssetId:
|
|
2109
|
-
muxPlaybackId:
|
|
2110
|
-
muxUploadId:
|
|
2111
|
-
name:
|
|
2112
|
-
description:
|
|
2113
|
-
tags:
|
|
2114
|
-
source:
|
|
2115
|
-
externalId:
|
|
2116
|
-
sourceId:
|
|
2117
|
-
width:
|
|
2118
|
-
height:
|
|
2119
|
-
aspectRatio:
|
|
2120
|
-
duration:
|
|
2121
|
-
transcript:
|
|
2122
|
-
transcriptSegments:
|
|
2123
|
-
scenes:
|
|
2124
|
-
descriptionEmbedding:
|
|
2125
|
-
searchText:
|
|
2002
|
+
import { z as z6 } from "zod";
|
|
2003
|
+
var videoStatusSchema = z6.enum(["uploading", "uploaded", "processing", "ready", "error"]);
|
|
2004
|
+
var videoTranscriptSegmentSchema = z6.object({
|
|
2005
|
+
text: z6.string(),
|
|
2006
|
+
startSecond: z6.number(),
|
|
2007
|
+
endSecond: z6.number()
|
|
2008
|
+
});
|
|
2009
|
+
var videoSceneSchema = z6.object({
|
|
2010
|
+
title: z6.string(),
|
|
2011
|
+
description: z6.string(),
|
|
2012
|
+
startSecond: z6.number(),
|
|
2013
|
+
endSecond: z6.number(),
|
|
2014
|
+
thumbnailTime: z6.number()
|
|
2015
|
+
});
|
|
2016
|
+
var videoDocSchema = z6.object({
|
|
2017
|
+
_id: z6.string(),
|
|
2018
|
+
_creationTime: z6.number(),
|
|
2019
|
+
companyId: z6.string(),
|
|
2020
|
+
muxAssetId: z6.string(),
|
|
2021
|
+
muxPlaybackId: z6.string(),
|
|
2022
|
+
muxUploadId: z6.string(),
|
|
2023
|
+
name: z6.string(),
|
|
2024
|
+
description: z6.string(),
|
|
2025
|
+
tags: z6.array(z6.string()),
|
|
2026
|
+
source: z6.string(),
|
|
2027
|
+
externalId: z6.string().optional(),
|
|
2028
|
+
sourceId: z6.string().optional(),
|
|
2029
|
+
width: z6.number().optional(),
|
|
2030
|
+
height: z6.number().optional(),
|
|
2031
|
+
aspectRatio: z6.number().optional(),
|
|
2032
|
+
duration: z6.number().optional(),
|
|
2033
|
+
transcript: z6.string().optional(),
|
|
2034
|
+
transcriptSegments: z6.array(videoTranscriptSegmentSchema).optional(),
|
|
2035
|
+
scenes: z6.array(videoSceneSchema).optional(),
|
|
2036
|
+
descriptionEmbedding: z6.array(z6.number()).optional(),
|
|
2037
|
+
searchText: z6.string().optional(),
|
|
2126
2038
|
status: videoStatusSchema,
|
|
2127
|
-
errorMessage:
|
|
2128
|
-
createdAt:
|
|
2129
|
-
updatedAt:
|
|
2130
|
-
thumbnailUrl:
|
|
2131
|
-
});
|
|
2132
|
-
var videosWebhookResponseSchema =
|
|
2133
|
-
var videosGetRequestSchema =
|
|
2134
|
-
var videosSearchRequestSchema =
|
|
2135
|
-
query:
|
|
2136
|
-
limit:
|
|
2137
|
-
tags:
|
|
2138
|
-
});
|
|
2139
|
-
var videoSearchResultSchema =
|
|
2140
|
-
_id:
|
|
2141
|
-
thumbnailUrl:
|
|
2142
|
-
name:
|
|
2143
|
-
description:
|
|
2144
|
-
tags:
|
|
2145
|
-
status:
|
|
2146
|
-
duration:
|
|
2147
|
-
muxPlaybackId:
|
|
2148
|
-
createdAt:
|
|
2149
|
-
});
|
|
2150
|
-
var videosSearchResponseSchema =
|
|
2151
|
-
var videosUploadResponseSchema =
|
|
2152
|
-
var videosDeleteRequestSchema =
|
|
2153
|
-
var videosDeleteResponseSchema =
|
|
2039
|
+
errorMessage: z6.string().optional(),
|
|
2040
|
+
createdAt: z6.number(),
|
|
2041
|
+
updatedAt: z6.number(),
|
|
2042
|
+
thumbnailUrl: z6.string()
|
|
2043
|
+
});
|
|
2044
|
+
var videosWebhookResponseSchema = z6.object({ ok: z6.literal(true) });
|
|
2045
|
+
var videosGetRequestSchema = z6.object({ id: z6.string().min(1, "Missing id parameter") });
|
|
2046
|
+
var videosSearchRequestSchema = z6.object({
|
|
2047
|
+
query: z6.string().min(1),
|
|
2048
|
+
limit: z6.coerce.number().int().positive().max(100).optional(),
|
|
2049
|
+
tags: z6.array(z6.string()).optional()
|
|
2050
|
+
});
|
|
2051
|
+
var videoSearchResultSchema = z6.object({
|
|
2052
|
+
_id: z6.string(),
|
|
2053
|
+
thumbnailUrl: z6.string(),
|
|
2054
|
+
name: z6.string(),
|
|
2055
|
+
description: z6.string(),
|
|
2056
|
+
tags: z6.array(z6.string()),
|
|
2057
|
+
status: z6.string(),
|
|
2058
|
+
duration: z6.number().optional(),
|
|
2059
|
+
muxPlaybackId: z6.string(),
|
|
2060
|
+
createdAt: z6.number()
|
|
2061
|
+
});
|
|
2062
|
+
var videosSearchResponseSchema = z6.array(videoSearchResultSchema);
|
|
2063
|
+
var videosUploadResponseSchema = z6.object({ uploadUrl: z6.string(), videoId: z6.string() });
|
|
2064
|
+
var videosDeleteRequestSchema = z6.object({ id: z6.string().min(1, "Missing video ID") });
|
|
2065
|
+
var videosDeleteResponseSchema = z6.object({ ok: z6.literal(true) });
|
|
2154
2066
|
|
|
2155
2067
|
// src/commands/actions/complete.ts
|
|
2156
2068
|
import { defineCommand as defineCommand2 } from "citty";
|
|
@@ -3963,37 +3875,37 @@ var GEO_TARGET_CONSTANT_REGEX = /^geoTargetConstants\/\d+$/;
|
|
|
3963
3875
|
var LANGUAGE_CONSTANT_REGEX = /^languageConstants\/\d+$/;
|
|
3964
3876
|
|
|
3965
3877
|
// ../api/src/ads-google/ops.ts
|
|
3966
|
-
import { z as
|
|
3967
|
-
var tempRefSchema2 =
|
|
3968
|
-
var refSchema =
|
|
3969
|
-
|
|
3970
|
-
|
|
3878
|
+
import { z as z7 } from "zod";
|
|
3879
|
+
var tempRefSchema2 = z7.string().regex(TEMP_REF_REGEX2, "expected a g_temp_* reference");
|
|
3880
|
+
var refSchema = z7.union([
|
|
3881
|
+
z7.string().regex(RESOURCE_NAME_REGEX, "expected a customers/\u2026/\u2026/\u2026 resource name"),
|
|
3882
|
+
z7.string().regex(NUMERIC_ID_REGEX2, "expected a numeric id"),
|
|
3971
3883
|
tempRefSchema2
|
|
3972
3884
|
]);
|
|
3973
3885
|
var targetRefSchema = refSchema;
|
|
3974
|
-
var microsSchema =
|
|
3975
|
-
var httpsUrlSchema2 =
|
|
3976
|
-
var customerIdSchema =
|
|
3977
|
-
var stageableStatusSchema2 =
|
|
3978
|
-
var matchTypeSchema =
|
|
3979
|
-
var keywordTextSchema =
|
|
3980
|
-
var budgetCreateSchema =
|
|
3981
|
-
name:
|
|
3886
|
+
var microsSchema = z7.number().int().positive("expected a positive micros amount");
|
|
3887
|
+
var httpsUrlSchema2 = z7.string().url().refine((u) => u.startsWith("https://"), "final URLs must be https");
|
|
3888
|
+
var customerIdSchema = z7.string().regex(NUMERIC_ID_REGEX2, "customerId must be the bare numeric customer id");
|
|
3889
|
+
var stageableStatusSchema2 = z7.enum(STAGEABLE_CREATE_STATUSES2);
|
|
3890
|
+
var matchTypeSchema = z7.enum(KEYWORD_MATCH_TYPES);
|
|
3891
|
+
var keywordTextSchema = z7.string().min(1).max(GOOGLE_ADS_LIMITS.keyword.textMax).refine((t) => t.trim().split(/\s+/).length <= GOOGLE_ADS_LIMITS.keyword.wordsMax, "keyword exceeds 10 words");
|
|
3892
|
+
var budgetCreateSchema = z7.object({
|
|
3893
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.budget.nameMax),
|
|
3982
3894
|
amountMicros: microsSchema,
|
|
3983
|
-
deliveryMethod:
|
|
3984
|
-
explicitlyShared:
|
|
3895
|
+
deliveryMethod: z7.enum(BUDGET_DELIVERY_METHODS).default("STANDARD"),
|
|
3896
|
+
explicitlyShared: z7.boolean().default(false)
|
|
3985
3897
|
});
|
|
3986
|
-
var budgetUpdateSchema =
|
|
3987
|
-
name:
|
|
3898
|
+
var budgetUpdateSchema = z7.object({
|
|
3899
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.budget.nameMax).optional(),
|
|
3988
3900
|
amountMicros: microsSchema.optional(),
|
|
3989
|
-
deliveryMethod:
|
|
3901
|
+
deliveryMethod: z7.enum(BUDGET_DELIVERY_METHODS).optional()
|
|
3990
3902
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
3991
|
-
var biddingConfigSchema =
|
|
3992
|
-
type:
|
|
3903
|
+
var biddingConfigSchema = z7.object({
|
|
3904
|
+
type: z7.enum(BIDDING_STRATEGY_TYPES),
|
|
3993
3905
|
targetCpaMicros: microsSchema.optional(),
|
|
3994
|
-
targetRoas:
|
|
3906
|
+
targetRoas: z7.number().positive().optional(),
|
|
3995
3907
|
cpcBidCeilingMicros: microsSchema.optional(),
|
|
3996
|
-
enhancedCpcEnabled:
|
|
3908
|
+
enhancedCpcEnabled: z7.boolean().optional()
|
|
3997
3909
|
}).superRefine((p, ctx) => {
|
|
3998
3910
|
if (p.type === "TARGET_CPA" && p.targetCpaMicros === void 0) {
|
|
3999
3911
|
ctx.addIssue({ code: "custom", path: ["targetCpaMicros"], message: "TARGET_CPA needs targetCpaMicros" });
|
|
@@ -4002,17 +3914,17 @@ var biddingConfigSchema = z8.object({
|
|
|
4002
3914
|
ctx.addIssue({ code: "custom", path: ["targetRoas"], message: "TARGET_ROAS needs targetRoas" });
|
|
4003
3915
|
}
|
|
4004
3916
|
});
|
|
4005
|
-
var networkSettingsSchema =
|
|
4006
|
-
targetGoogleSearch:
|
|
4007
|
-
targetSearchNetwork:
|
|
4008
|
-
targetContentNetwork:
|
|
4009
|
-
targetPartnerSearchNetwork:
|
|
3917
|
+
var networkSettingsSchema = z7.object({
|
|
3918
|
+
targetGoogleSearch: z7.boolean().optional(),
|
|
3919
|
+
targetSearchNetwork: z7.boolean().optional(),
|
|
3920
|
+
targetContentNetwork: z7.boolean().optional(),
|
|
3921
|
+
targetPartnerSearchNetwork: z7.boolean().optional()
|
|
4010
3922
|
});
|
|
4011
|
-
var dateSchema =
|
|
4012
|
-
var campaignCreateSchema2 =
|
|
4013
|
-
name:
|
|
4014
|
-
channelType:
|
|
4015
|
-
channelSubType:
|
|
3923
|
+
var dateSchema = z7.string().regex(/^\d{4}-\d{2}-\d{2}$/, "expected a YYYY-MM-DD date");
|
|
3924
|
+
var campaignCreateSchema2 = z7.object({
|
|
3925
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.campaign.nameMax),
|
|
3926
|
+
channelType: z7.enum(ADVERTISING_CHANNEL_TYPES),
|
|
3927
|
+
channelSubType: z7.enum(ADVERTISING_CHANNEL_SUB_TYPES).optional(),
|
|
4016
3928
|
budget: refSchema,
|
|
4017
3929
|
/** Inline standard bidding, or a portfolio strategy ref via biddingStrategy. */
|
|
4018
3930
|
bidding: biddingConfigSchema.optional(),
|
|
@@ -4021,7 +3933,7 @@ var campaignCreateSchema2 = z8.object({
|
|
|
4021
3933
|
startDate: dateSchema.optional(),
|
|
4022
3934
|
endDate: dateSchema.optional(),
|
|
4023
3935
|
/** Advisory Google Ads UI objective — drives warnings, not sent to the API. */
|
|
4024
|
-
objective:
|
|
3936
|
+
objective: z7.enum(CAMPAIGN_OBJECTIVES).optional(),
|
|
4025
3937
|
status: stageableStatusSchema2.default("PAUSED")
|
|
4026
3938
|
}).superRefine((p, ctx) => {
|
|
4027
3939
|
if (!p.bidding && !p.biddingStrategy) {
|
|
@@ -4045,129 +3957,129 @@ var campaignCreateSchema2 = z8.object({
|
|
|
4045
3957
|
ctx.addIssue({ code: "custom", path: ["endDate"], message: "endDate must be after startDate" });
|
|
4046
3958
|
}
|
|
4047
3959
|
});
|
|
4048
|
-
var campaignUpdateSchema2 =
|
|
4049
|
-
name:
|
|
3960
|
+
var campaignUpdateSchema2 = z7.object({
|
|
3961
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.campaign.nameMax).optional(),
|
|
4050
3962
|
budget: refSchema.optional(),
|
|
4051
3963
|
bidding: biddingConfigSchema.optional(),
|
|
4052
3964
|
networkSettings: networkSettingsSchema.optional(),
|
|
4053
3965
|
startDate: dateSchema.optional(),
|
|
4054
3966
|
endDate: dateSchema.optional(),
|
|
4055
|
-
status:
|
|
3967
|
+
status: z7.enum(["ENABLED", "PAUSED", "REMOVED"]).optional()
|
|
4056
3968
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4057
|
-
var adGroupCreateSchema =
|
|
4058
|
-
name:
|
|
3969
|
+
var adGroupCreateSchema = z7.object({
|
|
3970
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.adGroup.nameMax),
|
|
4059
3971
|
campaign: refSchema,
|
|
4060
|
-
type:
|
|
3972
|
+
type: z7.enum(AD_GROUP_TYPES).default("SEARCH_STANDARD"),
|
|
4061
3973
|
cpcBidMicros: microsSchema.optional(),
|
|
4062
3974
|
status: stageableStatusSchema2.default("PAUSED")
|
|
4063
3975
|
});
|
|
4064
|
-
var adGroupUpdateSchema =
|
|
4065
|
-
name:
|
|
3976
|
+
var adGroupUpdateSchema = z7.object({
|
|
3977
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.adGroup.nameMax).optional(),
|
|
4066
3978
|
cpcBidMicros: microsSchema.optional(),
|
|
4067
|
-
status:
|
|
3979
|
+
status: z7.enum(["ENABLED", "PAUSED", "REMOVED"]).optional()
|
|
4068
3980
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4069
|
-
var keywordAddSchema =
|
|
3981
|
+
var keywordAddSchema = z7.object({
|
|
4070
3982
|
adGroup: refSchema,
|
|
4071
3983
|
text: keywordTextSchema,
|
|
4072
3984
|
matchType: matchTypeSchema,
|
|
4073
3985
|
cpcBidMicros: microsSchema.optional(),
|
|
4074
|
-
finalUrls:
|
|
3986
|
+
finalUrls: z7.array(httpsUrlSchema2).optional(),
|
|
4075
3987
|
status: stageableStatusSchema2.default("ENABLED")
|
|
4076
3988
|
});
|
|
4077
|
-
var keywordUpdateSchema =
|
|
3989
|
+
var keywordUpdateSchema = z7.object({
|
|
4078
3990
|
cpcBidMicros: microsSchema.optional(),
|
|
4079
|
-
finalUrls:
|
|
4080
|
-
status:
|
|
3991
|
+
finalUrls: z7.array(httpsUrlSchema2).optional(),
|
|
3992
|
+
status: z7.enum(["ENABLED", "PAUSED", "REMOVED"]).optional()
|
|
4081
3993
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4082
|
-
var negativeKeywordAddSchema =
|
|
4083
|
-
level:
|
|
3994
|
+
var negativeKeywordAddSchema = z7.object({
|
|
3995
|
+
level: z7.enum(["adGroup", "campaign"]),
|
|
4084
3996
|
parent: refSchema,
|
|
4085
3997
|
text: keywordTextSchema,
|
|
4086
3998
|
matchType: matchTypeSchema
|
|
4087
3999
|
});
|
|
4088
|
-
var sharedSetCreateSchema =
|
|
4089
|
-
name:
|
|
4090
|
-
type:
|
|
4000
|
+
var sharedSetCreateSchema = z7.object({
|
|
4001
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.sharedSet.nameMax),
|
|
4002
|
+
type: z7.enum(SHARED_SET_TYPES).default("NEGATIVE_KEYWORDS")
|
|
4091
4003
|
});
|
|
4092
|
-
var sharedSetMemberAddSchema =
|
|
4004
|
+
var sharedSetMemberAddSchema = z7.object({
|
|
4093
4005
|
sharedSet: refSchema,
|
|
4094
4006
|
text: keywordTextSchema,
|
|
4095
4007
|
matchType: matchTypeSchema
|
|
4096
4008
|
});
|
|
4097
|
-
var campaignSharedSetAttachSchema =
|
|
4009
|
+
var campaignSharedSetAttachSchema = z7.object({
|
|
4098
4010
|
campaign: refSchema,
|
|
4099
4011
|
sharedSet: refSchema
|
|
4100
4012
|
});
|
|
4101
|
-
var adTextAssetSchema =
|
|
4102
|
-
text:
|
|
4103
|
-
pinnedField:
|
|
4013
|
+
var adTextAssetSchema = z7.object({
|
|
4014
|
+
text: z7.string().min(1),
|
|
4015
|
+
pinnedField: z7.enum(PINNED_FIELDS).optional()
|
|
4104
4016
|
});
|
|
4105
|
-
var responsiveSearchAdSchema =
|
|
4106
|
-
format:
|
|
4107
|
-
headlines:
|
|
4017
|
+
var responsiveSearchAdSchema = z7.object({
|
|
4018
|
+
format: z7.literal("responsiveSearch"),
|
|
4019
|
+
headlines: z7.array(
|
|
4108
4020
|
adTextAssetSchema.refine(
|
|
4109
4021
|
(a) => a.text.length <= GOOGLE_ADS_LIMITS.responsiveSearchAd.headlineTextMax,
|
|
4110
4022
|
"headline exceeds 30 chars"
|
|
4111
4023
|
)
|
|
4112
4024
|
).min(GOOGLE_ADS_LIMITS.responsiveSearchAd.headlinesMin).max(GOOGLE_ADS_LIMITS.responsiveSearchAd.headlinesMax),
|
|
4113
|
-
descriptions:
|
|
4025
|
+
descriptions: z7.array(
|
|
4114
4026
|
adTextAssetSchema.refine(
|
|
4115
4027
|
(a) => a.text.length <= GOOGLE_ADS_LIMITS.responsiveSearchAd.descriptionTextMax,
|
|
4116
4028
|
"description exceeds 90 chars"
|
|
4117
4029
|
)
|
|
4118
4030
|
).min(GOOGLE_ADS_LIMITS.responsiveSearchAd.descriptionsMin).max(GOOGLE_ADS_LIMITS.responsiveSearchAd.descriptionsMax),
|
|
4119
|
-
path1:
|
|
4120
|
-
path2:
|
|
4121
|
-
finalUrls:
|
|
4122
|
-
});
|
|
4123
|
-
var responsiveDisplayAdSchema =
|
|
4124
|
-
format:
|
|
4125
|
-
headlines:
|
|
4126
|
-
longHeadline:
|
|
4127
|
-
descriptions:
|
|
4128
|
-
businessName:
|
|
4031
|
+
path1: z7.string().max(GOOGLE_ADS_LIMITS.responsiveSearchAd.pathMax).optional(),
|
|
4032
|
+
path2: z7.string().max(GOOGLE_ADS_LIMITS.responsiveSearchAd.pathMax).optional(),
|
|
4033
|
+
finalUrls: z7.array(httpsUrlSchema2).min(1)
|
|
4034
|
+
});
|
|
4035
|
+
var responsiveDisplayAdSchema = z7.object({
|
|
4036
|
+
format: z7.literal("responsiveDisplay"),
|
|
4037
|
+
headlines: z7.array(z7.object({ text: z7.string().min(1).max(GOOGLE_ADS_LIMITS.responsiveDisplayAd.headlineTextMax) })).min(1).max(5),
|
|
4038
|
+
longHeadline: z7.object({ text: z7.string().min(1).max(GOOGLE_ADS_LIMITS.responsiveDisplayAd.longHeadlineTextMax) }),
|
|
4039
|
+
descriptions: z7.array(z7.object({ text: z7.string().min(1).max(GOOGLE_ADS_LIMITS.responsiveDisplayAd.descriptionTextMax) })).min(1).max(5),
|
|
4040
|
+
businessName: z7.string().min(1).max(GOOGLE_ADS_LIMITS.responsiveDisplayAd.businessNameMax),
|
|
4129
4041
|
// A Responsive Display Ad's images are fields on the ad's own content (never campaign-level
|
|
4130
4042
|
// asset links). Google requires ≥1 landscape marketing image (1.91:1) AND ≥1 square marketing
|
|
4131
4043
|
// image (1:1) to serve; the logo images are optional.
|
|
4132
|
-
marketingImageAssets:
|
|
4133
|
-
squareMarketingImageAssets:
|
|
4134
|
-
logoImageAssets:
|
|
4135
|
-
finalUrls:
|
|
4136
|
-
});
|
|
4137
|
-
var callAdSchema =
|
|
4138
|
-
format:
|
|
4139
|
-
countryCode:
|
|
4140
|
-
phoneNumber:
|
|
4141
|
-
headline1:
|
|
4142
|
-
headline2:
|
|
4143
|
-
description1:
|
|
4144
|
-
description2:
|
|
4145
|
-
businessName:
|
|
4146
|
-
finalUrls:
|
|
4147
|
-
});
|
|
4148
|
-
var appAdSchema =
|
|
4149
|
-
format:
|
|
4150
|
-
headlines:
|
|
4151
|
-
descriptions:
|
|
4152
|
-
});
|
|
4153
|
-
var videoAdSchema =
|
|
4154
|
-
format:
|
|
4044
|
+
marketingImageAssets: z7.array(refSchema).optional(),
|
|
4045
|
+
squareMarketingImageAssets: z7.array(refSchema).optional(),
|
|
4046
|
+
logoImageAssets: z7.array(refSchema).optional(),
|
|
4047
|
+
finalUrls: z7.array(httpsUrlSchema2).min(1)
|
|
4048
|
+
});
|
|
4049
|
+
var callAdSchema = z7.object({
|
|
4050
|
+
format: z7.literal("call"),
|
|
4051
|
+
countryCode: z7.string().length(2),
|
|
4052
|
+
phoneNumber: z7.string().min(3),
|
|
4053
|
+
headline1: z7.string().min(1).max(30),
|
|
4054
|
+
headline2: z7.string().min(1).max(30),
|
|
4055
|
+
description1: z7.string().min(1).max(90),
|
|
4056
|
+
description2: z7.string().min(1).max(90),
|
|
4057
|
+
businessName: z7.string().min(1).max(25),
|
|
4058
|
+
finalUrls: z7.array(httpsUrlSchema2).min(1)
|
|
4059
|
+
});
|
|
4060
|
+
var appAdSchema = z7.object({
|
|
4061
|
+
format: z7.literal("app"),
|
|
4062
|
+
headlines: z7.array(z7.object({ text: z7.string().min(1).max(30) })).min(1),
|
|
4063
|
+
descriptions: z7.array(z7.object({ text: z7.string().min(1).max(90) })).min(1)
|
|
4064
|
+
});
|
|
4065
|
+
var videoAdSchema = z7.object({
|
|
4066
|
+
format: z7.literal("video"),
|
|
4155
4067
|
// A raw YouTube id is not a publishable Google Ads reference — the video must be staged as
|
|
4156
4068
|
// its own `google.asset.create` (type: youtubeVideo) first, then referenced here by asset ref.
|
|
4157
|
-
videoAssets:
|
|
4158
|
-
finalUrls:
|
|
4159
|
-
});
|
|
4160
|
-
var demandGenAdSchema =
|
|
4161
|
-
format:
|
|
4162
|
-
headlines:
|
|
4163
|
-
descriptions:
|
|
4164
|
-
businessName:
|
|
4165
|
-
finalUrls:
|
|
4166
|
-
imageAssets:
|
|
4167
|
-
squareImageAssets:
|
|
4168
|
-
logoImageAssets:
|
|
4169
|
-
});
|
|
4170
|
-
var adContentSchema =
|
|
4069
|
+
videoAssets: z7.array(refSchema).min(1),
|
|
4070
|
+
finalUrls: z7.array(httpsUrlSchema2).min(1)
|
|
4071
|
+
});
|
|
4072
|
+
var demandGenAdSchema = z7.object({
|
|
4073
|
+
format: z7.literal("demandGen"),
|
|
4074
|
+
headlines: z7.array(z7.object({ text: z7.string().min(1).max(40) })).min(1).max(5),
|
|
4075
|
+
descriptions: z7.array(z7.object({ text: z7.string().min(1).max(90) })).min(1).max(5),
|
|
4076
|
+
businessName: z7.string().min(1).max(25),
|
|
4077
|
+
finalUrls: z7.array(httpsUrlSchema2).min(1),
|
|
4078
|
+
imageAssets: z7.array(refSchema).optional(),
|
|
4079
|
+
squareImageAssets: z7.array(refSchema).optional(),
|
|
4080
|
+
logoImageAssets: z7.array(refSchema).optional()
|
|
4081
|
+
});
|
|
4082
|
+
var adContentSchema = z7.discriminatedUnion("format", [
|
|
4171
4083
|
responsiveSearchAdSchema,
|
|
4172
4084
|
responsiveDisplayAdSchema,
|
|
4173
4085
|
callAdSchema,
|
|
@@ -4175,45 +4087,45 @@ var adContentSchema = z8.discriminatedUnion("format", [
|
|
|
4175
4087
|
videoAdSchema,
|
|
4176
4088
|
demandGenAdSchema
|
|
4177
4089
|
]);
|
|
4178
|
-
var adCreateSchema =
|
|
4090
|
+
var adCreateSchema = z7.object({
|
|
4179
4091
|
adGroup: refSchema,
|
|
4180
4092
|
status: stageableStatusSchema2.default("PAUSED"),
|
|
4181
4093
|
content: adContentSchema
|
|
4182
4094
|
});
|
|
4183
|
-
var adUpdateSchema =
|
|
4184
|
-
status:
|
|
4095
|
+
var adUpdateSchema = z7.object({
|
|
4096
|
+
status: z7.enum(["ENABLED", "PAUSED", "REMOVED"]).optional(),
|
|
4185
4097
|
/** Whole-content replacement for RSA-like formats; re-validated against adContentSchema. */
|
|
4186
|
-
content:
|
|
4098
|
+
content: z7.record(z7.string(), z7.unknown()).optional()
|
|
4187
4099
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4188
|
-
var textAssetSchema =
|
|
4189
|
-
var imageAssetSchema =
|
|
4190
|
-
type:
|
|
4191
|
-
imageId:
|
|
4192
|
-
name:
|
|
4193
|
-
});
|
|
4194
|
-
var youtubeVideoAssetSchema =
|
|
4195
|
-
type:
|
|
4196
|
-
youtubeVideoId:
|
|
4197
|
-
name:
|
|
4198
|
-
});
|
|
4199
|
-
var sitelinkAssetSchema =
|
|
4200
|
-
type:
|
|
4201
|
-
linkText:
|
|
4202
|
-
description1:
|
|
4203
|
-
description2:
|
|
4204
|
-
finalUrls:
|
|
4205
|
-
});
|
|
4206
|
-
var calloutAssetSchema =
|
|
4207
|
-
type:
|
|
4208
|
-
calloutText:
|
|
4209
|
-
});
|
|
4210
|
-
var structuredSnippetAssetSchema =
|
|
4211
|
-
type:
|
|
4212
|
-
header:
|
|
4213
|
-
values:
|
|
4214
|
-
});
|
|
4215
|
-
var callToActionAssetSchema =
|
|
4216
|
-
var assetCreateSchema =
|
|
4100
|
+
var textAssetSchema = z7.object({ type: z7.literal("text"), text: z7.string().min(1) });
|
|
4101
|
+
var imageAssetSchema = z7.object({
|
|
4102
|
+
type: z7.literal("image"),
|
|
4103
|
+
imageId: z7.string().min(1),
|
|
4104
|
+
name: z7.string().optional()
|
|
4105
|
+
});
|
|
4106
|
+
var youtubeVideoAssetSchema = z7.object({
|
|
4107
|
+
type: z7.literal("youtubeVideo"),
|
|
4108
|
+
youtubeVideoId: z7.string().min(1),
|
|
4109
|
+
name: z7.string().optional()
|
|
4110
|
+
});
|
|
4111
|
+
var sitelinkAssetSchema = z7.object({
|
|
4112
|
+
type: z7.literal("sitelink"),
|
|
4113
|
+
linkText: z7.string().min(1).max(GOOGLE_ADS_LIMITS.asset.sitelinkLinkTextMax),
|
|
4114
|
+
description1: z7.string().max(GOOGLE_ADS_LIMITS.asset.sitelinkDescriptionMax).optional(),
|
|
4115
|
+
description2: z7.string().max(GOOGLE_ADS_LIMITS.asset.sitelinkDescriptionMax).optional(),
|
|
4116
|
+
finalUrls: z7.array(httpsUrlSchema2).min(1)
|
|
4117
|
+
});
|
|
4118
|
+
var calloutAssetSchema = z7.object({
|
|
4119
|
+
type: z7.literal("callout"),
|
|
4120
|
+
calloutText: z7.string().min(1).max(GOOGLE_ADS_LIMITS.asset.calloutTextMax)
|
|
4121
|
+
});
|
|
4122
|
+
var structuredSnippetAssetSchema = z7.object({
|
|
4123
|
+
type: z7.literal("structuredSnippet"),
|
|
4124
|
+
header: z7.string().min(1).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetHeaderMax),
|
|
4125
|
+
values: z7.array(z7.string().min(1)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax)
|
|
4126
|
+
});
|
|
4127
|
+
var callToActionAssetSchema = z7.object({ type: z7.literal("callToAction"), callToAction: z7.string().min(1) });
|
|
4128
|
+
var assetCreateSchema = z7.discriminatedUnion("type", [
|
|
4217
4129
|
textAssetSchema,
|
|
4218
4130
|
imageAssetSchema,
|
|
4219
4131
|
youtubeVideoAssetSchema,
|
|
@@ -4222,136 +4134,136 @@ var assetCreateSchema = z8.discriminatedUnion("type", [
|
|
|
4222
4134
|
structuredSnippetAssetSchema,
|
|
4223
4135
|
callToActionAssetSchema
|
|
4224
4136
|
]);
|
|
4225
|
-
var assetUpdateSchema =
|
|
4226
|
-
name:
|
|
4227
|
-
linkText:
|
|
4228
|
-
description1:
|
|
4229
|
-
description2:
|
|
4230
|
-
finalUrls:
|
|
4231
|
-
calloutText:
|
|
4232
|
-
header:
|
|
4233
|
-
values:
|
|
4234
|
-
callToAction:
|
|
4235
|
-
text:
|
|
4137
|
+
var assetUpdateSchema = z7.object({
|
|
4138
|
+
name: z7.string().min(1).optional(),
|
|
4139
|
+
linkText: z7.string().min(1).max(GOOGLE_ADS_LIMITS.asset.sitelinkLinkTextMax).optional(),
|
|
4140
|
+
description1: z7.string().max(GOOGLE_ADS_LIMITS.asset.sitelinkDescriptionMax).optional(),
|
|
4141
|
+
description2: z7.string().max(GOOGLE_ADS_LIMITS.asset.sitelinkDescriptionMax).optional(),
|
|
4142
|
+
finalUrls: z7.array(httpsUrlSchema2).min(1).optional(),
|
|
4143
|
+
calloutText: z7.string().min(1).max(GOOGLE_ADS_LIMITS.asset.calloutTextMax).optional(),
|
|
4144
|
+
header: z7.string().min(1).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetHeaderMax).optional(),
|
|
4145
|
+
values: z7.array(z7.string().min(1)).min(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMin).max(GOOGLE_ADS_LIMITS.asset.structuredSnippetValuesMax).optional(),
|
|
4146
|
+
callToAction: z7.string().min(1).optional(),
|
|
4147
|
+
text: z7.string().min(1).optional()
|
|
4236
4148
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4237
|
-
var assetLinkAttachSchema =
|
|
4238
|
-
level:
|
|
4149
|
+
var assetLinkAttachSchema = z7.object({
|
|
4150
|
+
level: z7.enum(["campaign", "adGroup", "customer"]),
|
|
4239
4151
|
parent: refSchema.optional(),
|
|
4240
4152
|
asset: refSchema,
|
|
4241
|
-
fieldType:
|
|
4153
|
+
fieldType: z7.enum(ASSET_FIELD_TYPES)
|
|
4242
4154
|
}).superRefine((value, ctx) => {
|
|
4243
4155
|
if (value.level !== "customer" && !value.parent) {
|
|
4244
4156
|
ctx.addIssue({
|
|
4245
|
-
code:
|
|
4157
|
+
code: z7.ZodIssueCode.custom,
|
|
4246
4158
|
path: ["parent"],
|
|
4247
4159
|
message: `parent is required for a ${value.level}-level asset link (--parent-ref)`
|
|
4248
4160
|
});
|
|
4249
4161
|
}
|
|
4250
4162
|
});
|
|
4251
|
-
var assetGroupCreateSchema =
|
|
4163
|
+
var assetGroupCreateSchema = z7.object({
|
|
4252
4164
|
campaign: refSchema,
|
|
4253
|
-
name:
|
|
4254
|
-
finalUrls:
|
|
4255
|
-
headlines:
|
|
4256
|
-
longHeadlines:
|
|
4257
|
-
descriptions:
|
|
4258
|
-
businessName:
|
|
4259
|
-
imageAssets:
|
|
4260
|
-
squareImageAssets:
|
|
4261
|
-
logoAssets:
|
|
4262
|
-
status:
|
|
4263
|
-
});
|
|
4264
|
-
var assetGroupUpdateSchema =
|
|
4265
|
-
name:
|
|
4266
|
-
finalUrls:
|
|
4267
|
-
status:
|
|
4165
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.nameMax),
|
|
4166
|
+
finalUrls: z7.array(httpsUrlSchema2).min(1),
|
|
4167
|
+
headlines: z7.array(z7.object({ text: z7.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.headlineTextMax) })).min(GOOGLE_ADS_LIMITS.assetGroup.headlinesMin).max(GOOGLE_ADS_LIMITS.assetGroup.headlinesMax),
|
|
4168
|
+
longHeadlines: z7.array(z7.object({ text: z7.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.longHeadlineTextMax) })).min(GOOGLE_ADS_LIMITS.assetGroup.longHeadlinesMin).max(GOOGLE_ADS_LIMITS.assetGroup.longHeadlinesMax),
|
|
4169
|
+
descriptions: z7.array(z7.object({ text: z7.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.descriptionTextMax) })).min(GOOGLE_ADS_LIMITS.assetGroup.descriptionsMin).max(GOOGLE_ADS_LIMITS.assetGroup.descriptionsMax),
|
|
4170
|
+
businessName: z7.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.businessNameMax),
|
|
4171
|
+
imageAssets: z7.array(refSchema).optional(),
|
|
4172
|
+
squareImageAssets: z7.array(refSchema).optional(),
|
|
4173
|
+
logoAssets: z7.array(refSchema).optional(),
|
|
4174
|
+
status: z7.enum(["ENABLED", "PAUSED"]).default("PAUSED")
|
|
4175
|
+
});
|
|
4176
|
+
var assetGroupUpdateSchema = z7.object({
|
|
4177
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.assetGroup.nameMax).optional(),
|
|
4178
|
+
finalUrls: z7.array(httpsUrlSchema2).min(1).optional(),
|
|
4179
|
+
status: z7.enum(["ENABLED", "PAUSED", "REMOVED"]).optional()
|
|
4268
4180
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4269
|
-
var audienceCreateSchema2 =
|
|
4270
|
-
name:
|
|
4271
|
-
type:
|
|
4272
|
-
description:
|
|
4181
|
+
var audienceCreateSchema2 = z7.object({
|
|
4182
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.audience.nameMax),
|
|
4183
|
+
type: z7.enum(USER_LIST_TYPES).default("BASIC"),
|
|
4184
|
+
description: z7.string().optional(),
|
|
4273
4185
|
/** Customer-match members (crm-based) — file-first for large lists. */
|
|
4274
|
-
members:
|
|
4275
|
-
sourceFileRef:
|
|
4186
|
+
members: z7.array(z7.record(z7.string(), z7.string())).optional(),
|
|
4187
|
+
sourceFileRef: z7.string().optional()
|
|
4276
4188
|
});
|
|
4277
|
-
var audienceCriterionAttachSchema =
|
|
4278
|
-
level:
|
|
4189
|
+
var audienceCriterionAttachSchema = z7.object({
|
|
4190
|
+
level: z7.enum(["campaign", "adGroup"]),
|
|
4279
4191
|
parent: refSchema,
|
|
4280
4192
|
userList: refSchema,
|
|
4281
|
-
negative:
|
|
4193
|
+
negative: z7.boolean().default(false)
|
|
4282
4194
|
});
|
|
4283
|
-
var conversionActionCreateSchema =
|
|
4284
|
-
name:
|
|
4285
|
-
type:
|
|
4286
|
-
category:
|
|
4287
|
-
countingType:
|
|
4195
|
+
var conversionActionCreateSchema = z7.object({
|
|
4196
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.conversionAction.nameMax),
|
|
4197
|
+
type: z7.enum(CONVERSION_ACTION_TYPES).default("WEBPAGE"),
|
|
4198
|
+
category: z7.enum(CONVERSION_ACTION_CATEGORIES).default("DEFAULT"),
|
|
4199
|
+
countingType: z7.enum(CONVERSION_COUNTING_TYPES).default("ONE_PER_CLICK"),
|
|
4288
4200
|
defaultValueMicros: microsSchema.optional(),
|
|
4289
|
-
defaultCurrencyCode:
|
|
4290
|
-
clickThroughLookbackWindowDays:
|
|
4291
|
-
viewThroughLookbackWindowDays:
|
|
4292
|
-
status:
|
|
4293
|
-
});
|
|
4294
|
-
var conversionActionUpdateSchema =
|
|
4295
|
-
name:
|
|
4296
|
-
category:
|
|
4297
|
-
countingType:
|
|
4201
|
+
defaultCurrencyCode: z7.string().length(3).optional(),
|
|
4202
|
+
clickThroughLookbackWindowDays: z7.number().int().positive().optional(),
|
|
4203
|
+
viewThroughLookbackWindowDays: z7.number().int().positive().optional(),
|
|
4204
|
+
status: z7.enum(["ENABLED", "PAUSED"]).default("ENABLED")
|
|
4205
|
+
});
|
|
4206
|
+
var conversionActionUpdateSchema = z7.object({
|
|
4207
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.conversionAction.nameMax).optional(),
|
|
4208
|
+
category: z7.enum(CONVERSION_ACTION_CATEGORIES).optional(),
|
|
4209
|
+
countingType: z7.enum(CONVERSION_COUNTING_TYPES).optional(),
|
|
4298
4210
|
defaultValueMicros: microsSchema.optional(),
|
|
4299
|
-
defaultCurrencyCode:
|
|
4300
|
-
clickThroughLookbackWindowDays:
|
|
4301
|
-
viewThroughLookbackWindowDays:
|
|
4302
|
-
status:
|
|
4211
|
+
defaultCurrencyCode: z7.string().length(3).optional(),
|
|
4212
|
+
clickThroughLookbackWindowDays: z7.number().int().positive().optional(),
|
|
4213
|
+
viewThroughLookbackWindowDays: z7.number().int().positive().optional(),
|
|
4214
|
+
status: z7.enum(["ENABLED", "REMOVED", "HIDDEN"]).optional()
|
|
4303
4215
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4304
|
-
var biddingStrategyCreateSchema =
|
|
4305
|
-
name:
|
|
4216
|
+
var biddingStrategyCreateSchema = z7.object({
|
|
4217
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.biddingStrategy.nameMax),
|
|
4306
4218
|
config: biddingConfigSchema
|
|
4307
4219
|
}).superRefine((p, ctx) => {
|
|
4308
4220
|
if (p.config.type === "MANUAL_CPC") {
|
|
4309
4221
|
ctx.addIssue({ code: "custom", path: ["config", "type"], message: "portfolio strategies cannot be Manual CPC" });
|
|
4310
4222
|
}
|
|
4311
4223
|
});
|
|
4312
|
-
var biddingStrategyUpdateSchema =
|
|
4313
|
-
name:
|
|
4224
|
+
var biddingStrategyUpdateSchema = z7.object({
|
|
4225
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.biddingStrategy.nameMax).optional(),
|
|
4314
4226
|
config: biddingConfigSchema.optional()
|
|
4315
4227
|
}).refine((p) => Object.values(p).some((v) => v !== void 0), "update needs at least one field");
|
|
4316
|
-
var labelCreateSchema =
|
|
4317
|
-
name:
|
|
4318
|
-
backgroundColor:
|
|
4319
|
-
description:
|
|
4228
|
+
var labelCreateSchema = z7.object({
|
|
4229
|
+
name: z7.string().min(1).max(GOOGLE_ADS_LIMITS.label.nameMax),
|
|
4230
|
+
backgroundColor: z7.string().regex(/^#[0-9A-Fa-f]{6}$/).optional(),
|
|
4231
|
+
description: z7.string().optional()
|
|
4320
4232
|
});
|
|
4321
|
-
var labelAttachSchema =
|
|
4322
|
-
level:
|
|
4233
|
+
var labelAttachSchema = z7.object({
|
|
4234
|
+
level: z7.enum(["campaign", "adGroup", "ad"]),
|
|
4323
4235
|
parent: refSchema,
|
|
4324
4236
|
label: refSchema
|
|
4325
4237
|
});
|
|
4326
|
-
var locationCriterionSchema =
|
|
4327
|
-
criterionType:
|
|
4328
|
-
geoTargetConstant:
|
|
4329
|
-
});
|
|
4330
|
-
var languageCriterionSchema =
|
|
4331
|
-
criterionType:
|
|
4332
|
-
languageConstant:
|
|
4333
|
-
});
|
|
4334
|
-
var adScheduleCriterionSchema =
|
|
4335
|
-
criterionType:
|
|
4336
|
-
dayOfWeek:
|
|
4337
|
-
startHour:
|
|
4338
|
-
startMinute:
|
|
4339
|
-
endHour:
|
|
4340
|
-
endMinute:
|
|
4341
|
-
});
|
|
4342
|
-
var deviceCriterionSchema =
|
|
4343
|
-
criterionType:
|
|
4344
|
-
device:
|
|
4238
|
+
var locationCriterionSchema = z7.object({
|
|
4239
|
+
criterionType: z7.literal("location"),
|
|
4240
|
+
geoTargetConstant: z7.union([z7.string().regex(GEO_TARGET_CONSTANT_REGEX), z7.string().regex(NUMERIC_ID_REGEX2)])
|
|
4241
|
+
});
|
|
4242
|
+
var languageCriterionSchema = z7.object({
|
|
4243
|
+
criterionType: z7.literal("language"),
|
|
4244
|
+
languageConstant: z7.union([z7.string().regex(LANGUAGE_CONSTANT_REGEX), z7.string().regex(NUMERIC_ID_REGEX2)])
|
|
4245
|
+
});
|
|
4246
|
+
var adScheduleCriterionSchema = z7.object({
|
|
4247
|
+
criterionType: z7.literal("adSchedule"),
|
|
4248
|
+
dayOfWeek: z7.enum(DAYS_OF_WEEK),
|
|
4249
|
+
startHour: z7.number().int().min(0).max(23),
|
|
4250
|
+
startMinute: z7.enum(["ZERO", "FIFTEEN", "THIRTY", "FORTY_FIVE"]).default("ZERO"),
|
|
4251
|
+
endHour: z7.number().int().min(0).max(24),
|
|
4252
|
+
endMinute: z7.enum(["ZERO", "FIFTEEN", "THIRTY", "FORTY_FIVE"]).default("ZERO")
|
|
4253
|
+
});
|
|
4254
|
+
var deviceCriterionSchema = z7.object({
|
|
4255
|
+
criterionType: z7.literal("device"),
|
|
4256
|
+
device: z7.enum(DEVICE_TYPES),
|
|
4345
4257
|
// Google's `CampaignCriterion.bid_modifier`: "The modifier must be in the range 0.1 - 10.0. Use 0
|
|
4346
4258
|
// to opt out of a Device type." So 0 (exclude the device) and 0.1–10.0 are valid; the (0, 0.1) gap is not.
|
|
4347
|
-
bidModifier:
|
|
4259
|
+
bidModifier: z7.number().min(0).max(10).optional().refine((v) => v === void 0 || v === 0 || v >= 0.1, {
|
|
4348
4260
|
message: "bid modifier must be 0 (exclude the device) or between 0.1 and 10.0"
|
|
4349
4261
|
})
|
|
4350
4262
|
});
|
|
4351
|
-
var campaignCriterionAddSchema =
|
|
4263
|
+
var campaignCriterionAddSchema = z7.object({
|
|
4352
4264
|
campaign: refSchema,
|
|
4353
|
-
negative:
|
|
4354
|
-
criterion:
|
|
4265
|
+
negative: z7.boolean().default(false),
|
|
4266
|
+
criterion: z7.discriminatedUnion("criterionType", [
|
|
4355
4267
|
locationCriterionSchema,
|
|
4356
4268
|
languageCriterionSchema,
|
|
4357
4269
|
adScheduleCriterionSchema,
|
|
@@ -4361,7 +4273,7 @@ var campaignCriterionAddSchema = z8.object({
|
|
|
4361
4273
|
const c = val.criterion;
|
|
4362
4274
|
if (c.criterionType === "adSchedule" && c.endHour === 24 && c.endMinute !== "ZERO") {
|
|
4363
4275
|
ctx.addIssue({
|
|
4364
|
-
code:
|
|
4276
|
+
code: z7.ZodIssueCode.custom,
|
|
4365
4277
|
message: "endHour 24 (midnight) cannot have a non-zero endMinute",
|
|
4366
4278
|
path: ["criterion", "endMinute"]
|
|
4367
4279
|
});
|
|
@@ -4413,17 +4325,17 @@ var GOOGLE_DRAFT_OP_KINDS = [
|
|
|
4413
4325
|
"google.campaignCriterion.add",
|
|
4414
4326
|
"google.campaignCriterion.remove"
|
|
4415
4327
|
];
|
|
4416
|
-
var googleDraftOpKindSchema =
|
|
4328
|
+
var googleDraftOpKindSchema = z7.enum(GOOGLE_DRAFT_OP_KINDS);
|
|
4417
4329
|
function createOp2(kind, payload) {
|
|
4418
|
-
return
|
|
4330
|
+
return z7.object({ kind: z7.literal(kind), customerId: customerIdSchema, payload });
|
|
4419
4331
|
}
|
|
4420
4332
|
function updateOp2(kind, payload) {
|
|
4421
|
-
return
|
|
4333
|
+
return z7.object({ kind: z7.literal(kind), customerId: customerIdSchema, target: targetRefSchema, payload });
|
|
4422
4334
|
}
|
|
4423
4335
|
function targetOp(kind) {
|
|
4424
|
-
return
|
|
4336
|
+
return z7.object({ kind: z7.literal(kind), customerId: customerIdSchema, target: targetRefSchema });
|
|
4425
4337
|
}
|
|
4426
|
-
var googleDraftOpInputSchema =
|
|
4338
|
+
var googleDraftOpInputSchema = z7.discriminatedUnion("kind", [
|
|
4427
4339
|
createOp2("google.budget.create", budgetCreateSchema),
|
|
4428
4340
|
updateOp2("google.budget.update", budgetUpdateSchema),
|
|
4429
4341
|
createOp2("google.campaign.create", campaignCreateSchema2),
|
|
@@ -4471,132 +4383,132 @@ var googleDraftOpInputSchema = z8.discriminatedUnion("kind", [
|
|
|
4471
4383
|
]);
|
|
4472
4384
|
|
|
4473
4385
|
// ../api/src/ads-google/wire.ts
|
|
4474
|
-
import { z as
|
|
4475
|
-
var googleWriteModeSchema =
|
|
4476
|
-
var googleDraftOpResultSchema =
|
|
4477
|
-
status:
|
|
4478
|
-
resourceName:
|
|
4479
|
-
error:
|
|
4480
|
-
skippedBecause:
|
|
4481
|
-
executedAt:
|
|
4482
|
-
});
|
|
4483
|
-
var googleDraftStageRequestSchema =
|
|
4484
|
-
chatId:
|
|
4386
|
+
import { z as z8 } from "zod";
|
|
4387
|
+
var googleWriteModeSchema = z8.enum(["live", "simulated"]);
|
|
4388
|
+
var googleDraftOpResultSchema = z8.object({
|
|
4389
|
+
status: z8.enum(["applied", "simulated", "failed", "skipped"]),
|
|
4390
|
+
resourceName: z8.string().optional(),
|
|
4391
|
+
error: z8.string().optional(),
|
|
4392
|
+
skippedBecause: z8.string().optional(),
|
|
4393
|
+
executedAt: z8.number().optional()
|
|
4394
|
+
});
|
|
4395
|
+
var googleDraftStageRequestSchema = z8.object({
|
|
4396
|
+
chatId: z8.string(),
|
|
4485
4397
|
op: googleDraftOpInputSchema
|
|
4486
4398
|
});
|
|
4487
|
-
var googleDraftStageResponseSchema =
|
|
4488
|
-
staged:
|
|
4489
|
-
ref:
|
|
4399
|
+
var googleDraftStageResponseSchema = z8.object({
|
|
4400
|
+
staged: z8.literal(true),
|
|
4401
|
+
ref: z8.string(),
|
|
4490
4402
|
kind: googleDraftOpKindSchema,
|
|
4491
4403
|
mode: googleWriteModeSchema,
|
|
4492
|
-
dependsOn:
|
|
4493
|
-
summary:
|
|
4494
|
-
warnings:
|
|
4404
|
+
dependsOn: z8.array(z8.string()),
|
|
4405
|
+
summary: z8.string(),
|
|
4406
|
+
warnings: z8.array(z8.string()),
|
|
4495
4407
|
/** True when the op amended an already-staged op in place instead of appending a new one. */
|
|
4496
|
-
amended:
|
|
4408
|
+
amended: z8.boolean().optional()
|
|
4497
4409
|
});
|
|
4498
|
-
var googleDraftAmendRequestSchema =
|
|
4499
|
-
chatId:
|
|
4500
|
-
ref:
|
|
4501
|
-
patch:
|
|
4410
|
+
var googleDraftAmendRequestSchema = z8.object({
|
|
4411
|
+
chatId: z8.string(),
|
|
4412
|
+
ref: z8.string(),
|
|
4413
|
+
patch: z8.record(z8.string(), z8.unknown())
|
|
4502
4414
|
});
|
|
4503
|
-
var googleDraftShowRequestSchema =
|
|
4504
|
-
chatId:
|
|
4505
|
-
ref:
|
|
4415
|
+
var googleDraftShowRequestSchema = z8.object({
|
|
4416
|
+
chatId: z8.string(),
|
|
4417
|
+
ref: z8.string()
|
|
4506
4418
|
});
|
|
4507
4419
|
var GOOGLE_DRAFT_BATCH_MAX = 500;
|
|
4508
|
-
var googleDraftStageBatchRequestSchema =
|
|
4509
|
-
chatId:
|
|
4510
|
-
ops:
|
|
4420
|
+
var googleDraftStageBatchRequestSchema = z8.object({
|
|
4421
|
+
chatId: z8.string(),
|
|
4422
|
+
ops: z8.array(googleDraftOpInputSchema).min(1).max(GOOGLE_DRAFT_BATCH_MAX)
|
|
4511
4423
|
});
|
|
4512
|
-
var googleDraftStageBatchResponseSchema =
|
|
4513
|
-
staged:
|
|
4424
|
+
var googleDraftStageBatchResponseSchema = z8.object({
|
|
4425
|
+
staged: z8.literal(true),
|
|
4514
4426
|
mode: googleWriteModeSchema,
|
|
4515
|
-
count:
|
|
4516
|
-
ops:
|
|
4517
|
-
|
|
4518
|
-
ref:
|
|
4427
|
+
count: z8.number(),
|
|
4428
|
+
ops: z8.array(
|
|
4429
|
+
z8.object({
|
|
4430
|
+
ref: z8.string(),
|
|
4519
4431
|
kind: googleDraftOpKindSchema,
|
|
4520
|
-
dependsOn:
|
|
4521
|
-
summary:
|
|
4522
|
-
warnings:
|
|
4432
|
+
dependsOn: z8.array(z8.string()),
|
|
4433
|
+
summary: z8.string(),
|
|
4434
|
+
warnings: z8.array(z8.string())
|
|
4523
4435
|
})
|
|
4524
4436
|
)
|
|
4525
4437
|
});
|
|
4526
|
-
var googleDraftOpViewSchema =
|
|
4527
|
-
ref:
|
|
4438
|
+
var googleDraftOpViewSchema = z8.object({
|
|
4439
|
+
ref: z8.string(),
|
|
4528
4440
|
kind: googleDraftOpKindSchema,
|
|
4529
|
-
customerId:
|
|
4530
|
-
target:
|
|
4531
|
-
dependsOn:
|
|
4532
|
-
summary:
|
|
4533
|
-
stagedAt:
|
|
4441
|
+
customerId: z8.string(),
|
|
4442
|
+
target: z8.string().optional(),
|
|
4443
|
+
dependsOn: z8.array(z8.string()),
|
|
4444
|
+
summary: z8.string(),
|
|
4445
|
+
stagedAt: z8.number(),
|
|
4534
4446
|
result: googleDraftOpResultSchema.optional()
|
|
4535
4447
|
});
|
|
4536
|
-
var googleDraftShowResponseSchema =
|
|
4448
|
+
var googleDraftShowResponseSchema = z8.object({
|
|
4537
4449
|
op: googleDraftOpViewSchema.extend({
|
|
4538
|
-
payload:
|
|
4539
|
-
warnings:
|
|
4540
|
-
annotations:
|
|
4450
|
+
payload: z8.unknown().optional(),
|
|
4451
|
+
warnings: z8.array(z8.string()).optional(),
|
|
4452
|
+
annotations: z8.unknown().optional()
|
|
4541
4453
|
})
|
|
4542
4454
|
});
|
|
4543
|
-
var googleDraftListRequestSchema =
|
|
4544
|
-
chatId:
|
|
4455
|
+
var googleDraftListRequestSchema = z8.object({
|
|
4456
|
+
chatId: z8.string()
|
|
4545
4457
|
});
|
|
4546
|
-
var googleDraftAdvisorySchema =
|
|
4547
|
-
scope:
|
|
4548
|
-
message:
|
|
4458
|
+
var googleDraftAdvisorySchema = z8.object({
|
|
4459
|
+
scope: z8.enum(["campaign", "adGroup"]),
|
|
4460
|
+
message: z8.string()
|
|
4549
4461
|
});
|
|
4550
|
-
var googleDraftStatusCollectionSchema =
|
|
4551
|
-
label:
|
|
4552
|
-
added:
|
|
4553
|
-
removed:
|
|
4554
|
-
existing:
|
|
4462
|
+
var googleDraftStatusCollectionSchema = z8.object({
|
|
4463
|
+
label: z8.string(),
|
|
4464
|
+
added: z8.number(),
|
|
4465
|
+
removed: z8.number(),
|
|
4466
|
+
existing: z8.number()
|
|
4555
4467
|
});
|
|
4556
|
-
var googleDraftChangeOperationSchema =
|
|
4557
|
-
var googleDraftStatusNodeSchema =
|
|
4558
|
-
() =>
|
|
4559
|
-
entity:
|
|
4560
|
-
name:
|
|
4468
|
+
var googleDraftChangeOperationSchema = z8.enum(["create", "update", "pause", "resume", "remove"]);
|
|
4469
|
+
var googleDraftStatusNodeSchema = z8.lazy(
|
|
4470
|
+
() => z8.object({
|
|
4471
|
+
entity: z8.string(),
|
|
4472
|
+
name: z8.string(),
|
|
4561
4473
|
operation: googleDraftChangeOperationSchema.optional(),
|
|
4562
|
-
existing:
|
|
4563
|
-
collections:
|
|
4564
|
-
children:
|
|
4565
|
-
warnings:
|
|
4474
|
+
existing: z8.boolean(),
|
|
4475
|
+
collections: z8.array(googleDraftStatusCollectionSchema),
|
|
4476
|
+
children: z8.array(googleDraftStatusNodeSchema),
|
|
4477
|
+
warnings: z8.array(z8.string()).optional()
|
|
4566
4478
|
})
|
|
4567
4479
|
);
|
|
4568
|
-
var googleDraftListResponseSchema =
|
|
4569
|
-
status:
|
|
4480
|
+
var googleDraftListResponseSchema = z8.object({
|
|
4481
|
+
status: z8.enum(["active", "publishing", "applied", "discarded", "none"]),
|
|
4570
4482
|
mode: googleWriteModeSchema,
|
|
4571
|
-
count:
|
|
4572
|
-
ops:
|
|
4483
|
+
count: z8.number(),
|
|
4484
|
+
ops: z8.array(googleDraftOpViewSchema),
|
|
4573
4485
|
/** Grouped campaign ▸ ad group ▸ ad tree for the readable CLI status view. */
|
|
4574
|
-
tree:
|
|
4486
|
+
tree: z8.array(googleDraftStatusNodeSchema).optional(),
|
|
4575
4487
|
/** Non-blocking completeness advisories for the whole draft. */
|
|
4576
|
-
advisories:
|
|
4488
|
+
advisories: z8.array(googleDraftAdvisorySchema).optional()
|
|
4577
4489
|
});
|
|
4578
|
-
var googleDraftRemoveRequestSchema =
|
|
4579
|
-
chatId:
|
|
4580
|
-
ref:
|
|
4490
|
+
var googleDraftRemoveRequestSchema = z8.object({
|
|
4491
|
+
chatId: z8.string(),
|
|
4492
|
+
ref: z8.string()
|
|
4581
4493
|
});
|
|
4582
|
-
var googleDraftRemoveResponseSchema =
|
|
4494
|
+
var googleDraftRemoveResponseSchema = z8.object({
|
|
4583
4495
|
/** The requested ref plus any dependents removed by cascade. */
|
|
4584
|
-
removed:
|
|
4496
|
+
removed: z8.array(z8.string())
|
|
4585
4497
|
});
|
|
4586
|
-
var googleDraftClearRequestSchema =
|
|
4587
|
-
chatId:
|
|
4498
|
+
var googleDraftClearRequestSchema = z8.object({
|
|
4499
|
+
chatId: z8.string()
|
|
4588
4500
|
});
|
|
4589
|
-
var googleDraftClearResponseSchema =
|
|
4590
|
-
cleared:
|
|
4501
|
+
var googleDraftClearResponseSchema = z8.object({
|
|
4502
|
+
cleared: z8.number()
|
|
4591
4503
|
});
|
|
4592
|
-
var googleFieldErrorSchema =
|
|
4593
|
-
path:
|
|
4594
|
-
message:
|
|
4504
|
+
var googleFieldErrorSchema = z8.object({
|
|
4505
|
+
path: z8.string(),
|
|
4506
|
+
message: z8.string()
|
|
4595
4507
|
});
|
|
4596
|
-
var googleDraftErrorResponseSchema =
|
|
4597
|
-
code:
|
|
4598
|
-
error:
|
|
4599
|
-
fields:
|
|
4508
|
+
var googleDraftErrorResponseSchema = z8.object({
|
|
4509
|
+
code: z8.string(),
|
|
4510
|
+
error: z8.string(),
|
|
4511
|
+
fields: z8.array(googleFieldErrorSchema).optional()
|
|
4600
4512
|
});
|
|
4601
4513
|
|
|
4602
4514
|
// src/commands/ads/google/draft-status.ts
|
|
@@ -4761,11 +4673,11 @@ function rawTextEntries(value) {
|
|
|
4761
4673
|
const values = Array.isArray(value) ? value : typeof value === "string" ? [value] : [];
|
|
4762
4674
|
return values.filter((v) => typeof v === "string").flatMap((v) => v.split(",")).map((v) => v.trim()).filter(Boolean);
|
|
4763
4675
|
}
|
|
4764
|
-
function rawFileEntries(
|
|
4765
|
-
if (typeof
|
|
4676
|
+
function rawFileEntries(path15) {
|
|
4677
|
+
if (typeof path15 !== "string" || path15.length === 0) {
|
|
4766
4678
|
return [];
|
|
4767
4679
|
}
|
|
4768
|
-
return readFileSync2(
|
|
4680
|
+
return readFileSync2(path15, "utf8").split(/\r?\n/).map((line) => line.trim()).filter((line) => line.length > 0 && !line.startsWith("#"));
|
|
4769
4681
|
}
|
|
4770
4682
|
function keywordEntries(args) {
|
|
4771
4683
|
const defaultMatch = typeof args["match-type"] === "string" ? args["match-type"].toUpperCase() : void 0;
|
|
@@ -4788,19 +4700,19 @@ function keywordEntries(args) {
|
|
|
4788
4700
|
}
|
|
4789
4701
|
return entries;
|
|
4790
4702
|
}
|
|
4791
|
-
function loadJsonFileArg(
|
|
4792
|
-
if (typeof
|
|
4703
|
+
function loadJsonFileArg(path15) {
|
|
4704
|
+
if (typeof path15 !== "string" || path15.length === 0) {
|
|
4793
4705
|
return {};
|
|
4794
4706
|
}
|
|
4795
4707
|
try {
|
|
4796
|
-
const parsed = JSON.parse(readFileSync2(
|
|
4708
|
+
const parsed = JSON.parse(readFileSync2(path15, "utf8"));
|
|
4797
4709
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
4798
|
-
failWriteValidation(`${
|
|
4710
|
+
failWriteValidation(`${path15} must contain a JSON object`);
|
|
4799
4711
|
}
|
|
4800
4712
|
return parsed;
|
|
4801
4713
|
} catch (err) {
|
|
4802
4714
|
if (err instanceof SyntaxError) {
|
|
4803
|
-
failWriteValidation(`${
|
|
4715
|
+
failWriteValidation(`${path15} is not valid JSON: ${err.message}`);
|
|
4804
4716
|
}
|
|
4805
4717
|
throw err;
|
|
4806
4718
|
}
|
|
@@ -4911,10 +4823,10 @@ async function stageUpdate(kind, customerId, target, payload) {
|
|
|
4911
4823
|
async function stageTarget(kind, customerId, target) {
|
|
4912
4824
|
await stageGoogleOp({ kind, customerId, target });
|
|
4913
4825
|
}
|
|
4914
|
-
async function draftAction(
|
|
4826
|
+
async function draftAction(path15, body) {
|
|
4915
4827
|
try {
|
|
4916
4828
|
const chatId = requireChatId();
|
|
4917
|
-
const response = await apiPost(
|
|
4829
|
+
const response = await apiPost(path15, { chatId, ...body });
|
|
4918
4830
|
writeJsonEnvelope(response);
|
|
4919
4831
|
} catch (err) {
|
|
4920
4832
|
handleGoogleError(err);
|
|
@@ -8669,19 +8581,19 @@ function failWriteValidation2(message) {
|
|
|
8669
8581
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
8670
8582
|
process.exit(1);
|
|
8671
8583
|
}
|
|
8672
|
-
function loadJsonFileArg2(
|
|
8673
|
-
if (typeof
|
|
8584
|
+
function loadJsonFileArg2(path15) {
|
|
8585
|
+
if (typeof path15 !== "string" || path15.length === 0) {
|
|
8674
8586
|
return {};
|
|
8675
8587
|
}
|
|
8676
8588
|
try {
|
|
8677
|
-
const parsed = JSON.parse(readFileSync6(
|
|
8589
|
+
const parsed = JSON.parse(readFileSync6(path15, "utf8"));
|
|
8678
8590
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
8679
|
-
failWriteValidation2(`${
|
|
8591
|
+
failWriteValidation2(`${path15} must contain a JSON object`);
|
|
8680
8592
|
}
|
|
8681
8593
|
return parsed;
|
|
8682
8594
|
} catch (err) {
|
|
8683
8595
|
if (err instanceof SyntaxError) {
|
|
8684
|
-
failWriteValidation2(`${
|
|
8596
|
+
failWriteValidation2(`${path15} is not valid JSON: ${err.message}`);
|
|
8685
8597
|
}
|
|
8686
8598
|
throw err;
|
|
8687
8599
|
}
|
|
@@ -8766,15 +8678,15 @@ function parseLocaleFlag(value) {
|
|
|
8766
8678
|
}
|
|
8767
8679
|
return { language: match[1], country: match[2].toUpperCase() };
|
|
8768
8680
|
}
|
|
8769
|
-
function loadTargetingFileArg(
|
|
8770
|
-
if (typeof
|
|
8681
|
+
function loadTargetingFileArg(path15) {
|
|
8682
|
+
if (typeof path15 !== "string" || path15.length === 0) {
|
|
8771
8683
|
return void 0;
|
|
8772
8684
|
}
|
|
8773
|
-
const parsed = loadJsonFileArg2(
|
|
8685
|
+
const parsed = loadJsonFileArg2(path15);
|
|
8774
8686
|
const criteria = parsed.targetingCriteria ?? parsed;
|
|
8775
8687
|
if (!criteria.include) {
|
|
8776
8688
|
failWriteValidation2(
|
|
8777
|
-
`${
|
|
8689
|
+
`${path15} must contain targeting criteria with an "include" block (see baker schema ads.linkedin.campaigns.create)`
|
|
8778
8690
|
);
|
|
8779
8691
|
}
|
|
8780
8692
|
return criteria;
|
|
@@ -8809,14 +8721,14 @@ function parseCsvLine(line) {
|
|
|
8809
8721
|
cells.push(current);
|
|
8810
8722
|
return cells.map((cell) => cell.trim());
|
|
8811
8723
|
}
|
|
8812
|
-
function parseListFileArg(
|
|
8813
|
-
if (typeof
|
|
8724
|
+
function parseListFileArg(path15, maxRows) {
|
|
8725
|
+
if (typeof path15 !== "string" || path15.length === 0) {
|
|
8814
8726
|
return void 0;
|
|
8815
8727
|
}
|
|
8816
|
-
const raw = readFileSync6(
|
|
8728
|
+
const raw = readFileSync6(path15, "utf8");
|
|
8817
8729
|
const lines = raw.split(/\r?\n/).filter((line) => line.trim().length > 0);
|
|
8818
8730
|
if (lines.length < 2) {
|
|
8819
|
-
failWriteValidation2(`${
|
|
8731
|
+
failWriteValidation2(`${path15} needs a header row and at least one data row`);
|
|
8820
8732
|
}
|
|
8821
8733
|
const columns = parseCsvLine(lines[0]).map((column) => column.trim());
|
|
8822
8734
|
const rows = [];
|
|
@@ -8835,7 +8747,7 @@ function parseListFileArg(path12, maxRows) {
|
|
|
8835
8747
|
}
|
|
8836
8748
|
}
|
|
8837
8749
|
if (rows.length > maxRows) {
|
|
8838
|
-
failWriteValidation2(`${
|
|
8750
|
+
failWriteValidation2(`${path15} has ${rows.length} rows \u2014 the inline limit is ${maxRows}. Split the list.`);
|
|
8839
8751
|
}
|
|
8840
8752
|
return { columns, rows };
|
|
8841
8753
|
}
|
|
@@ -9480,7 +9392,7 @@ var leadFormsCreateCommand = defineCommand38({
|
|
|
9480
9392
|
Required: name, headline (\u226460), privacyPolicyUrl, questions[] (\u226412; playbook: \u22644 for completion).
|
|
9481
9393
|
Each question is a predefined profile field ({ name, predefinedField: "EMAIL" }) or a custom question ({ name, questionType: "MULTIPLE_CHOICE", options: [...] }; \u22643 custom).
|
|
9482
9394
|
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`
|
|
9395
|
+
Also supported: locale, formImageId|formImageUrn, privacyPolicyText, hiddenFields[], legalDisclaimer, thankYou.cta. Example: baker ads linkedin lead-forms create --file form.json`
|
|
9484
9396
|
},
|
|
9485
9397
|
args: {
|
|
9486
9398
|
...accountArgs,
|
|
@@ -10904,72 +10816,72 @@ var NUMERIC_ID_REGEX3 = /^\d+$/;
|
|
|
10904
10816
|
var IMAGE_HASH_REGEX = /^[A-Fa-f0-9]{16,}$/;
|
|
10905
10817
|
|
|
10906
10818
|
// ../api/src/ads-meta/ops.ts
|
|
10907
|
-
import { z as
|
|
10908
|
-
var tempRefSchema3 =
|
|
10909
|
-
var parentRefSchema2 =
|
|
10910
|
-
var moneySchema2 =
|
|
10911
|
-
amount:
|
|
10912
|
-
currencyCode:
|
|
10913
|
-
});
|
|
10914
|
-
var httpsUrlSchema3 =
|
|
10915
|
-
var bakerMediaIdSchema2 =
|
|
10916
|
-
var stageableStatusSchema3 =
|
|
10917
|
-
var updateStatusSchema =
|
|
10819
|
+
import { z as z9 } from "zod";
|
|
10820
|
+
var tempRefSchema3 = z9.string().regex(TEMP_REF_REGEX3, "expected a meta_temp_* reference");
|
|
10821
|
+
var parentRefSchema2 = z9.union([z9.string().regex(NUMERIC_ID_REGEX3, "expected a numeric id"), tempRefSchema3]);
|
|
10822
|
+
var moneySchema2 = z9.object({
|
|
10823
|
+
amount: z9.string().regex(/^\d+(\.\d{1,2})?$/, "expected a decimal amount like 50 or 50.00").refine((val) => Number(val) > 0, "amount must be greater than zero"),
|
|
10824
|
+
currencyCode: z9.string().length(3).optional()
|
|
10825
|
+
});
|
|
10826
|
+
var httpsUrlSchema3 = z9.string().url().refine((u) => u.startsWith("https://"), "destination URLs must be https");
|
|
10827
|
+
var bakerMediaIdSchema2 = z9.string().min(1);
|
|
10828
|
+
var stageableStatusSchema3 = z9.enum(STAGEABLE_CREATE_STATUSES3);
|
|
10829
|
+
var updateStatusSchema = z9.enum(UPDATE_STATUSES);
|
|
10918
10830
|
function currencyMinimums2(currencyCode) {
|
|
10919
10831
|
return CURRENCY_MINIMUMS2[currencyCode] ?? DEFAULT_CURRENCY_MINIMUM2;
|
|
10920
10832
|
}
|
|
10921
|
-
function validateDailyBudgetFloor(money, ctx,
|
|
10833
|
+
function validateDailyBudgetFloor(money, ctx, path15) {
|
|
10922
10834
|
if (money?.currencyCode) {
|
|
10923
10835
|
const min = currencyMinimums2(money.currencyCode).dailyBudgetMin;
|
|
10924
10836
|
if (Number(money.amount) < min) {
|
|
10925
|
-
ctx.addIssue({ code: "custom", path:
|
|
10837
|
+
ctx.addIssue({ code: "custom", path: path15, message: `below the ${min} ${money.currencyCode} daily minimum` });
|
|
10926
10838
|
}
|
|
10927
10839
|
}
|
|
10928
10840
|
}
|
|
10929
|
-
var geoLocationsSchema =
|
|
10930
|
-
countries:
|
|
10931
|
-
regions:
|
|
10932
|
-
cities:
|
|
10933
|
-
zips:
|
|
10934
|
-
location_types:
|
|
10935
|
-
}).catchall(
|
|
10936
|
-
var idNameSchema =
|
|
10937
|
-
var metaTargetingSchema =
|
|
10841
|
+
var geoLocationsSchema = z9.object({
|
|
10842
|
+
countries: z9.array(z9.string().length(2)).optional(),
|
|
10843
|
+
regions: z9.array(z9.object({ key: z9.string() })).optional(),
|
|
10844
|
+
cities: z9.array(z9.object({ key: z9.string(), radius: z9.number().optional(), distance_unit: z9.string().optional() })).optional(),
|
|
10845
|
+
zips: z9.array(z9.object({ key: z9.string() })).optional(),
|
|
10846
|
+
location_types: z9.array(z9.string()).optional()
|
|
10847
|
+
}).catchall(z9.unknown());
|
|
10848
|
+
var idNameSchema = z9.object({ id: z9.string(), name: z9.string().optional() });
|
|
10849
|
+
var metaTargetingSchema = z9.object({
|
|
10938
10850
|
geo_locations: geoLocationsSchema.optional(),
|
|
10939
10851
|
excluded_geo_locations: geoLocationsSchema.optional(),
|
|
10940
|
-
age_min:
|
|
10941
|
-
age_max:
|
|
10942
|
-
genders:
|
|
10943
|
-
locales:
|
|
10944
|
-
interests:
|
|
10945
|
-
behaviors:
|
|
10946
|
-
custom_audiences:
|
|
10947
|
-
excluded_custom_audiences:
|
|
10948
|
-
flexible_spec:
|
|
10949
|
-
exclusions:
|
|
10950
|
-
publisher_platforms:
|
|
10951
|
-
facebook_positions:
|
|
10952
|
-
instagram_positions:
|
|
10953
|
-
audience_network_positions:
|
|
10954
|
-
messenger_positions:
|
|
10955
|
-
device_platforms:
|
|
10956
|
-
targeting_automation:
|
|
10957
|
-
}).catchall(
|
|
10958
|
-
var specialAdCategoriesSchema =
|
|
10959
|
-
var campaignCreateSchema3 =
|
|
10960
|
-
name:
|
|
10961
|
-
objective:
|
|
10852
|
+
age_min: z9.number().int().min(13).max(65).optional(),
|
|
10853
|
+
age_max: z9.number().int().min(13).max(65).optional(),
|
|
10854
|
+
genders: z9.array(z9.union([z9.literal(1), z9.literal(2)])).optional(),
|
|
10855
|
+
locales: z9.array(z9.number().int()).optional(),
|
|
10856
|
+
interests: z9.array(idNameSchema).optional(),
|
|
10857
|
+
behaviors: z9.array(idNameSchema).optional(),
|
|
10858
|
+
custom_audiences: z9.array(z9.object({ id: parentRefSchema2 })).optional(),
|
|
10859
|
+
excluded_custom_audiences: z9.array(z9.object({ id: parentRefSchema2 })).optional(),
|
|
10860
|
+
flexible_spec: z9.array(z9.record(z9.string(), z9.unknown())).optional(),
|
|
10861
|
+
exclusions: z9.record(z9.string(), z9.unknown()).optional(),
|
|
10862
|
+
publisher_platforms: z9.array(z9.string()).optional(),
|
|
10863
|
+
facebook_positions: z9.array(z9.string()).optional(),
|
|
10864
|
+
instagram_positions: z9.array(z9.string()).optional(),
|
|
10865
|
+
audience_network_positions: z9.array(z9.string()).optional(),
|
|
10866
|
+
messenger_positions: z9.array(z9.string()).optional(),
|
|
10867
|
+
device_platforms: z9.array(z9.string()).optional(),
|
|
10868
|
+
targeting_automation: z9.object({ advantage_audience: z9.union([z9.literal(0), z9.literal(1)]) }).partial().optional()
|
|
10869
|
+
}).catchall(z9.unknown());
|
|
10870
|
+
var specialAdCategoriesSchema = z9.array(z9.enum(SPECIAL_AD_CATEGORIES)).default(["NONE"]);
|
|
10871
|
+
var campaignCreateSchema3 = z9.object({
|
|
10872
|
+
name: z9.string().min(1).max(META_LIMITS.campaign.nameMax),
|
|
10873
|
+
objective: z9.enum(OBJECTIVES),
|
|
10962
10874
|
status: stageableStatusSchema3.default("PAUSED"),
|
|
10963
10875
|
special_ad_categories: specialAdCategoriesSchema,
|
|
10964
|
-
special_ad_category_country:
|
|
10965
|
-
buying_type:
|
|
10966
|
-
bid_strategy:
|
|
10876
|
+
special_ad_category_country: z9.array(z9.string().length(2)).optional(),
|
|
10877
|
+
buying_type: z9.enum(BUYING_TYPES).default("AUCTION"),
|
|
10878
|
+
bid_strategy: z9.enum(BID_STRATEGIES).optional(),
|
|
10967
10879
|
/** Campaign Budget Optimization (Advantage campaign budget) — mutually exclusive with ad-set budgets. */
|
|
10968
10880
|
dailyBudget: moneySchema2.optional(),
|
|
10969
10881
|
lifetimeBudget: moneySchema2.optional(),
|
|
10970
10882
|
spendCap: moneySchema2.optional(),
|
|
10971
|
-
start_time:
|
|
10972
|
-
stop_time:
|
|
10883
|
+
start_time: z9.number().int().positive().optional(),
|
|
10884
|
+
stop_time: z9.number().int().positive().optional()
|
|
10973
10885
|
}).superRefine((p, ctx) => {
|
|
10974
10886
|
if (p.dailyBudget && p.lifetimeBudget) {
|
|
10975
10887
|
ctx.addIssue({ code: "custom", path: ["dailyBudget"], message: "set only one of dailyBudget or lifetimeBudget" });
|
|
@@ -10979,15 +10891,15 @@ var campaignCreateSchema3 = z10.object({
|
|
|
10979
10891
|
ctx.addIssue({ code: "custom", path: ["stop_time"], message: "stop_time must be after start_time" });
|
|
10980
10892
|
}
|
|
10981
10893
|
});
|
|
10982
|
-
var campaignUpdateSchema3 =
|
|
10983
|
-
name:
|
|
10894
|
+
var campaignUpdateSchema3 = z9.object({
|
|
10895
|
+
name: z9.string().min(1).max(META_LIMITS.campaign.nameMax).optional(),
|
|
10984
10896
|
status: updateStatusSchema.optional(),
|
|
10985
|
-
bid_strategy:
|
|
10897
|
+
bid_strategy: z9.enum(BID_STRATEGIES).optional(),
|
|
10986
10898
|
dailyBudget: moneySchema2.optional(),
|
|
10987
10899
|
lifetimeBudget: moneySchema2.optional(),
|
|
10988
10900
|
spendCap: moneySchema2.optional(),
|
|
10989
|
-
start_time:
|
|
10990
|
-
stop_time:
|
|
10901
|
+
start_time: z9.number().int().positive().optional(),
|
|
10902
|
+
stop_time: z9.number().int().positive().optional()
|
|
10991
10903
|
}).superRefine((p, ctx) => {
|
|
10992
10904
|
if (!Object.values(p).some((val) => val !== void 0)) {
|
|
10993
10905
|
ctx.addIssue({ code: "custom", message: "update needs at least one field" });
|
|
@@ -10997,38 +10909,38 @@ var campaignUpdateSchema3 = z10.object({
|
|
|
10997
10909
|
}
|
|
10998
10910
|
validateDailyBudgetFloor(p.dailyBudget, ctx, ["dailyBudget", "amount"]);
|
|
10999
10911
|
});
|
|
11000
|
-
var promotedObjectSchema =
|
|
10912
|
+
var promotedObjectSchema = z9.object({
|
|
11001
10913
|
page_id: parentRefSchema2.optional(),
|
|
11002
|
-
pixel_id:
|
|
11003
|
-
custom_event_type:
|
|
11004
|
-
application_id:
|
|
11005
|
-
object_store_url:
|
|
11006
|
-
product_catalog_id:
|
|
11007
|
-
product_set_id:
|
|
11008
|
-
whatsapp_phone_number:
|
|
11009
|
-
offline_conversion_data_set_id:
|
|
10914
|
+
pixel_id: z9.string().regex(NUMERIC_ID_REGEX3).optional(),
|
|
10915
|
+
custom_event_type: z9.enum(CUSTOM_EVENT_TYPES).optional(),
|
|
10916
|
+
application_id: z9.string().regex(NUMERIC_ID_REGEX3).optional(),
|
|
10917
|
+
object_store_url: z9.string().url().optional(),
|
|
10918
|
+
product_catalog_id: z9.string().regex(NUMERIC_ID_REGEX3).optional(),
|
|
10919
|
+
product_set_id: z9.string().regex(NUMERIC_ID_REGEX3).optional(),
|
|
10920
|
+
whatsapp_phone_number: z9.string().optional(),
|
|
10921
|
+
offline_conversion_data_set_id: z9.string().regex(NUMERIC_ID_REGEX3).optional()
|
|
11010
10922
|
}).partial();
|
|
11011
|
-
var attributionSpecSchema =
|
|
11012
|
-
|
|
11013
|
-
event_type:
|
|
11014
|
-
window_days:
|
|
10923
|
+
var attributionSpecSchema = z9.array(
|
|
10924
|
+
z9.object({
|
|
10925
|
+
event_type: z9.enum(ATTRIBUTION_EVENT_TYPES),
|
|
10926
|
+
window_days: z9.union([z9.literal(1), z9.literal(7), z9.literal(28)])
|
|
11015
10927
|
})
|
|
11016
10928
|
);
|
|
11017
10929
|
var adSetFields = {
|
|
11018
|
-
name:
|
|
10930
|
+
name: z9.string().min(1).max(META_LIMITS.adSet.nameMax),
|
|
11019
10931
|
campaign_id: parentRefSchema2,
|
|
11020
10932
|
status: stageableStatusSchema3.default("PAUSED"),
|
|
11021
10933
|
dailyBudget: moneySchema2.optional(),
|
|
11022
10934
|
lifetimeBudget: moneySchema2.optional(),
|
|
11023
10935
|
bidAmount: moneySchema2.optional(),
|
|
11024
|
-
bid_strategy:
|
|
11025
|
-
billing_event:
|
|
11026
|
-
optimization_goal:
|
|
11027
|
-
destination_type:
|
|
10936
|
+
bid_strategy: z9.enum(BID_STRATEGIES).optional(),
|
|
10937
|
+
billing_event: z9.enum(BILLING_EVENTS),
|
|
10938
|
+
optimization_goal: z9.enum(OPTIMIZATION_GOALS),
|
|
10939
|
+
destination_type: z9.enum(DESTINATION_TYPES).optional(),
|
|
11028
10940
|
promoted_object: promotedObjectSchema.optional(),
|
|
11029
10941
|
attribution_spec: attributionSpecSchema.optional(),
|
|
11030
|
-
start_time:
|
|
11031
|
-
end_time:
|
|
10942
|
+
start_time: z9.number().int().positive().optional(),
|
|
10943
|
+
end_time: z9.number().int().positive().optional(),
|
|
11032
10944
|
targeting: metaTargetingSchema
|
|
11033
10945
|
};
|
|
11034
10946
|
function validateAdSetBudgetAndBid(p, ctx) {
|
|
@@ -11046,22 +10958,22 @@ function validateAdSetBudgetAndBid(p, ctx) {
|
|
|
11046
10958
|
ctx.addIssue({ code: "custom", path: ["end_time"], message: "end_time must be after start_time" });
|
|
11047
10959
|
}
|
|
11048
10960
|
}
|
|
11049
|
-
var adSetCreateSchema =
|
|
10961
|
+
var adSetCreateSchema = z9.object(adSetFields).superRefine((p, ctx) => {
|
|
11050
10962
|
validateAdSetBudgetAndBid(p, ctx);
|
|
11051
10963
|
});
|
|
11052
|
-
var adSetUpdateSchema =
|
|
10964
|
+
var adSetUpdateSchema = z9.object({
|
|
11053
10965
|
name: adSetFields.name.optional(),
|
|
11054
10966
|
status: updateStatusSchema.optional(),
|
|
11055
10967
|
dailyBudget: moneySchema2.optional(),
|
|
11056
10968
|
lifetimeBudget: moneySchema2.optional(),
|
|
11057
10969
|
bidAmount: moneySchema2.optional(),
|
|
11058
|
-
bid_strategy:
|
|
11059
|
-
optimization_goal:
|
|
11060
|
-
destination_type:
|
|
10970
|
+
bid_strategy: z9.enum(BID_STRATEGIES).optional(),
|
|
10971
|
+
optimization_goal: z9.enum(OPTIMIZATION_GOALS).optional(),
|
|
10972
|
+
destination_type: z9.enum(DESTINATION_TYPES).optional(),
|
|
11061
10973
|
promoted_object: promotedObjectSchema.optional(),
|
|
11062
10974
|
attribution_spec: attributionSpecSchema.optional(),
|
|
11063
|
-
start_time:
|
|
11064
|
-
end_time:
|
|
10975
|
+
start_time: z9.number().int().positive().optional(),
|
|
10976
|
+
end_time: z9.number().int().positive().optional(),
|
|
11065
10977
|
targeting: metaTargetingSchema.optional()
|
|
11066
10978
|
}).superRefine((p, ctx) => {
|
|
11067
10979
|
if (!Object.values(p).some((val) => val !== void 0)) {
|
|
@@ -11069,38 +10981,38 @@ var adSetUpdateSchema = z10.object({
|
|
|
11069
10981
|
}
|
|
11070
10982
|
validateAdSetBudgetAndBid(p, ctx);
|
|
11071
10983
|
});
|
|
11072
|
-
var messageSchema =
|
|
11073
|
-
var headlineSchema2 =
|
|
11074
|
-
var descriptionSchema =
|
|
11075
|
-
var callToActionSchema =
|
|
11076
|
-
type:
|
|
10984
|
+
var messageSchema = z9.string().min(1).max(META_LIMITS.creative.messageHardMax);
|
|
10985
|
+
var headlineSchema2 = z9.string().min(1).max(META_LIMITS.creative.headlineMax);
|
|
10986
|
+
var descriptionSchema = z9.string().min(1).max(META_LIMITS.creative.descriptionMax);
|
|
10987
|
+
var callToActionSchema = z9.object({
|
|
10988
|
+
type: z9.enum(CTA_TYPES2),
|
|
11077
10989
|
/** Overrides the base link for the CTA button; defaults to the ad's link. */
|
|
11078
10990
|
link: httpsUrlSchema3.optional()
|
|
11079
10991
|
});
|
|
11080
|
-
var creativeEnhancementsSchema =
|
|
11081
|
-
standardEnhancements:
|
|
11082
|
-
features:
|
|
10992
|
+
var creativeEnhancementsSchema = z9.object({
|
|
10993
|
+
standardEnhancements: z9.enum(ENROLL_STATUSES).optional(),
|
|
10994
|
+
features: z9.record(z9.string(), z9.enum(ENROLL_STATUSES)).optional()
|
|
11083
10995
|
});
|
|
11084
10996
|
var creativeSharedFields = {
|
|
11085
|
-
name:
|
|
10997
|
+
name: z9.string().max(META_LIMITS.creative.nameMax).optional(),
|
|
11086
10998
|
/** Facebook Page id backing the ad's identity. */
|
|
11087
10999
|
page_id: parentRefSchema2,
|
|
11088
11000
|
/** Instagram account id for IG placements (aka instagram_actor_id on read). */
|
|
11089
|
-
instagram_user_id:
|
|
11001
|
+
instagram_user_id: z9.string().regex(NUMERIC_ID_REGEX3).optional(),
|
|
11090
11002
|
/** URL tracking parameters appended to the destination, e.g. "utm_source=fb&utm_campaign=x". */
|
|
11091
|
-
url_tags:
|
|
11003
|
+
url_tags: z9.string().max(1e3).optional(),
|
|
11092
11004
|
enhancements: creativeEnhancementsSchema.optional()
|
|
11093
11005
|
};
|
|
11094
11006
|
var imageMediaFields = {
|
|
11095
|
-
imageHash:
|
|
11007
|
+
imageHash: z9.string().regex(IMAGE_HASH_REGEX).optional(),
|
|
11096
11008
|
imageRef: tempRefSchema3.optional()
|
|
11097
11009
|
};
|
|
11098
11010
|
var videoMediaFields = {
|
|
11099
|
-
videoId:
|
|
11011
|
+
videoId: z9.string().regex(NUMERIC_ID_REGEX3).optional(),
|
|
11100
11012
|
videoRef: tempRefSchema3.optional(),
|
|
11101
11013
|
/** Thumbnail for a video creative — image hash, ref, or public url. */
|
|
11102
|
-
thumbnailHash:
|
|
11103
|
-
imageUrl:
|
|
11014
|
+
thumbnailHash: z9.string().regex(IMAGE_HASH_REGEX).optional(),
|
|
11015
|
+
imageUrl: z9.string().url().optional()
|
|
11104
11016
|
};
|
|
11105
11017
|
function countImageRefs(p) {
|
|
11106
11018
|
return [p.imageHash, p.imageRef].filter(Boolean).length;
|
|
@@ -11108,8 +11020,8 @@ function countImageRefs(p) {
|
|
|
11108
11020
|
function countVideoRefs(p) {
|
|
11109
11021
|
return [p.videoId, p.videoRef].filter(Boolean).length;
|
|
11110
11022
|
}
|
|
11111
|
-
var singleCreativeSchema =
|
|
11112
|
-
creativeType:
|
|
11023
|
+
var singleCreativeSchema = z9.object({
|
|
11024
|
+
creativeType: z9.literal("single"),
|
|
11113
11025
|
...creativeSharedFields,
|
|
11114
11026
|
/** Primary text. */
|
|
11115
11027
|
message: messageSchema,
|
|
@@ -11118,7 +11030,7 @@ var singleCreativeSchema = z10.object({
|
|
|
11118
11030
|
headline: headlineSchema2.optional(),
|
|
11119
11031
|
description: descriptionSchema.optional(),
|
|
11120
11032
|
/** Display URL / caption shown under the headline. */
|
|
11121
|
-
caption:
|
|
11033
|
+
caption: z9.string().max(255).optional(),
|
|
11122
11034
|
call_to_action: callToActionSchema.optional(),
|
|
11123
11035
|
...imageMediaFields,
|
|
11124
11036
|
...videoMediaFields
|
|
@@ -11142,10 +11054,10 @@ var singleCreativeSchema = z10.object({
|
|
|
11142
11054
|
});
|
|
11143
11055
|
}
|
|
11144
11056
|
});
|
|
11145
|
-
var carouselCardSchema =
|
|
11057
|
+
var carouselCardSchema = z9.object({
|
|
11146
11058
|
link: httpsUrlSchema3,
|
|
11147
|
-
headline:
|
|
11148
|
-
description:
|
|
11059
|
+
headline: z9.string().max(META_LIMITS.creative.headlineMax).optional(),
|
|
11060
|
+
description: z9.string().max(META_LIMITS.creative.descriptionMax).optional(),
|
|
11149
11061
|
call_to_action: callToActionSchema.optional(),
|
|
11150
11062
|
...imageMediaFields,
|
|
11151
11063
|
...videoMediaFields
|
|
@@ -11165,35 +11077,35 @@ var carouselCardSchema = z10.object({
|
|
|
11165
11077
|
ctx.addIssue({ code: "custom", path: ["videoId"], message: "each card is an image OR a video, not both" });
|
|
11166
11078
|
}
|
|
11167
11079
|
});
|
|
11168
|
-
var carouselCreativeSchema2 =
|
|
11169
|
-
creativeType:
|
|
11080
|
+
var carouselCreativeSchema2 = z9.object({
|
|
11081
|
+
creativeType: z9.literal("carousel"),
|
|
11170
11082
|
...creativeSharedFields,
|
|
11171
11083
|
message: messageSchema,
|
|
11172
11084
|
/** Optional "see more" card destination applied when a card has no own link. */
|
|
11173
11085
|
link: httpsUrlSchema3.optional(),
|
|
11174
11086
|
call_to_action: callToActionSchema.optional(),
|
|
11175
|
-
cards:
|
|
11087
|
+
cards: z9.array(carouselCardSchema).min(META_LIMITS.creative.carouselCardsMin).max(META_LIMITS.creative.carouselCardsMax)
|
|
11176
11088
|
});
|
|
11177
|
-
var dynamicImageSchema =
|
|
11178
|
-
var dynamicVideoSchema =
|
|
11089
|
+
var dynamicImageSchema = z9.object({ ...imageMediaFields }).refine((p) => countImageRefs(p) === 1, "each dynamic image needs exactly one reference");
|
|
11090
|
+
var dynamicVideoSchema = z9.object({
|
|
11179
11091
|
videoId: videoMediaFields.videoId,
|
|
11180
11092
|
videoRef: videoMediaFields.videoRef,
|
|
11181
11093
|
thumbnailHash: videoMediaFields.thumbnailHash
|
|
11182
11094
|
}).refine((p) => countVideoRefs(p) === 1, "each dynamic video needs exactly one reference");
|
|
11183
11095
|
var DYN = META_LIMITS.creative;
|
|
11184
|
-
var dynamicCreativeSchema =
|
|
11185
|
-
creativeType:
|
|
11096
|
+
var dynamicCreativeSchema = z9.object({
|
|
11097
|
+
creativeType: z9.literal("dynamic"),
|
|
11186
11098
|
...creativeSharedFields,
|
|
11187
|
-
bodies:
|
|
11188
|
-
titles:
|
|
11189
|
-
descriptions:
|
|
11190
|
-
images:
|
|
11191
|
-
videos:
|
|
11192
|
-
ad_formats:
|
|
11193
|
-
call_to_action_types:
|
|
11194
|
-
link_urls:
|
|
11099
|
+
bodies: z9.array(z9.object({ text: messageSchema })).min(DYN.dynamicTextsMin).max(DYN.dynamicTextsMax),
|
|
11100
|
+
titles: z9.array(z9.object({ text: headlineSchema2 })).min(DYN.dynamicTextsMin).max(DYN.dynamicTextsMax),
|
|
11101
|
+
descriptions: z9.array(z9.object({ text: descriptionSchema })).max(DYN.dynamicTextsMax).optional(),
|
|
11102
|
+
images: z9.array(dynamicImageSchema).optional(),
|
|
11103
|
+
videos: z9.array(dynamicVideoSchema).optional(),
|
|
11104
|
+
ad_formats: z9.array(z9.enum(AD_FORMATS2)).min(1),
|
|
11105
|
+
call_to_action_types: z9.array(z9.enum(CTA_TYPES2)).optional(),
|
|
11106
|
+
link_urls: z9.array(z9.object({ website_url: httpsUrlSchema3, display_url: z9.string().optional() })).min(1),
|
|
11195
11107
|
/** Multi-language / placement customization — structural passthrough for v1. */
|
|
11196
|
-
asset_customization_rules:
|
|
11108
|
+
asset_customization_rules: z9.array(z9.record(z9.string(), z9.unknown())).optional()
|
|
11197
11109
|
}).superRefine((p, ctx) => {
|
|
11198
11110
|
if (!(p.images?.length || p.videos?.length)) {
|
|
11199
11111
|
ctx.addIssue({
|
|
@@ -11203,57 +11115,57 @@ var dynamicCreativeSchema = z10.object({
|
|
|
11203
11115
|
});
|
|
11204
11116
|
}
|
|
11205
11117
|
});
|
|
11206
|
-
var existingPostCreativeSchema =
|
|
11207
|
-
creativeType:
|
|
11118
|
+
var existingPostCreativeSchema = z9.object({
|
|
11119
|
+
creativeType: z9.literal("existing_post"),
|
|
11208
11120
|
name: creativeSharedFields.name,
|
|
11209
11121
|
/** "<page_id>_<post_id>" object story id of the post to promote. */
|
|
11210
|
-
object_story_id:
|
|
11122
|
+
object_story_id: z9.string().regex(/^\d+_\d+$/, 'expected "<page_id>_<post_id>"'),
|
|
11211
11123
|
instagram_user_id: creativeSharedFields.instagram_user_id,
|
|
11212
11124
|
url_tags: creativeSharedFields.url_tags,
|
|
11213
11125
|
enhancements: creativeSharedFields.enhancements
|
|
11214
11126
|
});
|
|
11215
|
-
var creativeContentSchema2 =
|
|
11127
|
+
var creativeContentSchema2 = z9.discriminatedUnion("creativeType", [
|
|
11216
11128
|
singleCreativeSchema,
|
|
11217
11129
|
carouselCreativeSchema2,
|
|
11218
11130
|
dynamicCreativeSchema,
|
|
11219
11131
|
existingPostCreativeSchema
|
|
11220
11132
|
]);
|
|
11221
11133
|
var adCreativeCreateSchema = creativeContentSchema2;
|
|
11222
|
-
var adCreativeUpdateSchema =
|
|
11223
|
-
name:
|
|
11134
|
+
var adCreativeUpdateSchema = z9.object({
|
|
11135
|
+
name: z9.string().max(META_LIMITS.creative.nameMax).optional(),
|
|
11224
11136
|
status: updateStatusSchema.optional(),
|
|
11225
11137
|
/** Content patch — only honored when the target is a staged meta_temp_* creative. */
|
|
11226
|
-
content:
|
|
11138
|
+
content: z9.record(z9.string(), z9.unknown()).optional()
|
|
11227
11139
|
}).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
|
|
11228
|
-
var adCreateSchema2 =
|
|
11229
|
-
name:
|
|
11140
|
+
var adCreateSchema2 = z9.object({
|
|
11141
|
+
name: z9.string().min(1).max(META_LIMITS.ad.nameMax),
|
|
11230
11142
|
adset_id: parentRefSchema2,
|
|
11231
11143
|
status: stageableStatusSchema3.default("PAUSED"),
|
|
11232
|
-
creative:
|
|
11144
|
+
creative: z9.object({ creative_id: parentRefSchema2 }),
|
|
11233
11145
|
/** Conversion pixel / offline event set / view tags — structural passthrough. */
|
|
11234
|
-
tracking_specs:
|
|
11146
|
+
tracking_specs: z9.array(z9.record(z9.string(), z9.unknown())).optional()
|
|
11235
11147
|
});
|
|
11236
|
-
var adUpdateSchema2 =
|
|
11237
|
-
name:
|
|
11148
|
+
var adUpdateSchema2 = z9.object({
|
|
11149
|
+
name: z9.string().min(1).max(META_LIMITS.ad.nameMax).optional(),
|
|
11238
11150
|
status: updateStatusSchema.optional(),
|
|
11239
11151
|
/** Swapping the creative is the Meta way to "edit" an ad's creative. */
|
|
11240
|
-
creative:
|
|
11241
|
-
tracking_specs:
|
|
11152
|
+
creative: z9.object({ creative_id: parentRefSchema2 }).optional(),
|
|
11153
|
+
tracking_specs: z9.array(z9.record(z9.string(), z9.unknown())).optional()
|
|
11242
11154
|
}).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
|
|
11243
|
-
var lookalikeSpecSchema =
|
|
11244
|
-
origin:
|
|
11245
|
-
ratio:
|
|
11246
|
-
country:
|
|
11247
|
-
});
|
|
11248
|
-
var customAudienceCreateSchema =
|
|
11249
|
-
name:
|
|
11250
|
-
subtype:
|
|
11251
|
-
description:
|
|
11252
|
-
customer_file_source:
|
|
11253
|
-
retention_days:
|
|
11155
|
+
var lookalikeSpecSchema = z9.object({
|
|
11156
|
+
origin: z9.array(z9.object({ id: parentRefSchema2 })).min(1),
|
|
11157
|
+
ratio: z9.number().min(0.01).max(0.2).optional(),
|
|
11158
|
+
country: z9.string().length(2).optional()
|
|
11159
|
+
});
|
|
11160
|
+
var customAudienceCreateSchema = z9.object({
|
|
11161
|
+
name: z9.string().min(1).max(META_LIMITS.audience.nameMax),
|
|
11162
|
+
subtype: z9.enum(CUSTOM_AUDIENCE_SUBTYPES),
|
|
11163
|
+
description: z9.string().max(500).optional(),
|
|
11164
|
+
customer_file_source: z9.string().optional(),
|
|
11165
|
+
retention_days: z9.number().int().min(1).max(META_LIMITS.audience.retentionDaysMax).optional(),
|
|
11254
11166
|
lookalike_spec: lookalikeSpecSchema.optional(),
|
|
11255
11167
|
/** Website/engagement rule — structural passthrough validated by Meta. */
|
|
11256
|
-
rule:
|
|
11168
|
+
rule: z9.record(z9.string(), z9.unknown()).optional()
|
|
11257
11169
|
}).superRefine((p, ctx) => {
|
|
11258
11170
|
if (p.subtype === "LOOKALIKE" && !p.lookalike_spec) {
|
|
11259
11171
|
ctx.addIssue({ code: "custom", path: ["lookalike_spec"], message: "LOOKALIKE audiences need a lookalike_spec" });
|
|
@@ -11262,16 +11174,16 @@ var customAudienceCreateSchema = z10.object({
|
|
|
11262
11174
|
ctx.addIssue({ code: "custom", path: ["rule"], message: `${p.subtype} audiences need a rule (use --file)` });
|
|
11263
11175
|
}
|
|
11264
11176
|
});
|
|
11265
|
-
var customAudienceUpdateSchema =
|
|
11266
|
-
name:
|
|
11267
|
-
description:
|
|
11177
|
+
var customAudienceUpdateSchema = z9.object({
|
|
11178
|
+
name: z9.string().min(1).max(META_LIMITS.audience.nameMax).optional(),
|
|
11179
|
+
description: z9.string().max(500).optional()
|
|
11268
11180
|
}).refine((p) => Object.values(p).some((val) => val !== void 0), "update needs at least one field");
|
|
11269
|
-
var mediaUploadSchema =
|
|
11270
|
-
kind:
|
|
11181
|
+
var mediaUploadSchema = z9.object({
|
|
11182
|
+
kind: z9.enum(MEDIA_KINDS),
|
|
11271
11183
|
bakerImageId: bakerMediaIdSchema2.optional(),
|
|
11272
11184
|
bakerVideoId: bakerMediaIdSchema2.optional(),
|
|
11273
11185
|
/** Optional display name / filename hint. */
|
|
11274
|
-
name:
|
|
11186
|
+
name: z9.string().max(255).optional()
|
|
11275
11187
|
}).superRefine((p, ctx) => {
|
|
11276
11188
|
if (p.kind === "image" && !p.bakerImageId) {
|
|
11277
11189
|
ctx.addIssue({ code: "custom", path: ["bakerImageId"], message: "image uploads need a bakerImageId" });
|
|
@@ -11293,16 +11205,16 @@ var META_DRAFT_OP_KINDS = [
|
|
|
11293
11205
|
"customAudience.update",
|
|
11294
11206
|
"media.upload"
|
|
11295
11207
|
];
|
|
11296
|
-
var metaDraftOpKindSchema =
|
|
11297
|
-
var accountIdSchema2 =
|
|
11298
|
-
var updateTargetSchema2 =
|
|
11208
|
+
var metaDraftOpKindSchema = z9.enum(META_DRAFT_OP_KINDS);
|
|
11209
|
+
var accountIdSchema2 = z9.string().regex(NUMERIC_ID_REGEX3, "accountId must be the bare numeric ad account id");
|
|
11210
|
+
var updateTargetSchema2 = z9.union([z9.string().regex(NUMERIC_ID_REGEX3), tempRefSchema3]);
|
|
11299
11211
|
function createOp3(kind, payload) {
|
|
11300
|
-
return
|
|
11212
|
+
return z9.object({ kind: z9.literal(kind), accountId: accountIdSchema2, payload });
|
|
11301
11213
|
}
|
|
11302
11214
|
function updateOp3(kind, payload) {
|
|
11303
|
-
return
|
|
11215
|
+
return z9.object({ kind: z9.literal(kind), accountId: accountIdSchema2, target: updateTargetSchema2, payload });
|
|
11304
11216
|
}
|
|
11305
|
-
var metaDraftOpInputSchema =
|
|
11217
|
+
var metaDraftOpInputSchema = z9.discriminatedUnion("kind", [
|
|
11306
11218
|
createOp3("campaign.create", campaignCreateSchema3),
|
|
11307
11219
|
updateOp3("campaign.update", campaignUpdateSchema3),
|
|
11308
11220
|
createOp3("adSet.create", adSetCreateSchema),
|
|
@@ -11317,89 +11229,89 @@ var metaDraftOpInputSchema = z10.discriminatedUnion("kind", [
|
|
|
11317
11229
|
]);
|
|
11318
11230
|
|
|
11319
11231
|
// ../api/src/ads-meta/wire.ts
|
|
11320
|
-
import { z as
|
|
11321
|
-
var metaWriteModeSchema =
|
|
11322
|
-
var metaDraftOpResultSchema =
|
|
11323
|
-
status:
|
|
11232
|
+
import { z as z10 } from "zod";
|
|
11233
|
+
var metaWriteModeSchema = z10.enum(["live", "simulated"]);
|
|
11234
|
+
var metaDraftOpResultSchema = z10.object({
|
|
11235
|
+
status: z10.enum(["applied", "simulated", "failed", "skipped"]),
|
|
11324
11236
|
/** The resulting Meta node id (campaign/adset/creative/ad/audience) or simulated id. */
|
|
11325
|
-
id:
|
|
11237
|
+
id: z10.string().optional(),
|
|
11326
11238
|
/** For media.upload ops: the resulting image hash. */
|
|
11327
|
-
hash:
|
|
11328
|
-
error:
|
|
11329
|
-
skippedBecause:
|
|
11330
|
-
executedAt:
|
|
11239
|
+
hash: z10.string().optional(),
|
|
11240
|
+
error: z10.string().optional(),
|
|
11241
|
+
skippedBecause: z10.string().optional(),
|
|
11242
|
+
executedAt: z10.number().optional()
|
|
11331
11243
|
});
|
|
11332
|
-
var metaDraftStageRequestSchema =
|
|
11333
|
-
chatId:
|
|
11244
|
+
var metaDraftStageRequestSchema = z10.object({
|
|
11245
|
+
chatId: z10.string(),
|
|
11334
11246
|
op: metaDraftOpInputSchema
|
|
11335
11247
|
});
|
|
11336
|
-
var metaDraftStageResponseSchema =
|
|
11337
|
-
staged:
|
|
11338
|
-
ref:
|
|
11248
|
+
var metaDraftStageResponseSchema = z10.object({
|
|
11249
|
+
staged: z10.literal(true),
|
|
11250
|
+
ref: z10.string(),
|
|
11339
11251
|
kind: metaDraftOpKindSchema,
|
|
11340
11252
|
mode: metaWriteModeSchema,
|
|
11341
|
-
dependsOn:
|
|
11342
|
-
summary:
|
|
11343
|
-
warnings:
|
|
11253
|
+
dependsOn: z10.array(z10.string()),
|
|
11254
|
+
summary: z10.string(),
|
|
11255
|
+
warnings: z10.array(z10.string()),
|
|
11344
11256
|
/** True when the op amended an already-staged op in place instead of appending a new one. */
|
|
11345
|
-
amended:
|
|
11346
|
-
});
|
|
11347
|
-
var metaDraftDuplicateRequestSchema =
|
|
11348
|
-
chatId:
|
|
11349
|
-
accountId:
|
|
11350
|
-
entity:
|
|
11351
|
-
sourceId:
|
|
11352
|
-
overrides:
|
|
11257
|
+
amended: z10.boolean().optional()
|
|
11258
|
+
});
|
|
11259
|
+
var metaDraftDuplicateRequestSchema = z10.object({
|
|
11260
|
+
chatId: z10.string(),
|
|
11261
|
+
accountId: z10.string(),
|
|
11262
|
+
entity: z10.enum(["campaign", "adSet", "ad"]),
|
|
11263
|
+
sourceId: z10.string(),
|
|
11264
|
+
overrides: z10.record(z10.string(), z10.unknown()).optional(),
|
|
11353
11265
|
/** Pause the original after the copy publishes. */
|
|
11354
|
-
replace:
|
|
11266
|
+
replace: z10.boolean().optional()
|
|
11355
11267
|
});
|
|
11356
|
-
var metaDraftOpViewSchema =
|
|
11357
|
-
ref:
|
|
11268
|
+
var metaDraftOpViewSchema = z10.object({
|
|
11269
|
+
ref: z10.string(),
|
|
11358
11270
|
kind: metaDraftOpKindSchema,
|
|
11359
|
-
accountId:
|
|
11360
|
-
target:
|
|
11361
|
-
dependsOn:
|
|
11362
|
-
summary:
|
|
11363
|
-
stagedAt:
|
|
11271
|
+
accountId: z10.string(),
|
|
11272
|
+
target: z10.string().optional(),
|
|
11273
|
+
dependsOn: z10.array(z10.string()),
|
|
11274
|
+
summary: z10.string(),
|
|
11275
|
+
stagedAt: z10.number(),
|
|
11364
11276
|
result: metaDraftOpResultSchema.optional()
|
|
11365
11277
|
});
|
|
11366
|
-
var metaDraftListRequestSchema =
|
|
11367
|
-
chatId:
|
|
11278
|
+
var metaDraftListRequestSchema = z10.object({
|
|
11279
|
+
chatId: z10.string()
|
|
11368
11280
|
});
|
|
11369
|
-
var metaDraftAdvisorySchema =
|
|
11370
|
-
ref:
|
|
11371
|
-
message:
|
|
11281
|
+
var metaDraftAdvisorySchema = z10.object({
|
|
11282
|
+
ref: z10.string(),
|
|
11283
|
+
message: z10.string()
|
|
11372
11284
|
});
|
|
11373
|
-
var metaDraftListResponseSchema =
|
|
11374
|
-
status:
|
|
11285
|
+
var metaDraftListResponseSchema = z10.object({
|
|
11286
|
+
status: z10.enum(["active", "publishing", "applied", "discarded", "none"]),
|
|
11375
11287
|
mode: metaWriteModeSchema,
|
|
11376
|
-
count:
|
|
11377
|
-
ops:
|
|
11288
|
+
count: z10.number(),
|
|
11289
|
+
ops: z10.array(metaDraftOpViewSchema),
|
|
11378
11290
|
/** Non-blocking cross-op quality advisories — "good campaign, not just valid". */
|
|
11379
|
-
advisories:
|
|
11291
|
+
advisories: z10.array(metaDraftAdvisorySchema)
|
|
11380
11292
|
});
|
|
11381
|
-
var metaDraftRemoveRequestSchema =
|
|
11382
|
-
chatId:
|
|
11383
|
-
ref:
|
|
11293
|
+
var metaDraftRemoveRequestSchema = z10.object({
|
|
11294
|
+
chatId: z10.string(),
|
|
11295
|
+
ref: z10.string()
|
|
11384
11296
|
});
|
|
11385
|
-
var metaDraftRemoveResponseSchema =
|
|
11297
|
+
var metaDraftRemoveResponseSchema = z10.object({
|
|
11386
11298
|
/** The requested ref plus any dependents removed by cascade. */
|
|
11387
|
-
removed:
|
|
11299
|
+
removed: z10.array(z10.string())
|
|
11388
11300
|
});
|
|
11389
|
-
var metaDraftClearRequestSchema =
|
|
11390
|
-
chatId:
|
|
11301
|
+
var metaDraftClearRequestSchema = z10.object({
|
|
11302
|
+
chatId: z10.string()
|
|
11391
11303
|
});
|
|
11392
|
-
var metaDraftClearResponseSchema =
|
|
11393
|
-
cleared:
|
|
11304
|
+
var metaDraftClearResponseSchema = z10.object({
|
|
11305
|
+
cleared: z10.number()
|
|
11394
11306
|
});
|
|
11395
|
-
var metaFieldErrorSchema =
|
|
11396
|
-
path:
|
|
11397
|
-
message:
|
|
11307
|
+
var metaFieldErrorSchema = z10.object({
|
|
11308
|
+
path: z10.string(),
|
|
11309
|
+
message: z10.string()
|
|
11398
11310
|
});
|
|
11399
|
-
var metaDraftErrorResponseSchema =
|
|
11400
|
-
code:
|
|
11401
|
-
error:
|
|
11402
|
-
fields:
|
|
11311
|
+
var metaDraftErrorResponseSchema = z10.object({
|
|
11312
|
+
code: z10.string(),
|
|
11313
|
+
error: z10.string(),
|
|
11314
|
+
fields: z10.array(metaFieldErrorSchema).optional()
|
|
11403
11315
|
});
|
|
11404
11316
|
|
|
11405
11317
|
// src/commands/ads/meta/write-shared.ts
|
|
@@ -11410,19 +11322,19 @@ function failWriteValidation3(message) {
|
|
|
11410
11322
|
writeJsonEnvelope({ ok: false, error: { code: "VALIDATION_ERROR", message } });
|
|
11411
11323
|
process.exit(1);
|
|
11412
11324
|
}
|
|
11413
|
-
function loadJsonFileArg3(
|
|
11414
|
-
if (typeof
|
|
11325
|
+
function loadJsonFileArg3(path15) {
|
|
11326
|
+
if (typeof path15 !== "string" || path15.length === 0) {
|
|
11415
11327
|
return {};
|
|
11416
11328
|
}
|
|
11417
11329
|
try {
|
|
11418
|
-
const parsed = JSON.parse(readFileSync8(
|
|
11330
|
+
const parsed = JSON.parse(readFileSync8(path15, "utf8"));
|
|
11419
11331
|
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) {
|
|
11420
|
-
failWriteValidation3(`${
|
|
11332
|
+
failWriteValidation3(`${path15} must contain a JSON object`);
|
|
11421
11333
|
}
|
|
11422
11334
|
return parsed;
|
|
11423
11335
|
} catch (err) {
|
|
11424
11336
|
if (err instanceof SyntaxError) {
|
|
11425
|
-
failWriteValidation3(`${
|
|
11337
|
+
failWriteValidation3(`${path15} is not valid JSON: ${err.message}`);
|
|
11426
11338
|
}
|
|
11427
11339
|
throw err;
|
|
11428
11340
|
}
|
|
@@ -14484,8 +14396,8 @@ async function probeDuration(filePath) {
|
|
|
14484
14396
|
}
|
|
14485
14397
|
|
|
14486
14398
|
// src/commands/canvas/run.ts
|
|
14487
|
-
import { readFile as
|
|
14488
|
-
import
|
|
14399
|
+
import { readFile as readFile3 } from "fs/promises";
|
|
14400
|
+
import path6 from "path";
|
|
14489
14401
|
import { defineCommand as defineCommand88 } from "citty";
|
|
14490
14402
|
|
|
14491
14403
|
// src/commands/canvas/placeholders.ts
|
|
@@ -14529,9 +14441,290 @@ function isResolvableRelative(value) {
|
|
|
14529
14441
|
return typeof value === "string" && value.length > 0 && !value.includes("[TODO") && !path2.isAbsolute(value);
|
|
14530
14442
|
}
|
|
14531
14443
|
|
|
14444
|
+
// src/commands/canvas/run-record.ts
|
|
14445
|
+
import path3 from "path";
|
|
14446
|
+
var MAX_RUN_NODES = 200;
|
|
14447
|
+
var MAX_OUTPUTS_PER_NODE = 10;
|
|
14448
|
+
var MAX_FINAL_OUTPUTS = 10;
|
|
14449
|
+
var MAX_PARAMS_PREVIEW_LENGTH = 4e3;
|
|
14450
|
+
function paramsPreviewFromParams(params) {
|
|
14451
|
+
if (params === void 0 || params === null) return void 0;
|
|
14452
|
+
const text = humanParamText(params, "prompt") ?? humanParamText(params, "source") ?? compactJson(params);
|
|
14453
|
+
if (!text) return void 0;
|
|
14454
|
+
return text.length > MAX_PARAMS_PREVIEW_LENGTH ? `${text.slice(0, MAX_PARAMS_PREVIEW_LENGTH - 1)}\u2026` : text;
|
|
14455
|
+
}
|
|
14456
|
+
function humanParamText(params, key) {
|
|
14457
|
+
const value = params[key];
|
|
14458
|
+
if (typeof value !== "string") return void 0;
|
|
14459
|
+
const trimmed = value.trim();
|
|
14460
|
+
return trimmed && !trimmed.startsWith("$ref:") ? trimmed : void 0;
|
|
14461
|
+
}
|
|
14462
|
+
function compactJson(params) {
|
|
14463
|
+
try {
|
|
14464
|
+
const json = JSON.stringify(params);
|
|
14465
|
+
return json && json !== "{}" ? json : void 0;
|
|
14466
|
+
} catch {
|
|
14467
|
+
return void 0;
|
|
14468
|
+
}
|
|
14469
|
+
}
|
|
14470
|
+
var MAX_CREATIVE_SLUG_LENGTH = 100;
|
|
14471
|
+
function creativeSlugFromCanvasPath(filePath) {
|
|
14472
|
+
const normalized = filePath.split(path3.sep).join("/");
|
|
14473
|
+
const match = normalized.match(/(?:^|\/)src\/creatives\/([a-z0-9](?:[a-z0-9-]*[a-z0-9])?)\//);
|
|
14474
|
+
const slug = match?.[1] ?? null;
|
|
14475
|
+
return slug && slug.length <= MAX_CREATIVE_SLUG_LENGTH ? slug : null;
|
|
14476
|
+
}
|
|
14477
|
+
var OUTPUT_KINDS = /* @__PURE__ */ new Set(["image", "video", "audio", "json", "text", "font"]);
|
|
14478
|
+
function toRecordOutput(slot, value) {
|
|
14479
|
+
const refs = collectAssetRefLikes(value);
|
|
14480
|
+
const ref = refs.length === 1 ? refs[0] : null;
|
|
14481
|
+
if (!ref || !isPersistedAssetRef(ref)) return null;
|
|
14482
|
+
const kind = typeof ref.kind === "string" && OUTPUT_KINDS.has(ref.kind) ? ref.kind : null;
|
|
14483
|
+
if (!kind) return null;
|
|
14484
|
+
return {
|
|
14485
|
+
slot,
|
|
14486
|
+
kind,
|
|
14487
|
+
sha256: ref.sha256,
|
|
14488
|
+
url: ref.url,
|
|
14489
|
+
mime: ref.mime,
|
|
14490
|
+
width: typeof ref.width === "number" ? ref.width : void 0,
|
|
14491
|
+
height: typeof ref.height === "number" ? ref.height : void 0,
|
|
14492
|
+
durationMs: typeof ref.duration_ms === "number" ? ref.duration_ms : void 0
|
|
14493
|
+
};
|
|
14494
|
+
}
|
|
14495
|
+
function nodeOutputsToRecord(nodeOutputs) {
|
|
14496
|
+
const out = [];
|
|
14497
|
+
for (const [slot, value] of Object.entries(nodeOutputs)) {
|
|
14498
|
+
if (Array.isArray(value)) {
|
|
14499
|
+
value.forEach((item, i) => {
|
|
14500
|
+
const rec = toRecordOutput(`${slot}#${i}`, item);
|
|
14501
|
+
if (rec) out.push(rec);
|
|
14502
|
+
});
|
|
14503
|
+
} else {
|
|
14504
|
+
const rec = toRecordOutput(slot, value);
|
|
14505
|
+
if (rec) out.push(rec);
|
|
14506
|
+
}
|
|
14507
|
+
}
|
|
14508
|
+
return out.slice(0, MAX_OUTPUTS_PER_NODE);
|
|
14509
|
+
}
|
|
14510
|
+
function finalOutputsToRecord(output) {
|
|
14511
|
+
if (Array.isArray(output)) {
|
|
14512
|
+
return output.map((item, i) => toRecordOutput(`final#${i}`, item)).filter((rec2) => rec2 !== null).slice(0, MAX_FINAL_OUTPUTS);
|
|
14513
|
+
}
|
|
14514
|
+
const rec = toRecordOutput("final", output);
|
|
14515
|
+
return rec ? [rec] : [];
|
|
14516
|
+
}
|
|
14517
|
+
function buildRunRecord(result, meta, plan) {
|
|
14518
|
+
const nodes = result.node_runs.slice(0, MAX_RUN_NODES).map((run) => {
|
|
14519
|
+
const planned = plan?.get(run.node_id);
|
|
14520
|
+
return {
|
|
14521
|
+
nodeId: run.node_id,
|
|
14522
|
+
nodeType: run.node_type,
|
|
14523
|
+
cached: run.cached,
|
|
14524
|
+
credits: run.credits,
|
|
14525
|
+
durationMs: run.duration_ms,
|
|
14526
|
+
outputs: nodeOutputsToRecord(result.outputs_by_node[run.node_id] ?? {}),
|
|
14527
|
+
deps: planned?.deps,
|
|
14528
|
+
status: plan ? "completed" : void 0,
|
|
14529
|
+
paramsPreview: planned?.paramsPreview
|
|
14530
|
+
};
|
|
14531
|
+
});
|
|
14532
|
+
const finalOutputs = finalOutputsToRecord(result.output);
|
|
14533
|
+
return {
|
|
14534
|
+
runId: result.run_id,
|
|
14535
|
+
creativeSlug: meta.creativeSlug,
|
|
14536
|
+
canvasPath: meta.canvasPath,
|
|
14537
|
+
canvasSha: meta.canvasSha,
|
|
14538
|
+
chatId: meta.chatId,
|
|
14539
|
+
status: "completed",
|
|
14540
|
+
stats: {
|
|
14541
|
+
totalNodes: result.stats.total_nodes,
|
|
14542
|
+
cachedNodes: result.stats.cached_nodes,
|
|
14543
|
+
totalCredits: result.stats.total_credits,
|
|
14544
|
+
durationMs: result.stats.duration_ms
|
|
14545
|
+
},
|
|
14546
|
+
nodes,
|
|
14547
|
+
finalOutputs: finalOutputs.length > 0 ? finalOutputs : void 0
|
|
14548
|
+
};
|
|
14549
|
+
}
|
|
14550
|
+
function buildFailedRunRecord(runId, errorMessage, meta) {
|
|
14551
|
+
return {
|
|
14552
|
+
runId,
|
|
14553
|
+
creativeSlug: meta.creativeSlug,
|
|
14554
|
+
canvasPath: meta.canvasPath,
|
|
14555
|
+
canvasSha: meta.canvasSha,
|
|
14556
|
+
chatId: meta.chatId,
|
|
14557
|
+
status: "failed",
|
|
14558
|
+
errorMessage: errorMessage.slice(0, 2e3),
|
|
14559
|
+
stats: { totalNodes: 0, cachedNodes: 0, totalCredits: 0, durationMs: 0 },
|
|
14560
|
+
nodes: []
|
|
14561
|
+
};
|
|
14562
|
+
}
|
|
14563
|
+
|
|
14564
|
+
// src/commands/canvas/run-progress.ts
|
|
14565
|
+
var RunProgressTracker = class {
|
|
14566
|
+
runId;
|
|
14567
|
+
meta;
|
|
14568
|
+
startedAt;
|
|
14569
|
+
nodes = /* @__PURE__ */ new Map();
|
|
14570
|
+
planned = false;
|
|
14571
|
+
constructor(runId, meta) {
|
|
14572
|
+
this.runId = runId;
|
|
14573
|
+
this.meta = meta;
|
|
14574
|
+
this.startedAt = Date.now();
|
|
14575
|
+
}
|
|
14576
|
+
apply(event) {
|
|
14577
|
+
if (event.kind === "plan") {
|
|
14578
|
+
for (const node of event.nodes) {
|
|
14579
|
+
this.nodes.set(node.node_id, {
|
|
14580
|
+
nodeId: node.node_id,
|
|
14581
|
+
nodeType: node.node_type,
|
|
14582
|
+
cached: false,
|
|
14583
|
+
credits: 0,
|
|
14584
|
+
durationMs: 0,
|
|
14585
|
+
outputs: [],
|
|
14586
|
+
deps: node.deps,
|
|
14587
|
+
status: "pending",
|
|
14588
|
+
paramsPreview: paramsPreviewFromParams(node.params)
|
|
14589
|
+
});
|
|
14590
|
+
}
|
|
14591
|
+
this.planned = true;
|
|
14592
|
+
return;
|
|
14593
|
+
}
|
|
14594
|
+
if (event.kind === "node_start") {
|
|
14595
|
+
this.patchNode(event.node_id, { status: "running" });
|
|
14596
|
+
return;
|
|
14597
|
+
}
|
|
14598
|
+
if (event.kind === "node_settled") {
|
|
14599
|
+
this.patchNode(event.run.node_id, {
|
|
14600
|
+
status: "completed",
|
|
14601
|
+
cached: event.run.cached,
|
|
14602
|
+
credits: event.run.credits,
|
|
14603
|
+
durationMs: event.run.duration_ms,
|
|
14604
|
+
outputs: nodeOutputsToRecord(event.outputs)
|
|
14605
|
+
});
|
|
14606
|
+
return;
|
|
14607
|
+
}
|
|
14608
|
+
this.patchNode(event.node_id, { status: "failed" });
|
|
14609
|
+
}
|
|
14610
|
+
/** True once the plan event landed — before that there is nothing worth posting. */
|
|
14611
|
+
hasPlan() {
|
|
14612
|
+
return this.planned;
|
|
14613
|
+
}
|
|
14614
|
+
/** Plan facts (deps + params preview) for stamping the terminal record's nodes. */
|
|
14615
|
+
planInfo() {
|
|
14616
|
+
const info = /* @__PURE__ */ new Map();
|
|
14617
|
+
for (const node of this.nodes.values()) {
|
|
14618
|
+
info.set(node.nodeId, { deps: node.deps ?? [], paramsPreview: node.paramsPreview });
|
|
14619
|
+
}
|
|
14620
|
+
return info;
|
|
14621
|
+
}
|
|
14622
|
+
/** The current in-flight state as a postable full record. */
|
|
14623
|
+
snapshot() {
|
|
14624
|
+
const nodes = [...this.nodes.values()];
|
|
14625
|
+
return {
|
|
14626
|
+
runId: this.runId,
|
|
14627
|
+
...this.meta,
|
|
14628
|
+
status: "running",
|
|
14629
|
+
stats: {
|
|
14630
|
+
totalNodes: nodes.length,
|
|
14631
|
+
cachedNodes: nodes.filter((n) => n.cached).length,
|
|
14632
|
+
totalCredits: nodes.reduce((sum, n) => sum + n.credits, 0),
|
|
14633
|
+
durationMs: Date.now() - this.startedAt
|
|
14634
|
+
},
|
|
14635
|
+
nodes
|
|
14636
|
+
};
|
|
14637
|
+
}
|
|
14638
|
+
/**
|
|
14639
|
+
* Terminal record for a failed run, preserving what each node got to —
|
|
14640
|
+
* completed nodes keep their outputs so the graph shows exactly where the
|
|
14641
|
+
* run died instead of an empty husk.
|
|
14642
|
+
*/
|
|
14643
|
+
failedSnapshot(errorMessage) {
|
|
14644
|
+
const snapshot = this.snapshot();
|
|
14645
|
+
return {
|
|
14646
|
+
...snapshot,
|
|
14647
|
+
status: "failed",
|
|
14648
|
+
errorMessage: errorMessage.slice(0, 2e3),
|
|
14649
|
+
stats: { ...snapshot.stats, durationMs: Date.now() - this.startedAt }
|
|
14650
|
+
};
|
|
14651
|
+
}
|
|
14652
|
+
patchNode(nodeId, patch) {
|
|
14653
|
+
const existing = this.nodes.get(nodeId);
|
|
14654
|
+
if (!existing) return;
|
|
14655
|
+
this.nodes.set(nodeId, { ...existing, ...patch, status: patch.status ?? existing.status });
|
|
14656
|
+
}
|
|
14657
|
+
};
|
|
14658
|
+
var RunRecordPoster = class {
|
|
14659
|
+
post;
|
|
14660
|
+
latest = null;
|
|
14661
|
+
inflight = null;
|
|
14662
|
+
warned = false;
|
|
14663
|
+
keepaliveTimer = null;
|
|
14664
|
+
constructor(post) {
|
|
14665
|
+
this.post = post;
|
|
14666
|
+
}
|
|
14667
|
+
/** Queue a progress snapshot; returns immediately. */
|
|
14668
|
+
enqueue(payload) {
|
|
14669
|
+
this.latest = payload;
|
|
14670
|
+
if (!this.inflight) this.inflight = this.pump();
|
|
14671
|
+
}
|
|
14672
|
+
/**
|
|
14673
|
+
* Re-post the latest snapshot on an interval even with no new node events, so
|
|
14674
|
+
* the backend's `canvasRuns.updatedAt` heartbeat stays fresh during a long
|
|
14675
|
+
* single-clip poll (a video_generate clip can run minutes with no
|
|
14676
|
+
* intervening node events). When this process dies the keepalive stops → the
|
|
14677
|
+
* run's `updatedAt` goes stale → the backend reconciliation sweep force-fails
|
|
14678
|
+
* it as interrupted and surfaces the clips that finished. `produce` returns
|
|
14679
|
+
* null before the plan lands (nothing worth posting yet).
|
|
14680
|
+
*/
|
|
14681
|
+
startKeepalive(produce, intervalMs = 6e4) {
|
|
14682
|
+
if (this.keepaliveTimer) return;
|
|
14683
|
+
this.keepaliveTimer = setInterval(() => {
|
|
14684
|
+
const snapshot = produce();
|
|
14685
|
+
if (snapshot) this.enqueue(snapshot);
|
|
14686
|
+
}, intervalMs);
|
|
14687
|
+
this.keepaliveTimer.unref?.();
|
|
14688
|
+
}
|
|
14689
|
+
stopKeepalive() {
|
|
14690
|
+
if (this.keepaliveTimer) {
|
|
14691
|
+
clearInterval(this.keepaliveTimer);
|
|
14692
|
+
this.keepaliveTimer = null;
|
|
14693
|
+
}
|
|
14694
|
+
}
|
|
14695
|
+
/**
|
|
14696
|
+
* Post the terminal record (awaited, errors surfaced to the caller). Any
|
|
14697
|
+
* queued progress snapshot is superseded — the terminal record is the full
|
|
14698
|
+
* state — but an in-flight POST is awaited first so it can't land after.
|
|
14699
|
+
*/
|
|
14700
|
+
async flush(terminal) {
|
|
14701
|
+
this.stopKeepalive();
|
|
14702
|
+
this.latest = null;
|
|
14703
|
+
if (this.inflight) await this.inflight;
|
|
14704
|
+
await this.post(terminal);
|
|
14705
|
+
}
|
|
14706
|
+
async pump() {
|
|
14707
|
+
while (this.latest) {
|
|
14708
|
+
const payload = this.latest;
|
|
14709
|
+
this.latest = null;
|
|
14710
|
+
try {
|
|
14711
|
+
await this.post(payload);
|
|
14712
|
+
} catch (e) {
|
|
14713
|
+
if (!this.warned) {
|
|
14714
|
+
this.warned = true;
|
|
14715
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
14716
|
+
process.stderr.write(`[warn] live run progress not streaming (${msg})
|
|
14717
|
+
`);
|
|
14718
|
+
}
|
|
14719
|
+
}
|
|
14720
|
+
}
|
|
14721
|
+
this.inflight = null;
|
|
14722
|
+
}
|
|
14723
|
+
};
|
|
14724
|
+
|
|
14532
14725
|
// src/commands/canvas/run-retention.ts
|
|
14533
14726
|
import { rm } from "fs/promises";
|
|
14534
|
-
import
|
|
14727
|
+
import path4 from "path";
|
|
14535
14728
|
function runDirsToPrune(entries, keep, currentRunId) {
|
|
14536
14729
|
const runs = entries.filter((e) => /^r_[0-9A-Za-z]+$/.test(e) && e !== currentRunId).sort();
|
|
14537
14730
|
if (keep <= 0) return runs;
|
|
@@ -14548,13 +14741,52 @@ async function pruneOldRuns(outputsDir, keep, currentRunId, log) {
|
|
|
14548
14741
|
const toPrune = runDirsToPrune(entries, keep, currentRunId);
|
|
14549
14742
|
if (toPrune.length === 0) return;
|
|
14550
14743
|
for (const dir of toPrune) {
|
|
14551
|
-
await rm(
|
|
14744
|
+
await rm(path4.join(outputsDir, dir), { recursive: true, force: true }).catch(
|
|
14552
14745
|
(e) => log(`[prune ] could not remove ${dir}: ${e.message}`)
|
|
14553
14746
|
);
|
|
14554
14747
|
}
|
|
14555
14748
|
log(`[prune ] removed ${toPrune.length} old run dir(s), kept the ${keep} newest`);
|
|
14556
14749
|
}
|
|
14557
14750
|
|
|
14751
|
+
// src/commands/canvas/run-resume.ts
|
|
14752
|
+
import { mkdir, readFile as readFile2, rm as rm2, writeFile } from "fs/promises";
|
|
14753
|
+
import path5 from "path";
|
|
14754
|
+
function markerPath(outputsDir, canvasPath) {
|
|
14755
|
+
const key = sha256Hex(Buffer.from(path5.resolve(canvasPath))).slice(0, 32);
|
|
14756
|
+
return path5.join(outputsDir, ".inflight", `${key}.json`);
|
|
14757
|
+
}
|
|
14758
|
+
async function resolveRunId(opts) {
|
|
14759
|
+
if (opts.explicitRunId) return { runId: opts.explicitRunId, resumed: false };
|
|
14760
|
+
if (!opts.fresh) {
|
|
14761
|
+
const existing = await readMarkerRunId(opts.outputsDir, opts.canvasPath);
|
|
14762
|
+
if (existing) return { runId: existing, resumed: true };
|
|
14763
|
+
}
|
|
14764
|
+
return { runId: `r_${ulid()}`, resumed: false };
|
|
14765
|
+
}
|
|
14766
|
+
async function readMarkerRunId(outputsDir, canvasPath) {
|
|
14767
|
+
try {
|
|
14768
|
+
const raw = await readFile2(markerPath(outputsDir, canvasPath), "utf8");
|
|
14769
|
+
const parsed = JSON.parse(raw);
|
|
14770
|
+
return typeof parsed.runId === "string" && parsed.runId.length > 0 ? parsed.runId : null;
|
|
14771
|
+
} catch {
|
|
14772
|
+
return null;
|
|
14773
|
+
}
|
|
14774
|
+
}
|
|
14775
|
+
async function markRunInFlight(outputsDir, canvasPath, runId) {
|
|
14776
|
+
try {
|
|
14777
|
+
const file = markerPath(outputsDir, canvasPath);
|
|
14778
|
+
await mkdir(path5.dirname(file), { recursive: true });
|
|
14779
|
+
await writeFile(file, JSON.stringify({ runId, canvasPath: path5.resolve(canvasPath), startedAt: Date.now() }));
|
|
14780
|
+
} catch {
|
|
14781
|
+
}
|
|
14782
|
+
}
|
|
14783
|
+
async function clearRunMarker(outputsDir, canvasPath) {
|
|
14784
|
+
try {
|
|
14785
|
+
await rm2(markerPath(outputsDir, canvasPath), { force: true });
|
|
14786
|
+
} catch {
|
|
14787
|
+
}
|
|
14788
|
+
}
|
|
14789
|
+
|
|
14558
14790
|
// src/commands/canvas/run.ts
|
|
14559
14791
|
var runCommand = defineCommand88({
|
|
14560
14792
|
meta: { name: "run", description: "Validate and execute a canvas JSON file." },
|
|
@@ -14562,8 +14794,17 @@ var runCommand = defineCommand88({
|
|
|
14562
14794
|
file: { type: "positional", required: true, description: "Path to canvas JSON" },
|
|
14563
14795
|
"cache-dir": { type: "string", description: "Cache root (default ./canvas/.cache)" },
|
|
14564
14796
|
"outputs-dir": { type: "string", description: "Per-run outputs root (default ./canvas)" },
|
|
14565
|
-
"run-id": { type: "string", description: "Override run id" },
|
|
14797
|
+
"run-id": { type: "string", description: "Override run id (also resumes that run, re-attaching its in-flight jobs)" },
|
|
14798
|
+
fresh: {
|
|
14799
|
+
type: "boolean",
|
|
14800
|
+
default: false,
|
|
14801
|
+
description: "Ignore any interrupted-run marker and start a new run id instead of resuming"
|
|
14802
|
+
},
|
|
14566
14803
|
"cache-policy": { type: "string", description: "read_write | bypass | read_only" },
|
|
14804
|
+
regenerate: {
|
|
14805
|
+
type: "string",
|
|
14806
|
+
description: "Comma-separated node ids to force fresh THIS run (e.g. --regenerate gen_4x5,gen_9x16), bypassing the content cache for just those nodes + everything downstream. For a persistent re-render, bump a node's `regenerate` field in the canvas JSON instead."
|
|
14807
|
+
},
|
|
14567
14808
|
concurrency: {
|
|
14568
14809
|
type: "string",
|
|
14569
14810
|
description: "Max nodes per layer in flight at once (default 5; env BAKER_CANVAS_CONCURRENCY)"
|
|
@@ -14575,11 +14816,23 @@ var runCommand = defineCommand88({
|
|
|
14575
14816
|
"keep-runs": {
|
|
14576
14817
|
type: "string",
|
|
14577
14818
|
description: "After the run, prune old r_* run dirs, keeping the N newest (off by default)"
|
|
14819
|
+
},
|
|
14820
|
+
"remote-cache": {
|
|
14821
|
+
type: "string",
|
|
14822
|
+
description: "on | off \u2014 company-scoped remote cache + durable asset persistence (default on; env BAKER_CANVAS_REMOTE_CACHE)"
|
|
14823
|
+
},
|
|
14824
|
+
// citty consumes any `--no-<flag>` as a negation of `<flag>`, so the
|
|
14825
|
+
// opt-out spelling `--no-record` requires the flag to be named `record`
|
|
14826
|
+
// (a literal "no-record" arg would never receive a value).
|
|
14827
|
+
record: {
|
|
14828
|
+
type: "boolean",
|
|
14829
|
+
default: true,
|
|
14830
|
+
description: "Post the durable run-history record to Baker (disable with --no-record)"
|
|
14578
14831
|
}
|
|
14579
14832
|
},
|
|
14580
14833
|
async run({ args }) {
|
|
14581
|
-
const filePath =
|
|
14582
|
-
const raw = await
|
|
14834
|
+
const filePath = path6.resolve(String(args.file));
|
|
14835
|
+
const raw = await readFile3(filePath, "utf8");
|
|
14583
14836
|
let parsed;
|
|
14584
14837
|
try {
|
|
14585
14838
|
parsed = JSON.parse(raw);
|
|
@@ -14589,7 +14842,7 @@ var runCommand = defineCommand88({
|
|
|
14589
14842
|
`);
|
|
14590
14843
|
process.exit(2);
|
|
14591
14844
|
}
|
|
14592
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
14845
|
+
parsed = resolveRelativeCanvasPaths(parsed, path6.dirname(filePath));
|
|
14593
14846
|
const pending = unsuppliedPlaceholderAssets(parsed);
|
|
14594
14847
|
if (pending.length > 0) {
|
|
14595
14848
|
process.stderr.write(
|
|
@@ -14609,26 +14862,86 @@ var runCommand = defineCommand88({
|
|
|
14609
14862
|
);
|
|
14610
14863
|
process.exit(2);
|
|
14611
14864
|
}
|
|
14865
|
+
let regenerate;
|
|
14866
|
+
if (args.regenerate !== void 0) {
|
|
14867
|
+
const requested = String(args.regenerate).split(",").map((id) => id.trim()).filter((id) => id.length > 0);
|
|
14868
|
+
const known = new Set(canvasNodeIds(parsed));
|
|
14869
|
+
const unknown = requested.filter((id) => !known.has(id));
|
|
14870
|
+
if (unknown.length > 0) {
|
|
14871
|
+
process.stderr.write(
|
|
14872
|
+
`${JSON.stringify(
|
|
14873
|
+
{
|
|
14874
|
+
ok: false,
|
|
14875
|
+
error: {
|
|
14876
|
+
code: "unknown_regenerate_node",
|
|
14877
|
+
message: `--regenerate names node id(s) not in this canvas: ${unknown.join(", ")}. Known ids: ${[...known].join(", ")}`
|
|
14878
|
+
}
|
|
14879
|
+
},
|
|
14880
|
+
null,
|
|
14881
|
+
2
|
|
14882
|
+
)}
|
|
14883
|
+
`
|
|
14884
|
+
);
|
|
14885
|
+
process.exit(2);
|
|
14886
|
+
}
|
|
14887
|
+
if (requested.length > 0) {
|
|
14888
|
+
regenerate = new Set(requested);
|
|
14889
|
+
process.stdout.write(`[regenerate] forcing fresh this run: ${[...regenerate].join(", ")} (+ downstream)
|
|
14890
|
+
`);
|
|
14891
|
+
}
|
|
14892
|
+
}
|
|
14893
|
+
const remoteCache = args["remote-cache"] !== void 0 ? String(args["remote-cache"]) !== "off" : void 0;
|
|
14612
14894
|
const engine = createEngineFromEnv({
|
|
14613
14895
|
cacheDir: args["cache-dir"] ? String(args["cache-dir"]) : void 0,
|
|
14614
14896
|
outputsDir: args["outputs-dir"] ? String(args["outputs-dir"]) : void 0,
|
|
14615
14897
|
log: (line) => process.stdout.write(`${line}
|
|
14616
|
-
`)
|
|
14898
|
+
`),
|
|
14899
|
+
remoteCache
|
|
14900
|
+
});
|
|
14901
|
+
const outputsDir = args["outputs-dir"] ? path6.resolve(String(args["outputs-dir"])) : path6.resolve("canvas");
|
|
14902
|
+
const { runId, resumed } = await resolveRunId({
|
|
14903
|
+
explicitRunId: args["run-id"] ? String(args["run-id"]) : void 0,
|
|
14904
|
+
fresh: args.fresh === true,
|
|
14905
|
+
outputsDir,
|
|
14906
|
+
canvasPath: filePath
|
|
14617
14907
|
});
|
|
14908
|
+
if (resumed) {
|
|
14909
|
+
process.stdout.write(`[resume] continuing interrupted run ${runId} \u2014 in-flight jobs re-attach, cached nodes skip
|
|
14910
|
+
`);
|
|
14911
|
+
}
|
|
14912
|
+
await markRunInFlight(outputsDir, filePath, runId);
|
|
14913
|
+
const recordMeta = {
|
|
14914
|
+
creativeSlug: creativeSlugFromCanvasPath(filePath) ?? void 0,
|
|
14915
|
+
canvasPath: path6.relative(process.cwd(), filePath) || void 0,
|
|
14916
|
+
canvasSha: sha256Hex(Buffer.from(raw)),
|
|
14917
|
+
chatId: getEnv().BAKER_CHAT_ID || void 0
|
|
14918
|
+
};
|
|
14919
|
+
const record = args.record === false ? null : buildRecorder();
|
|
14920
|
+
const progress = record ? new RunProgressTracker(runId, recordMeta) : null;
|
|
14921
|
+
const poster = record ? new RunRecordPoster(record) : null;
|
|
14922
|
+
if (progress && poster) {
|
|
14923
|
+
poster.startKeepalive(() => progress.hasPlan() ? progress.snapshot() : null);
|
|
14924
|
+
}
|
|
14618
14925
|
try {
|
|
14619
14926
|
const policy = args["cache-policy"] ?? "read_write";
|
|
14620
14927
|
const result = await engine.run(parsed, {
|
|
14621
|
-
run_id:
|
|
14928
|
+
run_id: runId,
|
|
14622
14929
|
cache_policy: policy,
|
|
14623
14930
|
concurrency: resolveConcurrency(
|
|
14624
14931
|
// --concurrency wins; --parallel is the discoverable alias for the same bound.
|
|
14625
14932
|
(args.concurrency ?? args.parallel) !== void 0 ? String(args.concurrency ?? args.parallel) : void 0,
|
|
14626
14933
|
process.env.BAKER_CANVAS_CONCURRENCY
|
|
14627
|
-
)
|
|
14934
|
+
),
|
|
14935
|
+
regenerate,
|
|
14936
|
+
onProgress: progress && poster ? (event) => {
|
|
14937
|
+
progress.apply(event);
|
|
14938
|
+
if (progress.hasPlan()) poster.enqueue(progress.snapshot());
|
|
14939
|
+
} : void 0
|
|
14628
14940
|
});
|
|
14941
|
+
await clearRunMarker(outputsDir, filePath);
|
|
14942
|
+
if (poster) await poster.flush(buildRunRecord(result, recordMeta, progress?.planInfo()));
|
|
14629
14943
|
const keepRuns = args["keep-runs"] !== void 0 ? Number(args["keep-runs"]) : void 0;
|
|
14630
14944
|
if (keepRuns !== void 0 && Number.isFinite(keepRuns)) {
|
|
14631
|
-
const outputsDir = args["outputs-dir"] ? path4.resolve(String(args["outputs-dir"])) : path4.resolve("canvas");
|
|
14632
14945
|
await pruneOldRuns(outputsDir, keepRuns, result.run_id, (line) => process.stdout.write(`${line}
|
|
14633
14946
|
`));
|
|
14634
14947
|
}
|
|
@@ -14647,6 +14960,7 @@ var runCommand = defineCommand88({
|
|
|
14647
14960
|
`
|
|
14648
14961
|
);
|
|
14649
14962
|
} catch (e) {
|
|
14963
|
+
await clearRunMarker(outputsDir, filePath);
|
|
14650
14964
|
if (e instanceof ValidationError) {
|
|
14651
14965
|
process.stderr.write(
|
|
14652
14966
|
`${JSON.stringify({ ok: false, error: { code: "validation", issues: e.issues } }, null, 2)}
|
|
@@ -14654,8 +14968,10 @@ var runCommand = defineCommand88({
|
|
|
14654
14968
|
);
|
|
14655
14969
|
process.exit(2);
|
|
14656
14970
|
}
|
|
14971
|
+
const failedPayload = (message) => progress?.hasPlan() ? progress.failedSnapshot(message) : buildFailedRunRecord(runId, message, recordMeta);
|
|
14657
14972
|
if (e instanceof LayerExecutionError) {
|
|
14658
14973
|
const failures = e.failures.map((f) => ({ node_id: f.nodeId, message: describeFailureReason(f.reason) }));
|
|
14974
|
+
if (poster) await poster.flush(failedPayload(e.message));
|
|
14659
14975
|
process.stderr.write(
|
|
14660
14976
|
`${JSON.stringify({ ok: false, error: { code: "runtime", message: e.message, failures } }, null, 2)}
|
|
14661
14977
|
`
|
|
@@ -14663,40 +14979,59 @@ var runCommand = defineCommand88({
|
|
|
14663
14979
|
process.exit(1);
|
|
14664
14980
|
}
|
|
14665
14981
|
const msg = e instanceof Error ? e.message : String(e);
|
|
14982
|
+
if (poster) await poster.flush(failedPayload(msg));
|
|
14666
14983
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "runtime", message: msg } }, null, 2)}
|
|
14667
14984
|
`);
|
|
14668
14985
|
process.exit(1);
|
|
14669
14986
|
}
|
|
14670
14987
|
}
|
|
14671
14988
|
});
|
|
14989
|
+
function canvasNodeIds(parsed) {
|
|
14990
|
+
const nodes = parsed?.nodes;
|
|
14991
|
+
if (!Array.isArray(nodes)) return [];
|
|
14992
|
+
return nodes.map((node) => node?.id).filter((id) => typeof id === "string");
|
|
14993
|
+
}
|
|
14994
|
+
function buildRecorder() {
|
|
14995
|
+
return async (payload) => {
|
|
14996
|
+
try {
|
|
14997
|
+
const creds = requireCredentialsFromEnv();
|
|
14998
|
+
const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
|
|
14999
|
+
await client.recordRun(payload);
|
|
15000
|
+
} catch (e) {
|
|
15001
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
15002
|
+
process.stderr.write(`[warn] run record not persisted (${msg})
|
|
15003
|
+
`);
|
|
15004
|
+
}
|
|
15005
|
+
};
|
|
15006
|
+
}
|
|
14672
15007
|
|
|
14673
15008
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
14674
|
-
import { readFile as
|
|
14675
|
-
import
|
|
15009
|
+
import { access, mkdir as mkdir2, readFile as readFile4, writeFile as writeFile2 } from "fs/promises";
|
|
15010
|
+
import path9 from "path";
|
|
14676
15011
|
import { defineCommand as defineCommand89 } from "citty";
|
|
14677
15012
|
|
|
14678
15013
|
// src/engine/scaffold/staticAd.ts
|
|
14679
|
-
import { z as
|
|
15014
|
+
import { z as z11 } from "zod";
|
|
14680
15015
|
var GEN_ASPECT_RATIOS = /* @__PURE__ */ new Set(["1:1", "4:5", "9:16", "16:9", "4:3", "3:4", "2:3", "3:2", "21:9"]);
|
|
14681
15016
|
var DEFAULT_ASPECT_RATIO = "9:16";
|
|
14682
|
-
var Blueprint =
|
|
14683
|
-
meta:
|
|
14684
|
-
text_content:
|
|
15017
|
+
var Blueprint = z11.object({
|
|
15018
|
+
meta: z11.object({ estimated_aspect_ratio: z11.string().optional() }).loose().optional(),
|
|
15019
|
+
text_content: z11.array(z11.object({ text: z11.string().optional() }).loose()).optional()
|
|
14685
15020
|
}).loose();
|
|
14686
|
-
var ElementLocator =
|
|
14687
|
-
collection:
|
|
14688
|
-
index:
|
|
15021
|
+
var ElementLocator = z11.object({
|
|
15022
|
+
collection: z11.enum(["subjects", "people", "brands_logos"]),
|
|
15023
|
+
index: z11.number().int().nonnegative()
|
|
14689
15024
|
}).loose();
|
|
14690
|
-
var MainElement =
|
|
15025
|
+
var MainElement = z11.object({
|
|
14691
15026
|
// logo | product | person | animal | badge | other
|
|
14692
|
-
type:
|
|
14693
|
-
label:
|
|
14694
|
-
description:
|
|
14695
|
-
expression:
|
|
14696
|
-
reason:
|
|
15027
|
+
type: z11.string(),
|
|
15028
|
+
label: z11.string().optional(),
|
|
15029
|
+
description: z11.string().optional(),
|
|
15030
|
+
expression: z11.string().nullable().optional(),
|
|
15031
|
+
reason: z11.string().optional(),
|
|
14697
15032
|
locator: ElementLocator.optional()
|
|
14698
15033
|
}).loose();
|
|
14699
|
-
var MainElements =
|
|
15034
|
+
var MainElements = z11.array(MainElement);
|
|
14700
15035
|
function sanitizeId(raw, fallback) {
|
|
14701
15036
|
const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
14702
15037
|
return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
|
|
@@ -14858,18 +15193,175 @@ function staticAdReport(input, elementsInput, opts) {
|
|
|
14858
15193
|
};
|
|
14859
15194
|
}
|
|
14860
15195
|
|
|
15196
|
+
// src/commands/canvas/creative-definition.ts
|
|
15197
|
+
import path7 from "path";
|
|
15198
|
+
var PLATFORM_VALUES = ["meta", "google", "linkedin", "tiktok", "youtube", "x", "other"];
|
|
15199
|
+
var FORMAT_VALUES = ["1:1", "4:5", "9:16", "16:9", "1.91:1"];
|
|
15200
|
+
function titleFromSlug(slug) {
|
|
15201
|
+
const title = slug.split("-").filter(Boolean).map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join(" ");
|
|
15202
|
+
return title || slug;
|
|
15203
|
+
}
|
|
15204
|
+
function resolvePlatform(platform) {
|
|
15205
|
+
const value = platform?.trim();
|
|
15206
|
+
return value && PLATFORM_VALUES.includes(value) ? value : "meta";
|
|
15207
|
+
}
|
|
15208
|
+
function resolveFormats(aspect) {
|
|
15209
|
+
const value = aspect?.trim();
|
|
15210
|
+
return value && FORMAT_VALUES.includes(value) ? [value] : ["4:5"];
|
|
15211
|
+
}
|
|
15212
|
+
function referenceRelativePath(kind, ext) {
|
|
15213
|
+
const name = kind === "video" ? "source" : "original";
|
|
15214
|
+
return `references/${name}${ext}`;
|
|
15215
|
+
}
|
|
15216
|
+
function describeBlueprintIntent(blueprint) {
|
|
15217
|
+
const intent = blueprint?.ad_intent;
|
|
15218
|
+
if (typeof intent === "string" && intent.trim()) return intent.trim();
|
|
15219
|
+
if (intent && typeof intent === "object") {
|
|
15220
|
+
const summary = intent.summary ?? intent.feeling;
|
|
15221
|
+
if (typeof summary === "string" && summary.trim()) return summary.trim();
|
|
15222
|
+
}
|
|
15223
|
+
return void 0;
|
|
15224
|
+
}
|
|
15225
|
+
function yamlScalar(value) {
|
|
15226
|
+
return JSON.stringify(value);
|
|
15227
|
+
}
|
|
15228
|
+
function buildCreativeDefinition(input) {
|
|
15229
|
+
const lines = ["---", `title: ${yamlScalar(input.title)}`, `kind: ${input.kind}`, `platform: ${input.platform}`];
|
|
15230
|
+
lines.push(`formats: [${input.formats.map(yamlScalar).join(", ")}]`);
|
|
15231
|
+
lines.push(`status: ${input.status ?? "draft"}`);
|
|
15232
|
+
if (input.sourceReferenceUrl) lines.push(`sourceReferenceUrl: ${yamlScalar(input.sourceReferenceUrl)}`);
|
|
15233
|
+
if (input.sourceAdvertiser) lines.push(`sourceAdvertiser: ${yamlScalar(input.sourceAdvertiser)}`);
|
|
15234
|
+
if (input.sourceKind) lines.push(`sourceKind: ${input.sourceKind}`);
|
|
15235
|
+
if (input.sourcePath) lines.push(`sourcePath: ${yamlScalar(input.sourcePath)}`);
|
|
15236
|
+
lines.push("---", "");
|
|
15237
|
+
lines.push(input.description?.trim() || `${input.title} \u2014 canvas-built ${input.kind} ad for ${input.platform}.`);
|
|
15238
|
+
lines.push("");
|
|
15239
|
+
return lines.join("\n");
|
|
15240
|
+
}
|
|
15241
|
+
|
|
14861
15242
|
// src/commands/canvas/scaffold-static-ad-paths.ts
|
|
14862
|
-
import
|
|
14863
|
-
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd()) {
|
|
15243
|
+
import path8 from "path";
|
|
15244
|
+
function resolveScaffoldStaticAdPaths(rawFile, out, cwd = process.cwd(), slug) {
|
|
14864
15245
|
const file = rawFile.trim();
|
|
14865
15246
|
const imageIsUrl = /^https?:\/\//i.test(file);
|
|
14866
|
-
const imageSource = imageIsUrl ? file :
|
|
14867
|
-
const outPath = out ?
|
|
14868
|
-
const blueprintPath =
|
|
14869
|
-
|
|
15247
|
+
const imageSource = imageIsUrl ? file : path8.resolve(cwd, file);
|
|
15248
|
+
const outPath = out ? path8.resolve(cwd, out) : slug ? path8.join(cwd, "src", "creatives", slug, `${slug}.canvas.json`) : imageIsUrl ? path8.join(cwd, "static-ad.canvas.json") : path8.join(path8.dirname(imageSource), "static-ad.canvas.json");
|
|
15249
|
+
const blueprintPath = path8.join(path8.dirname(outPath), "prompt.json");
|
|
15250
|
+
const creativeDir = slug ? path8.dirname(outPath) : null;
|
|
15251
|
+
const definitionPath = creativeDir ? path8.join(creativeDir, "_definition.md") : null;
|
|
15252
|
+
const referencesDir = creativeDir ? path8.join(creativeDir, "references") : null;
|
|
15253
|
+
return { imageIsUrl, imageSource, outPath, blueprintPath, creativeDir, definitionPath, referencesDir };
|
|
15254
|
+
}
|
|
15255
|
+
var SCAFFOLD_SLUG_PATTERN = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/;
|
|
15256
|
+
var SCAFFOLD_SLUG_MAX_LENGTH = 100;
|
|
15257
|
+
function isValidScaffoldSlug(slug) {
|
|
15258
|
+
return slug.length <= SCAFFOLD_SLUG_MAX_LENGTH && SCAFFOLD_SLUG_PATTERN.test(slug);
|
|
15259
|
+
}
|
|
15260
|
+
|
|
15261
|
+
// src/commands/canvas/definition-graph.ts
|
|
15262
|
+
var MAX_NODES = 300;
|
|
15263
|
+
function walkStrings(value, cb) {
|
|
15264
|
+
if (typeof value === "string") {
|
|
15265
|
+
cb(value);
|
|
15266
|
+
return;
|
|
15267
|
+
}
|
|
15268
|
+
if (Array.isArray(value)) {
|
|
15269
|
+
for (const v of value) walkStrings(v, cb);
|
|
15270
|
+
return;
|
|
15271
|
+
}
|
|
15272
|
+
if (value && typeof value === "object") {
|
|
15273
|
+
for (const v of Object.values(value)) walkStrings(v, cb);
|
|
15274
|
+
}
|
|
15275
|
+
}
|
|
15276
|
+
function canvasToDefinitionGraph(canvas) {
|
|
15277
|
+
const rawNodes = canvas?.nodes;
|
|
15278
|
+
if (!Array.isArray(rawNodes)) return null;
|
|
15279
|
+
const parsed = [];
|
|
15280
|
+
for (const raw of rawNodes) {
|
|
15281
|
+
const id = raw?.id;
|
|
15282
|
+
const type = raw?.type;
|
|
15283
|
+
if (typeof id !== "string" || typeof type !== "string") continue;
|
|
15284
|
+
parsed.push({ id, type, inputs: raw.inputs, params: raw.params });
|
|
15285
|
+
if (parsed.length >= MAX_NODES) break;
|
|
15286
|
+
}
|
|
15287
|
+
if (parsed.length === 0) return null;
|
|
15288
|
+
const ids = new Set(parsed.map((n) => n.id));
|
|
15289
|
+
const nodes = parsed.map(({ id, type, inputs, params }) => {
|
|
15290
|
+
const deps = /* @__PURE__ */ new Set();
|
|
15291
|
+
const collect = (s) => {
|
|
15292
|
+
if (!s.startsWith(REF_PREFIX)) return;
|
|
15293
|
+
const expr = parseRefExpr(s);
|
|
15294
|
+
if (expr && expr.nodeId !== id && ids.has(expr.nodeId)) deps.add(expr.nodeId);
|
|
15295
|
+
};
|
|
15296
|
+
walkStrings(inputs, collect);
|
|
15297
|
+
walkStrings(params, collect);
|
|
15298
|
+
return deps.size > 0 ? { id, type, deps: [...deps] } : { id, type };
|
|
15299
|
+
});
|
|
15300
|
+
const rawOutput = canvas?.output;
|
|
15301
|
+
const outNode = rawOutput?.node;
|
|
15302
|
+
const outSlot = rawOutput?.output;
|
|
15303
|
+
const output = typeof outNode === "string" && typeof outSlot === "string" ? { node: outNode, output: outSlot } : void 0;
|
|
15304
|
+
return { nodes, output };
|
|
15305
|
+
}
|
|
15306
|
+
|
|
15307
|
+
// src/commands/canvas/sync-definition.ts
|
|
15308
|
+
async function syncCreativeDefinitionBestEffort(input) {
|
|
15309
|
+
const chatId = process.env.BAKER_CHAT_ID;
|
|
15310
|
+
if (!chatId) return;
|
|
15311
|
+
const graph = canvasToDefinitionGraph(input.canvas);
|
|
15312
|
+
if (!graph || graph.nodes.length === 0) return;
|
|
15313
|
+
try {
|
|
15314
|
+
const creds = requireCredentialsFromEnv();
|
|
15315
|
+
const client = new BackendClient({ baseUrl: creds.url, apiKey: creds.apiKey });
|
|
15316
|
+
await client.syncCreativeDefinition({
|
|
15317
|
+
slug: input.slug,
|
|
15318
|
+
title: input.title,
|
|
15319
|
+
platform: input.platform,
|
|
15320
|
+
formats: input.formats,
|
|
15321
|
+
sourceReferenceUrl: input.sourceReferenceUrl,
|
|
15322
|
+
graph,
|
|
15323
|
+
chatId
|
|
15324
|
+
});
|
|
15325
|
+
process.stdout.write(`[definition] synced workflow graph (${graph.nodes.length} nodes) \u2014 view it in the dashboard
|
|
15326
|
+
`);
|
|
15327
|
+
} catch (e) {
|
|
15328
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
15329
|
+
process.stderr.write(`[warn] workflow graph not synced (${msg})
|
|
15330
|
+
`);
|
|
15331
|
+
}
|
|
14870
15332
|
}
|
|
14871
15333
|
|
|
14872
15334
|
// src/commands/canvas/scaffold-static-ad.ts
|
|
15335
|
+
async function fileExists(target) {
|
|
15336
|
+
try {
|
|
15337
|
+
await access(target);
|
|
15338
|
+
return true;
|
|
15339
|
+
} catch {
|
|
15340
|
+
return false;
|
|
15341
|
+
}
|
|
15342
|
+
}
|
|
15343
|
+
var MODEL_SAFE_EXT_BY_MIME = {
|
|
15344
|
+
"image/png": ".png",
|
|
15345
|
+
"image/jpeg": ".jpg",
|
|
15346
|
+
"image/gif": ".gif",
|
|
15347
|
+
"image/webp": ".webp"
|
|
15348
|
+
};
|
|
15349
|
+
async function copySourceIntoReferences(source, isUrl, referencesDir) {
|
|
15350
|
+
await mkdir2(referencesDir, { recursive: true });
|
|
15351
|
+
let bytes;
|
|
15352
|
+
if (isUrl) {
|
|
15353
|
+
const res = await fetch(source);
|
|
15354
|
+
if (!res.ok) throw new Error(`failed to download source image (${res.status})`);
|
|
15355
|
+
bytes = Buffer.from(await res.arrayBuffer());
|
|
15356
|
+
} else {
|
|
15357
|
+
bytes = await readFile4(source);
|
|
15358
|
+
}
|
|
15359
|
+
const safe = await toModelSafeImage(bytes);
|
|
15360
|
+
const relPath = referenceRelativePath("image", MODEL_SAFE_EXT_BY_MIME[safe.mime] ?? ".png");
|
|
15361
|
+
const dest = path9.join(referencesDir, path9.basename(relPath));
|
|
15362
|
+
await writeFile2(dest, safe.bytes);
|
|
15363
|
+
return relPath;
|
|
15364
|
+
}
|
|
14873
15365
|
function resolveModel(kind, preferred) {
|
|
14874
15366
|
const ids = Object.keys(MODEL_REGISTRY[kind]);
|
|
14875
15367
|
return ids.includes(preferred) ? preferred : ids[0] ?? preferred;
|
|
@@ -14912,7 +15404,7 @@ DROP background extras, decorative props, generic scenery, and anything small or
|
|
|
14912
15404
|
For each kept element return: { "type": one of logo|product|person|animal|badge, "label": a short UPPER_SNAKE_CASE name (e.g. LOGO, PRODUCT, HERO_DOG, TRUSTPILOT), "description": a concrete reusable description to source/shoot the real asset (include the exact expression for a living subject, and its castable attributes \u2014 breed/species for an animal, apparent age band, apparent origin/ethnicity, and wardrobe/setting for a person \u2014 so it can be recast to fit OUR audience/market), "expression": the facial expression for a living subject or null, "reason": why it is identity-critical, "locator": the blueprint entry this element came from as { "collection": one of "subjects" | "people" | "brands_logos", "index": its 0-based position in that array } (people -> people; logos/badges -> brands_logos; products/animals/objects -> subjects). Output ONLY the JSON object.`;
|
|
14913
15405
|
async function loadAssetText(ref, label) {
|
|
14914
15406
|
const r = ref;
|
|
14915
|
-
if (typeof r?.path === "string") return
|
|
15407
|
+
if (typeof r?.path === "string") return readFile4(r.path, "utf8");
|
|
14916
15408
|
if (typeof r?.url === "string") {
|
|
14917
15409
|
const res = await fetch(r.url);
|
|
14918
15410
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -15029,6 +15521,16 @@ var scaffoldStaticAdCommand = defineCommand89({
|
|
|
15029
15521
|
file: { type: "positional", required: true, description: "Path or http(s) URL to the source/inspiration image" },
|
|
15030
15522
|
context: { type: "string", description: "Known provenance (advertiser, category, market) to ground the describe" },
|
|
15031
15523
|
out: { type: "string", description: "Output canvas path (default <image-dir>/static-ad.canvas.json)" },
|
|
15524
|
+
slug: {
|
|
15525
|
+
type: "string",
|
|
15526
|
+
description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
|
|
15527
|
+
},
|
|
15528
|
+
title: { type: "string", description: "Creative title for _definition.md (default: title-cased slug)" },
|
|
15529
|
+
platform: {
|
|
15530
|
+
type: "string",
|
|
15531
|
+
description: "Ad platform for _definition.md (meta|google|linkedin|tiktok|youtube|x|other; default meta)"
|
|
15532
|
+
},
|
|
15533
|
+
advertiser: { type: "string", description: "Source advertiser recorded in _definition.md" },
|
|
15032
15534
|
"describe-model": { type: "string", description: "Override the image_describe model id" },
|
|
15033
15535
|
"select-model": { type: "string", description: "Override the text_generate model id for element selection" },
|
|
15034
15536
|
"layout-model": { type: "string", description: "Override the text_generate model id for the layout pass" },
|
|
@@ -15037,10 +15539,21 @@ var scaffoldStaticAdCommand = defineCommand89({
|
|
|
15037
15539
|
"skip-font": { type: "boolean", description: "Skip the brand-font \u2192 type-specimen slot" }
|
|
15038
15540
|
},
|
|
15039
15541
|
async run({ args }) {
|
|
15040
|
-
const
|
|
15542
|
+
const slug = args.slug ? String(args.slug) : void 0;
|
|
15543
|
+
if (slug && !isValidScaffoldSlug(slug)) {
|
|
15544
|
+
process.stderr.write(
|
|
15545
|
+
`${JSON.stringify({ ok: false, error: { code: "invalid_slug", message: "--slug must be lowercase kebab (a-z, 0-9, hyphens), max 100 chars" } }, null, 2)}
|
|
15546
|
+
`
|
|
15547
|
+
);
|
|
15548
|
+
process.exit(2);
|
|
15549
|
+
}
|
|
15550
|
+
const { imageIsUrl, imageSource, outPath, blueprintPath, definitionPath, referencesDir } = resolveScaffoldStaticAdPaths(
|
|
15041
15551
|
String(args.file),
|
|
15042
|
-
args.out ? String(args.out) : void 0
|
|
15552
|
+
args.out ? String(args.out) : void 0,
|
|
15553
|
+
process.cwd(),
|
|
15554
|
+
slug
|
|
15043
15555
|
);
|
|
15556
|
+
await mkdir2(path9.dirname(outPath), { recursive: true });
|
|
15044
15557
|
const { describeModel, selectModel, layoutModel, genModel } = resolveModels(args);
|
|
15045
15558
|
const describeCanvas = buildDescribeCanvas(
|
|
15046
15559
|
imageSource,
|
|
@@ -15055,13 +15568,23 @@ var scaffoldStaticAdCommand = defineCommand89({
|
|
|
15055
15568
|
if (layout && annotated && typeof annotated === "object") {
|
|
15056
15569
|
annotated.layout = layout;
|
|
15057
15570
|
}
|
|
15058
|
-
await
|
|
15571
|
+
await writeFile2(blueprintPath, `${JSON.stringify(annotated, null, 2)}
|
|
15059
15572
|
`, "utf8");
|
|
15573
|
+
let canvasImagePath = imageSource;
|
|
15574
|
+
let canvasImageIsUrl = imageIsUrl;
|
|
15575
|
+
let canvasBlueprintPath = blueprintPath;
|
|
15576
|
+
let sourceRelPath;
|
|
15577
|
+
if (referencesDir) {
|
|
15578
|
+
sourceRelPath = await copySourceIntoReferences(imageSource, imageIsUrl, referencesDir);
|
|
15579
|
+
canvasImagePath = sourceRelPath;
|
|
15580
|
+
canvasImageIsUrl = false;
|
|
15581
|
+
canvasBlueprintPath = "./prompt.json";
|
|
15582
|
+
}
|
|
15060
15583
|
const opts = {
|
|
15061
15584
|
genModel,
|
|
15062
|
-
imagePath:
|
|
15063
|
-
imageIsUrl,
|
|
15064
|
-
blueprintPath,
|
|
15585
|
+
imagePath: canvasImagePath,
|
|
15586
|
+
imageIsUrl: canvasImageIsUrl,
|
|
15587
|
+
blueprintPath: canvasBlueprintPath,
|
|
15065
15588
|
aspectRatio: args.aspect ? String(args.aspect) : void 0,
|
|
15066
15589
|
includeFont: !args["skip-font"]
|
|
15067
15590
|
};
|
|
@@ -15081,14 +15604,43 @@ var scaffoldStaticAdCommand = defineCommand89({
|
|
|
15081
15604
|
);
|
|
15082
15605
|
process.exit(2);
|
|
15083
15606
|
}
|
|
15084
|
-
await
|
|
15607
|
+
await writeFile2(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
15085
15608
|
`, "utf8");
|
|
15609
|
+
if (definitionPath && !await fileExists(definitionPath)) {
|
|
15610
|
+
await writeFile2(
|
|
15611
|
+
definitionPath,
|
|
15612
|
+
buildCreativeDefinition({
|
|
15613
|
+
title: args.title ? String(args.title) : titleFromSlug(slug ?? ""),
|
|
15614
|
+
kind: "static",
|
|
15615
|
+
platform: resolvePlatform(args.platform ? String(args.platform) : void 0),
|
|
15616
|
+
formats: resolveFormats(args.aspect ? String(args.aspect) : report.aspect_ratio),
|
|
15617
|
+
sourceReferenceUrl: imageIsUrl ? imageSource : void 0,
|
|
15618
|
+
sourceAdvertiser: args.advertiser ? String(args.advertiser) : args.context ? String(args.context) : void 0,
|
|
15619
|
+
sourceKind: "image",
|
|
15620
|
+
sourcePath: sourceRelPath,
|
|
15621
|
+
description: describeBlueprintIntent(blueprint)
|
|
15622
|
+
}),
|
|
15623
|
+
"utf8"
|
|
15624
|
+
);
|
|
15625
|
+
}
|
|
15626
|
+
if (slug) {
|
|
15627
|
+
await syncCreativeDefinitionBestEffort({
|
|
15628
|
+
slug,
|
|
15629
|
+
title: args.title ? String(args.title) : titleFromSlug(slug),
|
|
15630
|
+
platform: resolvePlatform(args.platform ? String(args.platform) : void 0),
|
|
15631
|
+
formats: resolveFormats(args.aspect ? String(args.aspect) : report.aspect_ratio),
|
|
15632
|
+
sourceReferenceUrl: imageIsUrl ? imageSource : void 0,
|
|
15633
|
+
canvas
|
|
15634
|
+
});
|
|
15635
|
+
}
|
|
15086
15636
|
process.stdout.write(
|
|
15087
15637
|
`${JSON.stringify(
|
|
15088
15638
|
{
|
|
15089
15639
|
ok: true,
|
|
15090
15640
|
canvas_path: outPath,
|
|
15091
15641
|
prompt_path: blueprintPath,
|
|
15642
|
+
definition_path: definitionPath ?? void 0,
|
|
15643
|
+
source_reference: sourceRelPath ?? void 0,
|
|
15092
15644
|
output: canvas.output,
|
|
15093
15645
|
models: { describe: describeModel, select: selectModel, layout: layoutModel, gen: opts.genModel },
|
|
15094
15646
|
aspect_ratio: report.aspect_ratio,
|
|
@@ -15099,10 +15651,10 @@ var scaffoldStaticAdCommand = defineCommand89({
|
|
|
15099
15651
|
run_estimated_credits: validation.estimatedCredits
|
|
15100
15652
|
},
|
|
15101
15653
|
checklist: {
|
|
15102
|
-
edit_prompt: `Edit ${
|
|
15654
|
+
edit_prompt: `Edit ${path9.basename(blueprintPath)} \u2014 it is the blueprint generated from your image; rewrite it into the ad you want (palette, copy, claims, subjects). It feeds the generator directly.`,
|
|
15103
15655
|
assets_to_supply: report.elements,
|
|
15104
15656
|
font_slot: report.includes_font ? "Drop a brand font at the [TODO] brandfont path, or delete the brandfont + type_ref nodes to skip it." : "skipped (--skip-font)",
|
|
15105
|
-
note: "
|
|
15657
|
+
note: "Populate as you go: for each [TODO] ingest slot, source its real asset and wire it into the slot right away \u2014 one at a time, not all sourced first then reconciled at the end. When every slot is filled, `baker canvas validate` then `baker canvas run`. Running generates a billed image \u2014 it is not free."
|
|
15106
15658
|
}
|
|
15107
15659
|
},
|
|
15108
15660
|
null,
|
|
@@ -15114,13 +15666,14 @@ var scaffoldStaticAdCommand = defineCommand89({
|
|
|
15114
15666
|
});
|
|
15115
15667
|
|
|
15116
15668
|
// src/commands/canvas/scaffold-video.ts
|
|
15117
|
-
import { cp, mkdir, readFile as
|
|
15118
|
-
import
|
|
15669
|
+
import { access as access2, cp, mkdir as mkdir3, readFile as readFile7, writeFile as writeFile3 } from "fs/promises";
|
|
15670
|
+
import { tmpdir as tmpdir2 } from "os";
|
|
15671
|
+
import path12 from "path";
|
|
15119
15672
|
import { defineCommand as defineCommand90 } from "citty";
|
|
15120
15673
|
|
|
15121
15674
|
// src/engine/nodes/local/lib/sceneDetect.ts
|
|
15122
15675
|
import { execFile as execFile2 } from "child_process";
|
|
15123
|
-
import { mkdtemp, readdir as readdir2, readFile as
|
|
15676
|
+
import { mkdtemp, readdir as readdir2, readFile as readFile5, rm as rm3 } from "fs/promises";
|
|
15124
15677
|
import { tmpdir } from "os";
|
|
15125
15678
|
import { join as join2 } from "path";
|
|
15126
15679
|
import { promisify as promisify2 } from "util";
|
|
@@ -15196,9 +15749,9 @@ async function runSceneDetectOnce(filePath, threshold, minSceneLenS, timeoutMs)
|
|
|
15196
15749
|
);
|
|
15197
15750
|
const csvName = (await readdir2(outDir)).find((f) => f.toLowerCase().endsWith(".csv"));
|
|
15198
15751
|
if (!csvName) return [];
|
|
15199
|
-
return parsePySceneDetectCsvCuts(await
|
|
15752
|
+
return parsePySceneDetectCsvCuts(await readFile5(join2(outDir, csvName), "utf-8"));
|
|
15200
15753
|
} finally {
|
|
15201
|
-
await
|
|
15754
|
+
await rm3(outDir, { recursive: true, force: true });
|
|
15202
15755
|
}
|
|
15203
15756
|
}
|
|
15204
15757
|
async function detectSceneCutsPySceneDetect(filePath, opts = {}) {
|
|
@@ -15232,7 +15785,7 @@ import { toCardinal as nwKo } from "n2words/ko-KR";
|
|
|
15232
15785
|
import { toCardinal as nwNl } from "n2words/nl-NL";
|
|
15233
15786
|
import { toCardinal as nwPl } from "n2words/pl-PL";
|
|
15234
15787
|
import { toCardinal as nwPt } from "n2words/pt-PT";
|
|
15235
|
-
import { z as
|
|
15788
|
+
import { z as z12 } from "zod";
|
|
15236
15789
|
|
|
15237
15790
|
// src/engine/scaffold/lib/shoot-modes.ts
|
|
15238
15791
|
var SHOOT_MODES = [
|
|
@@ -15545,71 +16098,71 @@ function trimArgs(durationS, offsetS = 0, dims) {
|
|
|
15545
16098
|
"{{out.video}}"
|
|
15546
16099
|
];
|
|
15547
16100
|
}
|
|
15548
|
-
var FrameAsset =
|
|
15549
|
-
var DialogueLine =
|
|
15550
|
-
speaker:
|
|
15551
|
-
line:
|
|
16101
|
+
var FrameAsset = z12.object({ url: z12.string().optional() }).loose().optional();
|
|
16102
|
+
var DialogueLine = z12.object({
|
|
16103
|
+
speaker: z12.string().optional(),
|
|
16104
|
+
line: z12.string().optional(),
|
|
15552
16105
|
// Absolute seconds on the source timeline (the deconstruct emits both).
|
|
15553
|
-
start_s:
|
|
15554
|
-
end_s:
|
|
15555
|
-
delivery:
|
|
15556
|
-
voice_description:
|
|
16106
|
+
start_s: z12.number().optional(),
|
|
16107
|
+
end_s: z12.number().optional(),
|
|
16108
|
+
delivery: z12.string().optional(),
|
|
16109
|
+
voice_description: z12.string().optional(),
|
|
15557
16110
|
// DECON-supplied: is this speaker's FACE visibly speaking in THIS scene? Element
|
|
15558
16111
|
// presence alone can't answer that — a founder pictured in a polaroid close-up is
|
|
15559
16112
|
// "present" yet the line is voiceover, and treating it as on-camera produced a
|
|
15560
16113
|
// native Seedance lip-sync clip of a still photograph. `false` pins the line to
|
|
15561
16114
|
// the VO path; absent keeps the presence-based decision (old blueprints).
|
|
15562
|
-
on_camera:
|
|
16115
|
+
on_camera: z12.boolean().optional()
|
|
15563
16116
|
}).loose();
|
|
15564
|
-
var Sfx =
|
|
15565
|
-
at_s:
|
|
15566
|
-
duration_s:
|
|
15567
|
-
sound_effect_prompt:
|
|
15568
|
-
description:
|
|
16117
|
+
var Sfx = z12.object({
|
|
16118
|
+
at_s: z12.number().optional(),
|
|
16119
|
+
duration_s: z12.number().optional(),
|
|
16120
|
+
sound_effect_prompt: z12.string().optional(),
|
|
16121
|
+
description: z12.string().optional()
|
|
15569
16122
|
}).loose();
|
|
15570
|
-
var CompositionRegion =
|
|
16123
|
+
var CompositionRegion = z12.object({
|
|
15571
16124
|
// full | top | bottom | left | right | inset
|
|
15572
|
-
panel:
|
|
16125
|
+
panel: z12.string().optional(),
|
|
15573
16126
|
// 9-grid anchor for an `inset` presenter box.
|
|
15574
|
-
position:
|
|
15575
|
-
is_presenter:
|
|
16127
|
+
position: z12.string().optional(),
|
|
16128
|
+
is_presenter: z12.boolean().optional(),
|
|
15576
16129
|
// The cast id shown/speaking in this region (routes lip-sync + element refs).
|
|
15577
|
-
cast_ref:
|
|
16130
|
+
cast_ref: z12.string().optional(),
|
|
15578
16131
|
// What the region's content IS: camera | screen_capture | static_graphic |
|
|
15579
16132
|
// generated. Authoritative for routing when present (regex-over-prose fallback
|
|
15580
16133
|
// otherwise): screen_capture/static_graphic are rebuilt from REAL surfaces on the
|
|
15581
16134
|
// overlay layer, never AI-generated.
|
|
15582
|
-
kind:
|
|
16135
|
+
kind: z12.string().optional(),
|
|
15583
16136
|
// Opaque id naming the SPECIFIC on-screen document/note/app-state this
|
|
15584
16137
|
// screen_capture region shows. Two scenes share it only when they show the SAME
|
|
15585
16138
|
// recording continuing (scrolling/typing/waiting within it) — a genuinely
|
|
15586
16139
|
// DIFFERENT document/note/recording (a source video splicing two screen captures)
|
|
15587
16140
|
// gets a different id. Breaks a persistent-layout run into separate surface stubs
|
|
15588
16141
|
// instead of asking the operator for one screenshot that can't cover both.
|
|
15589
|
-
surface_id:
|
|
16142
|
+
surface_id: z12.string().optional(),
|
|
15590
16143
|
// Camera bubble(s)/inset(s) embedded INSIDE this region's surface (a Loom-style
|
|
15591
16144
|
// presenter bubble inside a screen recording) — video-in-video the reproduction
|
|
15592
16145
|
// must re-composite, not paint into the surface.
|
|
15593
|
-
nested:
|
|
15594
|
-
summary:
|
|
15595
|
-
frame_prompt:
|
|
15596
|
-
motion_prompt:
|
|
16146
|
+
nested: z12.array(z12.object({}).loose()).optional(),
|
|
16147
|
+
summary: z12.string().optional(),
|
|
16148
|
+
frame_prompt: z12.string().optional(),
|
|
16149
|
+
motion_prompt: z12.string().optional()
|
|
15597
16150
|
}).loose();
|
|
15598
|
-
var SceneComposition =
|
|
16151
|
+
var SceneComposition = z12.object({
|
|
15599
16152
|
// full_frame (default) | split_screen | pip | keyed_overlay
|
|
15600
|
-
layout:
|
|
16153
|
+
layout: z12.string().optional(),
|
|
15601
16154
|
// split_screen only: vertical (top/bottom) | horizontal (left/right).
|
|
15602
|
-
split_axis:
|
|
15603
|
-
regions:
|
|
16155
|
+
split_axis: z12.string().optional(),
|
|
16156
|
+
regions: z12.array(CompositionRegion).optional()
|
|
15604
16157
|
}).loose();
|
|
15605
|
-
var CameraMotion =
|
|
15606
|
-
var TranscriptWord =
|
|
15607
|
-
var Scene =
|
|
15608
|
-
start_s:
|
|
15609
|
-
end_s:
|
|
15610
|
-
duration_s:
|
|
15611
|
-
summary:
|
|
15612
|
-
action_detail:
|
|
16158
|
+
var CameraMotion = z12.object({ movement: z12.string().optional(), detail: z12.string().optional() }).loose();
|
|
16159
|
+
var TranscriptWord = z12.object({ text: z12.string().optional() }).loose();
|
|
16160
|
+
var Scene = z12.object({
|
|
16161
|
+
start_s: z12.number().optional(),
|
|
16162
|
+
end_s: z12.number().optional(),
|
|
16163
|
+
duration_s: z12.number().optional(),
|
|
16164
|
+
summary: z12.string().optional(),
|
|
16165
|
+
action_detail: z12.string().optional(),
|
|
15613
16166
|
// The scene's spatial layout. Absent/full_frame ⇒ one uncut shot (default path).
|
|
15614
16167
|
// A layered layout (split_screen/pip/keyed_overlay) with regions ⇒ the scaffold
|
|
15615
16168
|
// builds one clip per region and stacks/overlays them into the scene picture.
|
|
@@ -15617,82 +16170,82 @@ var Scene = z13.object({
|
|
|
15617
16170
|
// The capture "look" for this scene — selected from the ad-native shoot-mode
|
|
15618
16171
|
// grammar (see lib/shoot-modes.ts). When absent the scaffold auto-derives a
|
|
15619
16172
|
// UGC/product mode; a human can override per scene by setting this.
|
|
15620
|
-
shoot_mode:
|
|
16173
|
+
shoot_mode: z12.string().optional(),
|
|
15621
16174
|
// Diegetic ambient the clip's native audio should carry (no music). When
|
|
15622
16175
|
// absent the scene falls back to its shoot mode's default ambience.
|
|
15623
|
-
ambient:
|
|
16176
|
+
ambient: z12.string().optional(),
|
|
15624
16177
|
camera_motion: CameraMotion.optional(),
|
|
15625
|
-
start_frame_prompt:
|
|
15626
|
-
end_frame_prompt:
|
|
15627
|
-
motion_prompt:
|
|
16178
|
+
start_frame_prompt: z12.string().optional(),
|
|
16179
|
+
end_frame_prompt: z12.string().optional(),
|
|
16180
|
+
motion_prompt: z12.string().optional(),
|
|
15628
16181
|
// The scene's role in the ad's persuasion arc (DECON-supplied); drives the
|
|
15629
16182
|
// script re-craft checklist. Inferred from position when absent.
|
|
15630
|
-
narrative_role:
|
|
16183
|
+
narrative_role: z12.string().optional(),
|
|
15631
16184
|
// DECON-supplied on the HOOK scene: the engineered physical/emotional state that
|
|
15632
16185
|
// makes the first frame stop the scroll (sweaty/breathless/urgent …). Injected
|
|
15633
16186
|
// into the hook's start-frame description so the generator renders that state,
|
|
15634
16187
|
// not a calm influencer (CCA-11).
|
|
15635
|
-
hook_mechanic:
|
|
16188
|
+
hook_mechanic: z12.object({ mechanic: z12.string().optional(), why_it_stops_scroll: z12.string().optional() }).loose().optional(),
|
|
15636
16189
|
// DECON-supplied per-scene location (so a gym hook isn't flattened to "home").
|
|
15637
|
-
scene_setting:
|
|
16190
|
+
scene_setting: z12.string().optional(),
|
|
15638
16191
|
// How this scene cuts to the next (DECON-supplied). A recognized non-cut type
|
|
15639
16192
|
// (fade/whip/zoom/dissolve/swipe) is reproduced as an ffmpeg xfade at the
|
|
15640
16193
|
// boundary; cut/match_cut/none/other stay hard cuts. The last scene's value is
|
|
15641
16194
|
// ignored (nothing follows it).
|
|
15642
|
-
transition_out:
|
|
15643
|
-
dialogue:
|
|
15644
|
-
sfx:
|
|
15645
|
-
overlays:
|
|
15646
|
-
floating_elements:
|
|
16195
|
+
transition_out: z12.object({ type: z12.string().optional(), description: z12.string().optional() }).loose().optional(),
|
|
16196
|
+
dialogue: z12.array(DialogueLine).optional(),
|
|
16197
|
+
sfx: z12.array(Sfx).optional(),
|
|
16198
|
+
overlays: z12.array(z12.unknown()).optional(),
|
|
16199
|
+
floating_elements: z12.array(z12.unknown()).optional(),
|
|
15647
16200
|
// DECON-supplied: how much the picture itself moves within the shot. Gates the
|
|
15648
16201
|
// flash-hold optimization — a sub-2s b-roll flash with REAL subject motion
|
|
15649
16202
|
// (pouring, spreading, hands working) must stay a real clip; freezing it turns
|
|
15650
16203
|
// a montage into a slideshow. Absent (old blueprints) keeps the cheap still.
|
|
15651
|
-
motion_level:
|
|
15652
|
-
transcript_slice:
|
|
16204
|
+
motion_level: z12.enum(["static", "subtle", "dynamic"]).optional(),
|
|
16205
|
+
transcript_slice: z12.array(TranscriptWord).optional(),
|
|
15653
16206
|
start_frame_asset: FrameAsset,
|
|
15654
16207
|
end_frame_asset: FrameAsset,
|
|
15655
16208
|
// DECON-supplied: true when this scene is a length-split CONTINUATION of the
|
|
15656
16209
|
// previous one (the SAME physical shot, broken up only because it exceeded the
|
|
15657
16210
|
// clip ceiling). The scaffold then shares the splice keyframe — this scene's
|
|
15658
16211
|
// start frame IS the previous scene's end frame — so the join is seamless.
|
|
15659
|
-
continues_previous:
|
|
16212
|
+
continues_previous: z12.boolean().optional()
|
|
15660
16213
|
}).loose();
|
|
15661
|
-
var VideoBlueprint =
|
|
15662
|
-
source:
|
|
15663
|
-
global:
|
|
15664
|
-
music:
|
|
15665
|
-
present:
|
|
15666
|
-
music_prompt:
|
|
16214
|
+
var VideoBlueprint = z12.object({
|
|
16215
|
+
source: z12.object({ aspect_ratio: z12.string().optional(), duration_s: z12.number().optional() }).loose().optional(),
|
|
16216
|
+
global: z12.object({
|
|
16217
|
+
music: z12.object({
|
|
16218
|
+
present: z12.boolean().optional(),
|
|
16219
|
+
music_prompt: z12.string().optional(),
|
|
15667
16220
|
// Absolute second the music enters in the reference (the bed often
|
|
15668
16221
|
// kicks in mid-ad, after the hook). We start the regenerated track here
|
|
15669
16222
|
// instead of at 0 so the timing matches.
|
|
15670
|
-
starts_at_s:
|
|
16223
|
+
starts_at_s: z12.number().optional(),
|
|
15671
16224
|
// Populated by the deconstruct when AudD (Shazam-style) recognizes the
|
|
15672
16225
|
// reference track. We never reuse it — only style the regenerated bed.
|
|
15673
|
-
identified_track:
|
|
16226
|
+
identified_track: z12.object({ title: z12.string().optional(), artist: z12.string().optional() }).loose().nullish()
|
|
15674
16227
|
}).loose().optional(),
|
|
15675
|
-
cast:
|
|
15676
|
-
|
|
15677
|
-
id:
|
|
15678
|
-
description:
|
|
16228
|
+
cast: z12.array(
|
|
16229
|
+
z12.object({
|
|
16230
|
+
id: z12.string().optional(),
|
|
16231
|
+
description: z12.string().optional(),
|
|
15679
16232
|
// The deconstruct's note on the target-market localization (e.g. "native
|
|
15680
16233
|
// French speaker") — read to derive the spoken-track language code.
|
|
15681
|
-
market_localization_note:
|
|
16234
|
+
market_localization_note: z12.string().optional()
|
|
15682
16235
|
}).loose()
|
|
15683
16236
|
).optional(),
|
|
15684
|
-
voiceover:
|
|
16237
|
+
voiceover: z12.object({
|
|
15685
16238
|
// on_camera | mixed → mouths are on screen (lip-sync candidates);
|
|
15686
16239
|
// voiceover | none → narration over the picture (no lip-sync).
|
|
15687
|
-
mode:
|
|
15688
|
-
voice_description:
|
|
15689
|
-
persona:
|
|
16240
|
+
mode: z12.string().optional(),
|
|
16241
|
+
voice_description: z12.string().optional(),
|
|
16242
|
+
persona: z12.string().optional()
|
|
15690
16243
|
}).loose().optional(),
|
|
15691
16244
|
// Visual palette — read only to colour a clean brand-card/CTA plate (the
|
|
15692
16245
|
// first hex is the dominant brand colour); never to drive frame generation.
|
|
15693
|
-
style:
|
|
16246
|
+
style: z12.object({ palette: z12.array(z12.object({ hex: z12.string().optional() }).loose()).optional() }).loose().optional()
|
|
15694
16247
|
}).loose().optional(),
|
|
15695
|
-
scenes:
|
|
16248
|
+
scenes: z12.array(Scene).min(1)
|
|
15696
16249
|
}).loose();
|
|
15697
16250
|
function injectHookPhysicality(blueprint) {
|
|
15698
16251
|
for (const scene of blueprint.scenes) {
|
|
@@ -15702,26 +16255,26 @@ function injectHookPhysicality(blueprint) {
|
|
|
15702
16255
|
scene.start_frame_prompt = `${prompt} The subject's physical state IS the scroll-stopper \u2014 render it explicitly, not a calm pose: ${why}.`;
|
|
15703
16256
|
}
|
|
15704
16257
|
}
|
|
15705
|
-
var AppearsItem =
|
|
15706
|
-
var RecurringElement =
|
|
16258
|
+
var AppearsItem = z12.union([z12.number(), z12.object({ scene: z12.number(), edge: z12.string().optional() }).loose()]);
|
|
16259
|
+
var RecurringElement = z12.object({
|
|
15707
16260
|
// person | animal | product | logo | badge | other
|
|
15708
|
-
type:
|
|
15709
|
-
label:
|
|
15710
|
-
description:
|
|
15711
|
-
expression:
|
|
16261
|
+
type: z12.string(),
|
|
16262
|
+
label: z12.string().optional(),
|
|
16263
|
+
description: z12.string().optional(),
|
|
16264
|
+
expression: z12.string().nullable().optional(),
|
|
15712
16265
|
// When the element maps to a global cast entry, its stable id (for annotation).
|
|
15713
|
-
cast_id:
|
|
16266
|
+
cast_id: z12.string().nullable().optional(),
|
|
15714
16267
|
// The label of another element that is the SAME individual as this one, shown
|
|
15715
16268
|
// in a DIFFERENT wardrobe/persona/state (e.g. one creator playing skeptic in a
|
|
15716
16269
|
// pink shirt and believer in a white shirt). Each look gets its own reference
|
|
15717
16270
|
// slot, but the face/identity must stay identical across them.
|
|
15718
|
-
same_as:
|
|
16271
|
+
same_as: z12.string().nullable().optional(),
|
|
15719
16272
|
// Scenes the element appears in. Either a bare list of scene indices (both
|
|
15720
16273
|
// edges) or per-{scene,edge} entries. Both forms are accepted and merged.
|
|
15721
|
-
scenes:
|
|
15722
|
-
appears_in:
|
|
16274
|
+
scenes: z12.array(z12.number()).optional(),
|
|
16275
|
+
appears_in: z12.array(AppearsItem).optional()
|
|
15723
16276
|
}).loose();
|
|
15724
|
-
var RecurringElements =
|
|
16277
|
+
var RecurringElements = z12.array(RecurringElement);
|
|
15725
16278
|
function sanitizeId2(raw, fallback) {
|
|
15726
16279
|
const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
15727
16280
|
return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
|
|
@@ -15823,6 +16376,13 @@ function slimBlueprintForSelection(blueprintInput) {
|
|
|
15823
16376
|
}
|
|
15824
16377
|
return out;
|
|
15825
16378
|
}
|
|
16379
|
+
function slimBlueprintForFrameStyle(blueprintInput) {
|
|
16380
|
+
if (!blueprintInput || typeof blueprintInput !== "object" || Array.isArray(blueprintInput)) return blueprintInput;
|
|
16381
|
+
const bp = blueprintInput;
|
|
16382
|
+
const out = {};
|
|
16383
|
+
for (const k of ["version", "source", "global", "reference_elements"]) if (k in bp) out[k] = bp[k];
|
|
16384
|
+
return out;
|
|
16385
|
+
}
|
|
15826
16386
|
function roleForType2(type) {
|
|
15827
16387
|
switch (type.toLowerCase()) {
|
|
15828
16388
|
case "logo":
|
|
@@ -16080,9 +16640,10 @@ function buildFrameRef(edge, url, framePrompt, present, ctx, nodes) {
|
|
|
16080
16640
|
id: genId,
|
|
16081
16641
|
type: "image_generate",
|
|
16082
16642
|
// `params.prompt` is this frame's authoritative, edit-per-frame description.
|
|
16083
|
-
// `target_blueprint` is the shared ad spec (cast identity, palette, brand,
|
|
16084
|
-
// the frame must stay consistent with
|
|
16085
|
-
|
|
16643
|
+
// `target_blueprint` is the SLIM shared ad spec (global cast identity, palette, brand,
|
|
16644
|
+
// type — no per-scene content) the frame must stay consistent with; editing one frame
|
|
16645
|
+
// never touches another, and no image inlines the whole film to render one frame.
|
|
16646
|
+
inputs: { target_blueprint: "$ref:prompt_style.asset", ...reference.length > 0 ? { reference } : {} },
|
|
16086
16647
|
params: genParams
|
|
16087
16648
|
});
|
|
16088
16649
|
return `$ref:${genId}.images#0`;
|
|
@@ -16139,7 +16700,7 @@ function scrubFloatSentences(text, floatDescs) {
|
|
|
16139
16700
|
return kept;
|
|
16140
16701
|
}
|
|
16141
16702
|
function sceneFloatDescs(scene) {
|
|
16142
|
-
const floats =
|
|
16703
|
+
const floats = z12.array(FloatingElement).safeParse(scene.floating_elements ?? []);
|
|
16143
16704
|
if (!floats.success) return [];
|
|
16144
16705
|
return floats.data.map((f) => f.description?.trim() ?? "").filter(Boolean);
|
|
16145
16706
|
}
|
|
@@ -16604,18 +17165,24 @@ function emitFlashHold(i, scene, slots, ctx, lengths, out, outAr, nodes, clips)
|
|
|
16604
17165
|
});
|
|
16605
17166
|
clips.push({ ref: `$ref:s${i}_clip.video`, scene_s: lengths.dur, out });
|
|
16606
17167
|
}
|
|
16607
|
-
function emitScreenScene(i, scene, lengths, out, outAr, nodes, clips) {
|
|
16608
|
-
const
|
|
16609
|
-
const
|
|
16610
|
-
|
|
16611
|
-
|
|
16612
|
-
|
|
16613
|
-
|
|
16614
|
-
|
|
16615
|
-
|
|
16616
|
-
|
|
16617
|
-
|
|
16618
|
-
|
|
17168
|
+
function emitScreenScene(i, scene, lengths, out, outAr, surfaceIngests, nodes, clips) {
|
|
17169
|
+
const regions = (scene.composition?.regions ?? []).filter((r) => Boolean(r) && typeof r === "object");
|
|
17170
|
+
const surfaceId = regions.find((r) => r.surface_id)?.surface_id;
|
|
17171
|
+
let refId = surfaceId ? surfaceIngests.get(surfaceId) : void 0;
|
|
17172
|
+
if (!refId) {
|
|
17173
|
+
const label = commentSafe((scene.summary || scene.start_frame_prompt || "the app screen").slice(0, 120));
|
|
17174
|
+
refId = `s${i}_screen_ref`;
|
|
17175
|
+
nodes.push({
|
|
17176
|
+
id: refId,
|
|
17177
|
+
type: "ingest",
|
|
17178
|
+
params: {
|
|
17179
|
+
source: "path",
|
|
17180
|
+
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]`,
|
|
17181
|
+
expect: "image"
|
|
17182
|
+
}
|
|
17183
|
+
});
|
|
17184
|
+
if (surfaceId) surfaceIngests.set(surfaceId, refId);
|
|
17185
|
+
}
|
|
16619
17186
|
nodes.push({
|
|
16620
17187
|
id: `s${i}_clip`,
|
|
16621
17188
|
type: "ffmpeg",
|
|
@@ -16859,9 +17426,16 @@ function makePresenterPresent(slots, canonical, opts = {}) {
|
|
|
16859
17426
|
return presence.has(sceneIndex);
|
|
16860
17427
|
};
|
|
16861
17428
|
}
|
|
16862
|
-
var PAUSE_GAP_S = 0.6;
|
|
16863
17429
|
var SEEDANCE_SAFE_MAX_S = SEEDANCE_DURATIONS.find((d) => d >= 10) ?? 10;
|
|
16864
17430
|
var PHRASE_MAX_S = SEEDANCE_SAFE_MAX_S;
|
|
17431
|
+
var PAUSE_GAP_S = 0.6;
|
|
17432
|
+
function isAdjacentShownCut(ln, lastShownScene, scenes) {
|
|
17433
|
+
if (!ln.shown || lastShownScene === null) return false;
|
|
17434
|
+
return ln.sceneIndex === lastShownScene + 1 && scenes[ln.sceneIndex]?.continues_previous !== true;
|
|
17435
|
+
}
|
|
17436
|
+
function breaksPhrase(cur, ln, lineCover, lineClipStart, scenes) {
|
|
17437
|
+
return cur.speaker !== ln.speaker || ln.start - cur.end > PAUSE_GAP_S || isAdjacentShownCut(ln, cur.lastShownScene, scenes) || Math.max(cur.coverEnd, lineCover) - Math.min(cur.clipStart, lineClipStart) > PHRASE_MAX_S;
|
|
17438
|
+
}
|
|
16865
17439
|
var JOIN_DEDUP_MAX_WORDS = 4;
|
|
16866
17440
|
function joinKey(word) {
|
|
16867
17441
|
return word.toLowerCase().replace(/[^\p{L}\p{N}]+/gu, "");
|
|
@@ -16904,10 +17478,7 @@ function collapseVoiceover(blueprint) {
|
|
|
16904
17478
|
const presenter = [...presenters][0];
|
|
16905
17479
|
return (speaker) => NARRATOR_SPEAKERS.has(speaker.toLowerCase()) ? presenter : speaker;
|
|
16906
17480
|
}
|
|
16907
|
-
function
|
|
16908
|
-
const casts = castIdSet(blueprint);
|
|
16909
|
-
const cameraOn = onCameraDialogue(blueprint);
|
|
16910
|
-
const sceneEndS = (i) => blueprint.scenes[i]?.end_s ?? blueprint.scenes[i]?.start_s ?? 0;
|
|
17481
|
+
function multiSpeakerScenes(blueprint, casts, cameraOn, canonical, presentStrict) {
|
|
16911
17482
|
const multiSpeaker = /* @__PURE__ */ new Set();
|
|
16912
17483
|
blueprint.scenes.forEach((scene, i) => {
|
|
16913
17484
|
const onCamAll = new Set(
|
|
@@ -16917,32 +17488,45 @@ function buildPhrases(blueprint, canonical, compositeScenes, presenterPresent, p
|
|
|
16917
17488
|
const effective = onCamPresent.length > 0 ? new Set(onCamPresent) : onCamAll;
|
|
16918
17489
|
if (effective.size >= 2) multiSpeaker.add(i);
|
|
16919
17490
|
});
|
|
16920
|
-
|
|
16921
|
-
|
|
17491
|
+
return multiSpeaker;
|
|
17492
|
+
}
|
|
17493
|
+
function lineClipWindow(ln, scenes) {
|
|
17494
|
+
if (!ln.shown) return { cover: ln.end, clipStart: ln.start };
|
|
17495
|
+
const sc = scenes[ln.sceneIndex];
|
|
17496
|
+
const sceneEnd = sc?.end_s ?? sc?.start_s ?? 0;
|
|
17497
|
+
return { cover: Math.max(ln.end, sceneEnd), clipStart: Math.min(ln.start, sc?.start_s ?? ln.start) };
|
|
17498
|
+
}
|
|
17499
|
+
function dialogueLines(blueprint, ctx) {
|
|
17500
|
+
return blueprint.scenes.flatMap((scene, sceneIndex) => {
|
|
17501
|
+
if (ctx.compositeScenes.has(sceneIndex)) return [];
|
|
17502
|
+
return (scene.dialogue ?? []).filter((l) => Boolean(l.line?.trim())).map((l) => {
|
|
16922
17503
|
const raw = l.speaker ?? "voiceover";
|
|
16923
|
-
const sp = canonical(raw);
|
|
16924
17504
|
const text = l.line.trim();
|
|
16925
17505
|
const start = l.start_s ?? scene.start_s ?? 0;
|
|
17506
|
+
const shown = l.on_camera !== false && !sceneIsAllGraphic(scene) && isOnCameraSpeaker(raw, ctx.casts, ctx.cameraOn) && !ctx.multiSpeaker.has(sceneIndex) && ctx.presenterPresent(ctx.canonical(raw), sceneIndex);
|
|
16926
17507
|
return {
|
|
16927
17508
|
sceneIndex,
|
|
16928
|
-
speaker:
|
|
16929
|
-
|
|
16930
|
-
// here (not a cutaway). A b-roll cutaway mid-phrase fails this and gets
|
|
16931
|
-
// its own clip while the phrase voice plays under it. An explicit
|
|
16932
|
-
// deconstruct voiceover stamp (`on_camera: false`) wins over element
|
|
16933
|
-
// presence — a speaker pictured in a photo is "present" but not talking.
|
|
16934
|
-
// An all-graphic composition (no camera region) is voiceover by
|
|
16935
|
-
// definition: nobody is on screen to lip-sync.
|
|
16936
|
-
shown: l.on_camera !== false && !sceneIsAllGraphic(scene) && isOnCameraSpeaker(raw, casts, cameraOn) && !multiSpeaker.has(sceneIndex) && presenterPresent(sp, sceneIndex),
|
|
17509
|
+
speaker: ctx.canonical(raw),
|
|
17510
|
+
shown,
|
|
16937
17511
|
start,
|
|
16938
|
-
// Real speech end. When the deconstruct gives no end_s, estimate it from
|
|
16939
|
-
// the words — NOT the scene end (which would fabricate continuity across
|
|
16940
|
-
// a long silent b-roll gap and wrongly merge two separate phrases).
|
|
16941
17512
|
end: l.end_s ?? start + estSpeechS(text),
|
|
16942
17513
|
text
|
|
16943
17514
|
};
|
|
16944
|
-
})
|
|
16945
|
-
).sort((a, b) => a.start - b.start);
|
|
17515
|
+
});
|
|
17516
|
+
}).sort((a, b) => a.start - b.start);
|
|
17517
|
+
}
|
|
17518
|
+
function buildPhrases(blueprint, canonical, compositeScenes, presenterPresent, presentStrict) {
|
|
17519
|
+
const casts = castIdSet(blueprint);
|
|
17520
|
+
const cameraOn = onCameraDialogue(blueprint);
|
|
17521
|
+
const multiSpeaker = multiSpeakerScenes(blueprint, casts, cameraOn, canonical, presentStrict);
|
|
17522
|
+
const lines = dialogueLines(blueprint, {
|
|
17523
|
+
compositeScenes,
|
|
17524
|
+
multiSpeaker,
|
|
17525
|
+
canonical,
|
|
17526
|
+
casts,
|
|
17527
|
+
cameraOn,
|
|
17528
|
+
presenterPresent
|
|
17529
|
+
});
|
|
16946
17530
|
const phrases = [];
|
|
16947
17531
|
let cur = null;
|
|
16948
17532
|
const flush = () => {
|
|
@@ -16960,12 +17544,8 @@ function buildPhrases(blueprint, canonical, compositeScenes, presenterPresent, p
|
|
|
16960
17544
|
cur = null;
|
|
16961
17545
|
};
|
|
16962
17546
|
for (const ln of lines) {
|
|
16963
|
-
const lineCover
|
|
16964
|
-
const
|
|
16965
|
-
const breakRun = !cur || cur.speaker !== ln.speaker || ln.start - cur.end > PAUSE_GAP_S || // Cap by SCENE COVERAGE span, not line end — a presenter run whose sliced scenes span
|
|
16966
|
-
// more than one Seedance clip splits into the next take here (at this scene's
|
|
16967
|
-
// boundary, never mid-scene), so no segment ever reads past the generated clip.
|
|
16968
|
-
Math.max(cur.coverEnd, lineCover) - Math.min(cur.clipStart, lineClipStart) > PHRASE_MAX_S;
|
|
17547
|
+
const { cover: lineCover, clipStart: lineClipStart } = lineClipWindow(ln, blueprint.scenes);
|
|
17548
|
+
const breakRun = !cur || breaksPhrase(cur, ln, lineCover, lineClipStart, blueprint.scenes);
|
|
16969
17549
|
if (breakRun || !cur) {
|
|
16970
17550
|
flush();
|
|
16971
17551
|
cur = {
|
|
@@ -16976,6 +17556,7 @@ function buildPhrases(blueprint, canonical, compositeScenes, presenterPresent, p
|
|
|
16976
17556
|
coverEnd: lineCover,
|
|
16977
17557
|
clipStart: lineClipStart,
|
|
16978
17558
|
texts: [ln.text],
|
|
17559
|
+
lastShownScene: ln.shown ? ln.sceneIndex : null,
|
|
16979
17560
|
shown: /* @__PURE__ */ new Set()
|
|
16980
17561
|
};
|
|
16981
17562
|
} else {
|
|
@@ -16983,6 +17564,7 @@ function buildPhrases(blueprint, canonical, compositeScenes, presenterPresent, p
|
|
|
16983
17564
|
cur.end = Math.max(cur.end, ln.end);
|
|
16984
17565
|
cur.coverEnd = Math.max(cur.coverEnd, lineCover);
|
|
16985
17566
|
cur.clipStart = Math.min(cur.clipStart, lineClipStart);
|
|
17567
|
+
if (ln.shown) cur.lastShownScene = ln.sceneIndex;
|
|
16986
17568
|
}
|
|
16987
17569
|
if (ln.shown) cur.shown.add(ln.sceneIndex);
|
|
16988
17570
|
}
|
|
@@ -17090,19 +17672,12 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
|
|
|
17090
17672
|
inputs: { clip: clipRef },
|
|
17091
17673
|
params: { args: audioExtractArgs(extractLen, speechOffset), outputs: { audio: { kind: "audio", ext: "mp3" } } }
|
|
17092
17674
|
});
|
|
17093
|
-
const convId =
|
|
17094
|
-
|
|
17095
|
-
|
|
17096
|
-
|
|
17097
|
-
inputs: { audio: `$ref:s${anchor}_voextract.audio`, voice_ref: `$ref:${voiceNode}.voice_id` },
|
|
17098
|
-
params: { model: FIXED_VOICE_CONVERT_MODEL, voice: "{{voice_ref}}" }
|
|
17099
|
-
});
|
|
17100
|
-
out.voTracks.push({
|
|
17101
|
-
slot: convId,
|
|
17102
|
-
ref: `$ref:${convId}.audio`,
|
|
17675
|
+
const convId = `${voiceNode}_conv`;
|
|
17676
|
+
out.nativeSegments.push({
|
|
17677
|
+
voiceNode,
|
|
17678
|
+
ref: `$ref:s${anchor}_voextract.audio`,
|
|
17103
17679
|
start_s: phrase.start_s,
|
|
17104
|
-
end_s: phrase.
|
|
17105
|
-
kind: "vo"
|
|
17680
|
+
end_s: phrase.start_s + extractLen
|
|
17106
17681
|
});
|
|
17107
17682
|
out.voSegments.push({
|
|
17108
17683
|
slot: convId,
|
|
@@ -17118,18 +17693,35 @@ function emitPhraseClip(phrase, voiceNode, env, nodes, out) {
|
|
|
17118
17693
|
est_speech_s: Math.round(estSpeechWindowS(phrase.text, phrase.start_s, phrase.end_s) * 100) / 100,
|
|
17119
17694
|
speech_words: wordCount(phrase.text)
|
|
17120
17695
|
});
|
|
17121
|
-
|
|
17122
|
-
|
|
17123
|
-
|
|
17124
|
-
|
|
17125
|
-
|
|
17696
|
+
registerMergedPhraseSlices(phrase, clipRef, clipStart, genDur, Boolean(chained), env, out);
|
|
17697
|
+
}
|
|
17698
|
+
function registerMergedPhraseSlices(phrase, clipRef, clipStart, genDur, chained, env, out) {
|
|
17699
|
+
const shown = [...phrase.shownScenes].sort((a, b) => a - b);
|
|
17700
|
+
let r = 0;
|
|
17701
|
+
let firstRegistered = true;
|
|
17702
|
+
while (r < shown.length) {
|
|
17703
|
+
const first = shown[r];
|
|
17704
|
+
let last = first;
|
|
17705
|
+
while (r + 1 < shown.length && shown[r + 1] === last + 1) last = shown[++r];
|
|
17706
|
+
r++;
|
|
17707
|
+
const firstSc = env.blueprint.scenes[first];
|
|
17708
|
+
if (!firstSc) continue;
|
|
17709
|
+
const firstStart = firstSc.start_s ?? clipStart;
|
|
17710
|
+
const rawOffset = firstStart - clipStart;
|
|
17711
|
+
const runEnd = env.blueprint.scenes[last]?.end_s ?? firstStart + sceneDurationS(firstSc);
|
|
17712
|
+
out.sceneSlice.set(first, {
|
|
17126
17713
|
clipRef,
|
|
17127
|
-
// Snap a sub-frame offset (line-start vs scene-start drift) to 0 so a
|
|
17128
|
-
//
|
|
17714
|
+
// Snap a sub-frame offset (line-start vs scene-start drift) to 0 so a run that tiles
|
|
17715
|
+
// the clip hits the whole-clip fast path instead of a needless re-encode + tiny shift.
|
|
17129
17716
|
offset: rawOffset < 0.05 ? 0 : rawOffset,
|
|
17130
|
-
len:
|
|
17131
|
-
clipDur: genDur
|
|
17717
|
+
len: Math.max(0.5, runEnd - firstStart),
|
|
17718
|
+
clipDur: genDur,
|
|
17719
|
+
...firstRegistered && chained ? { continuesFrame: true } : {}
|
|
17132
17720
|
});
|
|
17721
|
+
firstRegistered = false;
|
|
17722
|
+
for (let s = first + 1; s <= last; s++) {
|
|
17723
|
+
out.sceneSlice.set(s, { clipRef, offset: 0, len: 0, clipDur: genDur, skip: true });
|
|
17724
|
+
}
|
|
17133
17725
|
}
|
|
17134
17726
|
}
|
|
17135
17727
|
function emitPhraseTts(phrase, voiceNode, idx, used, nodes, out, languageCode) {
|
|
@@ -17272,7 +17864,7 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
|
|
|
17272
17864
|
return void 0;
|
|
17273
17865
|
}
|
|
17274
17866
|
if (!env.reuse && sceneIsFullScreenUi(scene, present)) {
|
|
17275
|
-
emitScreenScene(i, scene, lengths, lengths.out, env.outAr, nodes, out.clips);
|
|
17867
|
+
emitScreenScene(i, scene, lengths, lengths.out, env.outAr, env.surfaceIngests, nodes, out.clips);
|
|
17276
17868
|
return void 0;
|
|
17277
17869
|
}
|
|
17278
17870
|
const isCta = scene.narrative_role?.trim() === "cta" || isLast;
|
|
@@ -17284,7 +17876,8 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
|
|
|
17284
17876
|
emitFlashHold(i, scene, env.slots, ctx, lengths, lengths.out, env.outAr, nodes, out.clips);
|
|
17285
17877
|
return void 0;
|
|
17286
17878
|
}
|
|
17287
|
-
const
|
|
17879
|
+
const sharesPrevFrame = Boolean(scene.continues_previous && prevEndFrame);
|
|
17880
|
+
const first = sharesPrevFrame && prevEndFrame ? prevEndFrame : buildFrameRef(
|
|
17288
17881
|
"start",
|
|
17289
17882
|
scene.start_frame_asset?.url,
|
|
17290
17883
|
scene.start_frame_prompt,
|
|
@@ -17332,9 +17925,26 @@ function emitBrollScene(scene, i, isLast, env, nodes, out, prevEndFrame) {
|
|
|
17332
17925
|
out.nativeSegments
|
|
17333
17926
|
);
|
|
17334
17927
|
}
|
|
17335
|
-
out.clips.push(clip);
|
|
17928
|
+
out.clips.push(sharesPrevFrame ? { ...clip, continuesFrame: true } : clip);
|
|
17336
17929
|
return last;
|
|
17337
17930
|
}
|
|
17931
|
+
function emitPresenterSliceClip(i, slice, env, nodes, out) {
|
|
17932
|
+
if (slice.skip) return;
|
|
17933
|
+
const cont = slice.continuesFrame ? { continuesFrame: true } : {};
|
|
17934
|
+
const normDims = env.genAr !== env.outAr ? canvasDims(env.outAr) : void 0;
|
|
17935
|
+
const whole = slice.offset === 0 && Math.abs(slice.len - slice.clipDur) <= 0.05 && !normDims;
|
|
17936
|
+
if (whole) {
|
|
17937
|
+
out.clips.push({ ref: slice.clipRef, scene_s: slice.len, out: null, ...cont });
|
|
17938
|
+
return;
|
|
17939
|
+
}
|
|
17940
|
+
nodes.push({
|
|
17941
|
+
id: `s${i}_seg`,
|
|
17942
|
+
type: "ffmpeg",
|
|
17943
|
+
inputs: { clip: slice.clipRef },
|
|
17944
|
+
params: { args: trimArgs(slice.len, slice.offset, normDims), outputs: { video: { kind: "video", ext: "mp4" } } }
|
|
17945
|
+
});
|
|
17946
|
+
out.clips.push({ ref: `$ref:s${i}_seg.video`, scene_s: slice.len, out: null, ...cont });
|
|
17947
|
+
}
|
|
17338
17948
|
function buildTimeline(blueprint, slots, opts, nodes) {
|
|
17339
17949
|
const reuse = opts.frames === "reuse";
|
|
17340
17950
|
const uiRouted = uiRoutedSceneSet(blueprint);
|
|
@@ -17407,22 +18017,7 @@ function buildTimeline(blueprint, slots, opts, nodes) {
|
|
|
17407
18017
|
}
|
|
17408
18018
|
const slice = out.sceneSlice.get(i);
|
|
17409
18019
|
if (slice) {
|
|
17410
|
-
|
|
17411
|
-
const whole = slice.offset === 0 && Math.abs(slice.len - slice.clipDur) <= 0.05 && !normDims;
|
|
17412
|
-
if (whole) {
|
|
17413
|
-
out.clips.push({ ref: slice.clipRef, scene_s: slice.len, out: null });
|
|
17414
|
-
} else {
|
|
17415
|
-
nodes.push({
|
|
17416
|
-
id: `s${i}_seg`,
|
|
17417
|
-
type: "ffmpeg",
|
|
17418
|
-
inputs: { clip: slice.clipRef },
|
|
17419
|
-
params: {
|
|
17420
|
-
args: trimArgs(slice.len, slice.offset, normDims),
|
|
17421
|
-
outputs: { video: { kind: "video", ext: "mp4" } }
|
|
17422
|
-
}
|
|
17423
|
-
});
|
|
17424
|
-
out.clips.push({ ref: `$ref:s${i}_seg.video`, scene_s: slice.len, out: null });
|
|
17425
|
-
}
|
|
18020
|
+
emitPresenterSliceClip(i, slice, env, nodes, out);
|
|
17426
18021
|
prevEndFrame = void 0;
|
|
17427
18022
|
return;
|
|
17428
18023
|
}
|
|
@@ -17479,25 +18074,25 @@ function buildSfxMusic(blueprint, nodes) {
|
|
|
17479
18074
|
}
|
|
17480
18075
|
return tracks;
|
|
17481
18076
|
}
|
|
17482
|
-
var OverlayStyle =
|
|
17483
|
-
var Overlay =
|
|
17484
|
-
text:
|
|
17485
|
-
appears_at_s:
|
|
17486
|
-
duration_s:
|
|
17487
|
-
position:
|
|
17488
|
-
role:
|
|
17489
|
-
animation:
|
|
17490
|
-
animation_detail:
|
|
18077
|
+
var OverlayStyle = z12.object({ color_hex: z12.string().optional(), background: z12.string().optional(), size: z12.string().optional() }).loose();
|
|
18078
|
+
var Overlay = z12.object({
|
|
18079
|
+
text: z12.string().optional(),
|
|
18080
|
+
appears_at_s: z12.number().optional(),
|
|
18081
|
+
duration_s: z12.number().optional(),
|
|
18082
|
+
position: z12.string().optional(),
|
|
18083
|
+
role: z12.string().optional(),
|
|
18084
|
+
animation: z12.string().optional(),
|
|
18085
|
+
animation_detail: z12.string().optional(),
|
|
17491
18086
|
style: OverlayStyle.optional()
|
|
17492
18087
|
}).loose();
|
|
17493
|
-
var FloatingElement =
|
|
17494
|
-
kind:
|
|
17495
|
-
description:
|
|
17496
|
-
brand_name:
|
|
17497
|
-
what_it_represents:
|
|
17498
|
-
appears_at_s:
|
|
17499
|
-
duration_s:
|
|
17500
|
-
position:
|
|
18088
|
+
var FloatingElement = z12.object({
|
|
18089
|
+
kind: z12.string().optional(),
|
|
18090
|
+
description: z12.string().optional(),
|
|
18091
|
+
brand_name: z12.string().nullish(),
|
|
18092
|
+
what_it_represents: z12.string().optional(),
|
|
18093
|
+
appears_at_s: z12.number().optional(),
|
|
18094
|
+
duration_s: z12.number().optional(),
|
|
18095
|
+
position: z12.string().optional()
|
|
17501
18096
|
}).loose();
|
|
17502
18097
|
function escapeHtml(s) {
|
|
17503
18098
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -17529,7 +18124,7 @@ function positionClass(position) {
|
|
|
17529
18124
|
function collectCaptions(blueprint) {
|
|
17530
18125
|
return blueprint.scenes.flatMap((scene) => {
|
|
17531
18126
|
const sceneStart = scene.start_s ?? 0;
|
|
17532
|
-
const overlays =
|
|
18127
|
+
const overlays = z12.array(Overlay).safeParse(scene.overlays ?? []);
|
|
17533
18128
|
return overlays.success ? overlays.data.filter((ov) => Boolean(ov.text?.trim())).map((ov) => {
|
|
17534
18129
|
const at = ov.appears_at_s ?? sceneStart;
|
|
17535
18130
|
return { text: ov.text.trim(), at, end: at + (ov.duration_s ?? 2.5), ov };
|
|
@@ -17609,7 +18204,7 @@ function collectFloatWindows(blueprint, uiRouted) {
|
|
|
17609
18204
|
const windows = /* @__PURE__ */ new Map();
|
|
17610
18205
|
blueprint.scenes.forEach((scene, i) => {
|
|
17611
18206
|
const sceneStart = scene.start_s ?? 0;
|
|
17612
|
-
const floats =
|
|
18207
|
+
const floats = z12.array(FloatingElement).safeParse(scene.floating_elements ?? []);
|
|
17613
18208
|
if (!floats.success) return;
|
|
17614
18209
|
for (const fe of floats.data) {
|
|
17615
18210
|
const at = fe.appears_at_s ?? sceneStart;
|
|
@@ -17708,25 +18303,40 @@ function lastSceneEnd(blueprint) {
|
|
|
17708
18303
|
for (const s of blueprint.scenes) end = Math.max(end, s.end_s ?? 0);
|
|
17709
18304
|
return end > 0 ? end : 8;
|
|
17710
18305
|
}
|
|
17711
|
-
function
|
|
18306
|
+
function seamDropOps(clips, i, seam) {
|
|
18307
|
+
if (seam === "off") return null;
|
|
18308
|
+
if (seam === "head" && i > 0 && clips[i]?.continuesFrame) return "trim=start_frame=1,setpts=PTS-STARTPTS";
|
|
18309
|
+
if (seam === "tail" && clips[i + 1]?.continuesFrame) return "reverse,trim=start_frame=1,setpts=PTS-STARTPTS,reverse";
|
|
18310
|
+
return null;
|
|
18311
|
+
}
|
|
18312
|
+
function concatArgs(clips, seam) {
|
|
17712
18313
|
const inputs = [];
|
|
17713
|
-
|
|
17714
|
-
|
|
18314
|
+
const pre = [];
|
|
18315
|
+
const labels = [];
|
|
18316
|
+
clips.forEach((_, i) => {
|
|
17715
18317
|
inputs.push("-i", `{{in.c${i}}}`);
|
|
17716
|
-
|
|
17717
|
-
|
|
17718
|
-
|
|
18318
|
+
const ops = seamDropOps(clips, i, seam);
|
|
18319
|
+
if (ops) {
|
|
18320
|
+
pre.push(`[${i}:v]${ops}[c${i}]`);
|
|
18321
|
+
labels.push(`[c${i}]`);
|
|
18322
|
+
} else {
|
|
18323
|
+
labels.push(`[${i}:v]`);
|
|
18324
|
+
}
|
|
18325
|
+
});
|
|
18326
|
+
const graph = [...pre, `${labels.join("")}concat=n=${clips.length}:v=1:a=0[v]`].join(";");
|
|
18327
|
+
return [...inputs, "-filter_complex", graph, "-map", "[v]", "{{out.video}}"];
|
|
17719
18328
|
}
|
|
17720
18329
|
function clipInputLen(c) {
|
|
17721
18330
|
return c.scene_s + (c.out?.dur ?? 0);
|
|
17722
18331
|
}
|
|
17723
|
-
function xfadeSpineArgs(clips) {
|
|
18332
|
+
function xfadeSpineArgs(clips, seam) {
|
|
17724
18333
|
const n = clips.length;
|
|
17725
18334
|
const inputs = [];
|
|
17726
18335
|
const filt = [];
|
|
17727
18336
|
for (let i = 0; i < n; i++) {
|
|
17728
18337
|
inputs.push("-i", `{{in.c${i}}}`);
|
|
17729
|
-
|
|
18338
|
+
const ops = seamDropOps(clips, i, seam);
|
|
18339
|
+
filt.push(`[${i}:v]format=yuv420p,fps=30,setsar=1,settb=AVTB${ops ? `,${ops}` : ""}[c${i}]`);
|
|
17730
18340
|
}
|
|
17731
18341
|
let cur = "c0";
|
|
17732
18342
|
let accLen = clipInputLen(clips[0]);
|
|
@@ -17748,13 +18358,13 @@ function xfadeSpineArgs(clips) {
|
|
|
17748
18358
|
}
|
|
17749
18359
|
return [...inputs, "-filter_complex", filt.join(";"), "-map", "[v]", "{{out.video}}"];
|
|
17750
18360
|
}
|
|
17751
|
-
function buildSpine(clips, nodes) {
|
|
18361
|
+
function buildSpine(clips, seam, nodes) {
|
|
17752
18362
|
const inputs = {};
|
|
17753
18363
|
clips.forEach((c, i) => {
|
|
17754
18364
|
inputs[`c${i}`] = c.ref;
|
|
17755
18365
|
});
|
|
17756
18366
|
const hasTransition = clips.length > 1 && clips.some((c) => c.out);
|
|
17757
|
-
const args = hasTransition ? xfadeSpineArgs(clips) : concatArgs(clips
|
|
18367
|
+
const args = hasTransition ? xfadeSpineArgs(clips, seam) : concatArgs(clips, seam);
|
|
17758
18368
|
nodes.push({
|
|
17759
18369
|
id: "spine",
|
|
17760
18370
|
type: "ffmpeg",
|
|
@@ -17763,16 +18373,24 @@ function buildSpine(clips, nodes) {
|
|
|
17763
18373
|
});
|
|
17764
18374
|
return "$ref:spine.video";
|
|
17765
18375
|
}
|
|
17766
|
-
function
|
|
17767
|
-
const blueprint = VideoBlueprint.parse(input);
|
|
17768
|
-
injectHookPhysicality(blueprint);
|
|
17769
|
-
const elements = RecurringElements.parse(elementsInput);
|
|
17770
|
-
const nodes = [];
|
|
18376
|
+
function emitBlueprintIngests(opts, nodes) {
|
|
17771
18377
|
nodes.push({
|
|
17772
18378
|
id: "prompt",
|
|
17773
18379
|
type: "ingest",
|
|
17774
18380
|
params: { source: "path", path: opts.blueprintPath ?? "./prompt.json", expect: "json" }
|
|
17775
18381
|
});
|
|
18382
|
+
nodes.push({
|
|
18383
|
+
id: "prompt_style",
|
|
18384
|
+
type: "ingest",
|
|
18385
|
+
params: { source: "path", path: opts.blueprintStylePath ?? "./prompt.style.json", expect: "json" }
|
|
18386
|
+
});
|
|
18387
|
+
}
|
|
18388
|
+
function scaffoldVideoCanvas(input, elementsInput, opts) {
|
|
18389
|
+
const blueprint = VideoBlueprint.parse(input);
|
|
18390
|
+
injectHookPhysicality(blueprint);
|
|
18391
|
+
const elements = RecurringElements.parse(elementsInput);
|
|
18392
|
+
const nodes = [];
|
|
18393
|
+
emitBlueprintIngests(opts, nodes);
|
|
17776
18394
|
const slots = buildElementSlots(elements);
|
|
17777
18395
|
extendPresenceByPromptMentions(slots, blueprint);
|
|
17778
18396
|
slots.forEach((slot, i) => {
|
|
@@ -17784,7 +18402,7 @@ function scaffoldVideoCanvas(input, elementsInput, opts) {
|
|
|
17784
18402
|
});
|
|
17785
18403
|
buildElementSheets(slots, nodes);
|
|
17786
18404
|
const { clips, voTracks, vo_segments, talking_scenes } = buildTimeline(blueprint, slots, opts, nodes);
|
|
17787
|
-
let videoRef = buildSpine(clips, nodes);
|
|
18405
|
+
let videoRef = buildSpine(clips, opts.seamDedup ?? "head", nodes);
|
|
17788
18406
|
let videoNode = "spine";
|
|
17789
18407
|
const overlays = blueprint.scenes.flatMap((s) => s.overlays ?? []);
|
|
17790
18408
|
const floating = blueprint.scenes.flatMap((s) => s.floating_elements ?? []);
|
|
@@ -17995,8 +18613,8 @@ function buildMotionBoard(blueprint) {
|
|
|
17995
18613
|
const end_s = scene.end_s ?? start_s + sceneDurationS(scene);
|
|
17996
18614
|
cursor = end_s;
|
|
17997
18615
|
const spoken = sceneSpokenText(scene);
|
|
17998
|
-
const overlays =
|
|
17999
|
-
const floats =
|
|
18616
|
+
const overlays = z12.array(Overlay).safeParse(scene.overlays ?? []);
|
|
18617
|
+
const floats = z12.array(FloatingElement).safeParse(scene.floating_elements ?? []);
|
|
18000
18618
|
const graphics = [
|
|
18001
18619
|
...(overlays.success ? overlays.data : []).filter((ov) => ov.text?.trim()).map((ov) => ({
|
|
18002
18620
|
kind: "text",
|
|
@@ -18026,7 +18644,7 @@ function buildMotionBoard(blueprint) {
|
|
|
18026
18644
|
});
|
|
18027
18645
|
}
|
|
18028
18646
|
var VIDEO_GUIDE = [
|
|
18029
|
-
"Scaffolded by `baker canvas scaffold-video` \u2014 a runnable reproduction of your reference video, built like an editing timeline.
|
|
18647
|
+
"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`.",
|
|
18030
18648
|
"",
|
|
18031
18649
|
"WHAT TO DO NEXT:",
|
|
18032
18650
|
"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.",
|
|
@@ -18110,9 +18728,9 @@ function buildVideoTodo(report, overlayCount, floatingCount, opts, blueprint) {
|
|
|
18110
18728
|
voice_description: d.voice_description,
|
|
18111
18729
|
line: d.line
|
|
18112
18730
|
})),
|
|
18113
|
-
talking_head_note: "
|
|
18114
|
-
voice_note: "ONE voice per person: a single voice_select is reused across all that person's
|
|
18115
|
-
native_timing: "
|
|
18731
|
+
talking_head_note: "SHOT-NATIVE: a presenter shot is ONE Seedance clip (its line quoted in s<anchor>_clip's prompt + generate_audio) so lips+voice are generated together \u2014 no tts, no veed-lipsync. Two adjacent presenter shots at a hard cut are SEPARATE clips; a cutaway phrase (the presenter on camera, cut to b-roll, back on camera) stays one clip and slices its on-camera windows (s<i>_seg). Edit the line in the s<anchor>_clip prompt to re-author it. A pure-voiceover phrase (speaker never shown) is one ElevenLabs tts read instead.",
|
|
18732
|
+
voice_note: "ONE voice per person: a single voice_select is reused across all that person's shots (on-camera AND off \u2014 the deconstruct's `voiceover` label folds into the sole presenter). Every presenter clip's native audio is extracted and re-voiced to that brand voice through a SINGLE merged audio_voice_convert per speaker (<voice>_conv, eleven_multilingual_sts_v2, timing preserved so lips stay matched) \u2014 so timbre stays consistent across the separate shot clips. Set voice_select.voice_id's gender/language to match the creator.",
|
|
18733
|
+
native_timing: "Clips separate at COMPLETE BREAKS between shots, but the VOICE stays continuous where it should: a voiceover narration is ONE read across the b-roll it plays over, and a cutaway leaves the presenter's read continuous under the insert. Each clip is generated long enough for its estimated speech. `metadata.video.talking_scenes` carries each shot's scene_s vs est_speech_s. CAVEAT: a b-roll cutaway INSIDE a phrase lands at an approximate (proportional) time \u2014 Seedance exposes no word timing \u2014 so if a cutaway is off its beat, nudge the scene boundary (it's a starting point).",
|
|
18116
18734
|
craft: {
|
|
18117
18735
|
note: "Production-craft principles that raise every clip's realism. Full rationale: references/video-craft.md (production craft); references/script-craft.md + meta-ads-playbook for the hook/message layer.",
|
|
18118
18736
|
principles: [
|
|
@@ -18223,23 +18841,23 @@ function videoReport(input, elementsInput) {
|
|
|
18223
18841
|
|
|
18224
18842
|
// src/commands/canvas/composition-path.ts
|
|
18225
18843
|
import { existsSync as existsSync3 } from "fs";
|
|
18226
|
-
import
|
|
18844
|
+
import path10 from "path";
|
|
18227
18845
|
function resolveShippedCanvasDir(name, startDir, exists = existsSync3, maxDepth = 8) {
|
|
18228
|
-
const rel =
|
|
18846
|
+
const rel = path10.join("canvas", name);
|
|
18229
18847
|
let dir = startDir;
|
|
18230
18848
|
for (let i = 0; i < maxDepth; i++) {
|
|
18231
|
-
const candidate =
|
|
18232
|
-
if (exists(
|
|
18233
|
-
const parent =
|
|
18849
|
+
const candidate = path10.join(dir, rel);
|
|
18850
|
+
if (exists(path10.join(candidate, "meta.json"))) return candidate;
|
|
18851
|
+
const parent = path10.dirname(dir);
|
|
18234
18852
|
if (parent === dir) break;
|
|
18235
18853
|
dir = parent;
|
|
18236
18854
|
}
|
|
18237
|
-
return
|
|
18855
|
+
return path10.resolve(startDir, "../../../", rel);
|
|
18238
18856
|
}
|
|
18239
18857
|
|
|
18240
18858
|
// src/commands/canvas/gitignore.ts
|
|
18241
|
-
import { appendFile, readFile as
|
|
18242
|
-
import
|
|
18859
|
+
import { appendFile, readFile as readFile6 } from "fs/promises";
|
|
18860
|
+
import path11 from "path";
|
|
18243
18861
|
function missingGitignoreEntries(existing, entries) {
|
|
18244
18862
|
const present = new Set(
|
|
18245
18863
|
existing.split("\n").map((l) => l.trim().replace(/\/+$/, "")).filter((l) => l.length > 0 && !l.startsWith("#"))
|
|
@@ -18247,10 +18865,10 @@ function missingGitignoreEntries(existing, entries) {
|
|
|
18247
18865
|
return entries.filter((e) => !present.has(e.trim().replace(/\/+$/, "")));
|
|
18248
18866
|
}
|
|
18249
18867
|
async function ensureGitignore(dir, entries) {
|
|
18250
|
-
const file =
|
|
18868
|
+
const file = path11.join(dir, ".gitignore");
|
|
18251
18869
|
let existing;
|
|
18252
18870
|
try {
|
|
18253
|
-
existing = await
|
|
18871
|
+
existing = await readFile6(file, "utf8");
|
|
18254
18872
|
} catch {
|
|
18255
18873
|
return;
|
|
18256
18874
|
}
|
|
@@ -18289,7 +18907,7 @@ ONE PERSON, MULTIPLE LOOKS: if a single individual plays MULTIPLE personas or wa
|
|
|
18289
18907
|
For each kept element return: { "type": one of person|animal|product|logo|badge|location, "label": a short UPPER_SNAKE_CASE name (e.g. HERO, CREATOR_SKEPTIC, INSURANCE_CARD, LOGO), "description": a concrete reusable description to source/shoot the real asset \u2014 for a person/animal give a NEUTRAL castable role (e.g. "hero pet-owner, woman in her 30s" or "a small beagle"), NOT the original individual's literal face/identity: we RECAST with a FRESH person/animal, so never tell the agent to reuse the original. "expression": a living subject's typical expression or null, "cast_id": the global.cast id if it maps to one else null, "same_as": the label of another element this is the SAME individual as (different wardrobe/persona) else null, "scenes": the 0-based indices of ONLY the scenes where the element is ACTUALLY VISIBLE ON SCREEN \u2014 judged from that scene's start_frame_prompt / end_frame_prompt subjects and its action_detail, NOT from who is merely speaking. A narrator heard over b-roll is NOT present in that b-roll scene; a dog-running cutaway does NOT contain the couch creator just because she talks across it. Do NOT pad the list \u2014 an element wrongly listed in a scene makes the reproduction render the wrong subject there (e.g. the creator appearing in a pure-dog b-roll). When in doubt, leave a scene OUT. Output ONLY the JSON object.`;
|
|
18290
18908
|
async function loadAssetText2(ref, label) {
|
|
18291
18909
|
const r = ref;
|
|
18292
|
-
if (typeof r?.path === "string") return
|
|
18910
|
+
if (typeof r?.path === "string") return readFile7(r.path, "utf8");
|
|
18293
18911
|
if (typeof r?.url === "string") {
|
|
18294
18912
|
const res = await fetch(r.url);
|
|
18295
18913
|
if (!res.ok) throw new Error(`failed to fetch ${label} (${res.status})`);
|
|
@@ -18308,7 +18926,7 @@ async function loadTranscriptBestEffort(ref) {
|
|
|
18308
18926
|
async function stageCaptions(outDir, transcript) {
|
|
18309
18927
|
const text = transcript?.trim();
|
|
18310
18928
|
if (!text || text === "[]") return {};
|
|
18311
|
-
const compositionPath =
|
|
18929
|
+
const compositionPath = path12.join(outDir, "tiktok-captions-composition");
|
|
18312
18930
|
await cp(SHIPPED_CAPTIONS_DIR, compositionPath, { recursive: true });
|
|
18313
18931
|
return { compositionPath };
|
|
18314
18932
|
}
|
|
@@ -18326,12 +18944,12 @@ function patchCompositionHtml(html, dims) {
|
|
|
18326
18944
|
return html.replace(/(<meta\s+name="viewport"\s+content="width=)\d+(,\s*height=)\d+(")/i, `$1${dims.w}$2${dims.h}$3`).replace(/(width:\s*)\d+(px;\s*height:\s*)\d+(px;)/i, `$1${dims.w}$2${dims.h}$3`).replace(/(data-width=")\d+(")/i, `$1${dims.w}$2`).replace(/(data-height=")\d+(")/i, `$1${dims.h}$2`);
|
|
18327
18945
|
}
|
|
18328
18946
|
async function stampCompositionDims(compositionDir, dims) {
|
|
18329
|
-
const metaPath =
|
|
18330
|
-
const rawMeta = await
|
|
18331
|
-
await
|
|
18332
|
-
const htmlPath =
|
|
18333
|
-
const rawHtml = await
|
|
18334
|
-
await
|
|
18947
|
+
const metaPath = path12.join(compositionDir, "meta.json");
|
|
18948
|
+
const rawMeta = await readFile7(metaPath, "utf8");
|
|
18949
|
+
await writeFile3(metaPath, patchCompositionMeta(rawMeta, dims), "utf8");
|
|
18950
|
+
const htmlPath = path12.join(compositionDir, "index.html");
|
|
18951
|
+
const rawHtml = await readFile7(htmlPath, "utf8");
|
|
18952
|
+
await writeFile3(htmlPath, patchCompositionHtml(rawHtml, dims), "utf8");
|
|
18335
18953
|
}
|
|
18336
18954
|
function parseElements2(raw) {
|
|
18337
18955
|
const parsed = JSON.parse(raw);
|
|
@@ -18371,6 +18989,62 @@ function fail2(code, message) {
|
|
|
18371
18989
|
`);
|
|
18372
18990
|
process.exit(2);
|
|
18373
18991
|
}
|
|
18992
|
+
var VIDEO_EXT_BY_MIME = {
|
|
18993
|
+
"video/mp4": ".mp4",
|
|
18994
|
+
"video/quicktime": ".mov",
|
|
18995
|
+
"video/webm": ".webm",
|
|
18996
|
+
"video/x-matroska": ".mkv"
|
|
18997
|
+
};
|
|
18998
|
+
function referenceVideoExt(url, contentType) {
|
|
18999
|
+
const fromPath = path12.extname(new URL(url).pathname).toLowerCase();
|
|
19000
|
+
if (fromPath && fromPath.length <= 5) return fromPath;
|
|
19001
|
+
const mime = (contentType ?? "").split(";")[0]?.trim().toLowerCase();
|
|
19002
|
+
return mime && VIDEO_EXT_BY_MIME[mime] || ".mp4";
|
|
19003
|
+
}
|
|
19004
|
+
async function fileExists2(target) {
|
|
19005
|
+
return access2(target).then(
|
|
19006
|
+
() => true,
|
|
19007
|
+
() => false
|
|
19008
|
+
);
|
|
19009
|
+
}
|
|
19010
|
+
function videoSourceReference(blueprint, fileArg2) {
|
|
19011
|
+
const bp = blueprint ?? {};
|
|
19012
|
+
const durable = typeof bp.source?.url === "string" ? bp.source.url : void 0;
|
|
19013
|
+
const original = /^https?:\/\//i.test(fileArg2) ? fileArg2 : void 0;
|
|
19014
|
+
const brand = bp.global?.branding?.brand_name;
|
|
19015
|
+
return { url: durable ?? original, advertiser: typeof brand === "string" && brand.trim() ? brand.trim() : void 0 };
|
|
19016
|
+
}
|
|
19017
|
+
function videoDefinitionDescription(blueprint) {
|
|
19018
|
+
const g = (blueprint ?? {}).global ?? {};
|
|
19019
|
+
const notes = g.reproduction_notes;
|
|
19020
|
+
if (typeof notes === "string" && notes.trim()) return notes.trim();
|
|
19021
|
+
const product = g.branding?.product;
|
|
19022
|
+
return typeof product === "string" && product.trim() ? product.trim() : void 0;
|
|
19023
|
+
}
|
|
19024
|
+
async function materializeReferenceVideo(fileArg2) {
|
|
19025
|
+
if (!/^https?:\/\//i.test(fileArg2)) return path12.resolve(fileArg2);
|
|
19026
|
+
let res;
|
|
19027
|
+
try {
|
|
19028
|
+
res = await fetch(fileArg2);
|
|
19029
|
+
} catch (e) {
|
|
19030
|
+
throw new Error(`failed to download reference video: ${e instanceof Error ? e.message : String(e)}`);
|
|
19031
|
+
}
|
|
19032
|
+
if (!res.ok) throw new Error(`failed to download reference video (${res.status} ${res.statusText})`);
|
|
19033
|
+
const bytes = Buffer.from(await res.arrayBuffer());
|
|
19034
|
+
if (bytes.length === 0) throw new Error("reference video download was empty");
|
|
19035
|
+
const dest = path12.join(
|
|
19036
|
+
tmpdir2(),
|
|
19037
|
+
`baker-ref-${sha256Hex(bytes).slice(0, 16)}${referenceVideoExt(fileArg2, res.headers.get("content-type"))}`
|
|
19038
|
+
);
|
|
19039
|
+
await writeFile3(dest, bytes);
|
|
19040
|
+
return dest;
|
|
19041
|
+
}
|
|
19042
|
+
function resolveSeamDedup(raw) {
|
|
19043
|
+
if (raw === void 0) return "head";
|
|
19044
|
+
const v = String(raw);
|
|
19045
|
+
if (v === "head" || v === "tail" || v === "off") return v;
|
|
19046
|
+
throw new Error(`--seam-dedup must be "head", "tail", or "off" (got "${v}")`);
|
|
19047
|
+
}
|
|
18374
19048
|
function resolveModels2(args) {
|
|
18375
19049
|
const pick = (flag, kind, fallback) => args[flag] ? String(args[flag]) : resolveModel2(kind, fallback);
|
|
18376
19050
|
return {
|
|
@@ -18467,13 +19141,25 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18467
19141
|
description: "Turn a reference video into a runnable reproduction canvas in one command. Runs billed passes \u2014 video_deconstruct (the full scene-by-scene blueprint + transcript, baked to prompt.json as the editable 'prompt') and an AI selection of the video's RECURRING identity elements (person/animal/product/logo) \u2014 then scaffolds a pipeline where every scene boundary is a static-ad-grade frame (the blueprint as target_blueprint, a reference legend, the real frame as anchor) and each recurring element gets ONE shared [TODO] ingest slot wired into every frame it appears in. The clips feed Seedance an ultra-detailed motion brief (action, camera, dialogue, transcript). Edit prompt.json, drop the real source images, then `baker canvas run`."
|
|
18468
19142
|
},
|
|
18469
19143
|
args: {
|
|
18470
|
-
file: {
|
|
19144
|
+
file: {
|
|
19145
|
+
type: "positional",
|
|
19146
|
+
required: true,
|
|
19147
|
+
description: "Reference video \u2014 a local path OR an http(s) URL (e.g. a winning-ads link). A URL is downloaded for you; pass --slug or --out with it."
|
|
19148
|
+
},
|
|
18471
19149
|
out: { type: "string", description: "Output canvas path (default <video-dir>/<name>.video.canvas.json)" },
|
|
19150
|
+
slug: {
|
|
19151
|
+
type: "string",
|
|
19152
|
+
description: "Creative slug \u2014 writes the canvas to src/creatives/<slug>/<slug>.canvas.json (repo convention)"
|
|
19153
|
+
},
|
|
18472
19154
|
frames: { type: "string", description: '"generate" (default, anchored regen) or "reuse" (wire real frames in)' },
|
|
18473
19155
|
ambient: {
|
|
18474
19156
|
type: "boolean",
|
|
18475
19157
|
description: "Give silent b-roll scenes native diegetic ambient mixed deep under the music bed (off by default)"
|
|
18476
19158
|
},
|
|
19159
|
+
"seam-dedup": {
|
|
19160
|
+
type: "string",
|
|
19161
|
+
description: `How to dedup the frame two clips SHARE when a long shot is split for length: "head" (default, drop the second clip's first frame), "tail" (drop the first clip's last frame), or "off" (keep both).`
|
|
19162
|
+
},
|
|
18477
19163
|
"max-scenes": { type: "string", description: "Cap the number of scenes the deconstruct emits" },
|
|
18478
19164
|
"shot-threshold": {
|
|
18479
19165
|
type: "string",
|
|
@@ -18481,6 +19167,14 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18481
19167
|
},
|
|
18482
19168
|
language: { type: "string", description: "Transcript/dialogue language hint (e.g. fr, en)" },
|
|
18483
19169
|
focus: { type: "string", description: "Known provenance/emphasis to ground the deconstruct" },
|
|
19170
|
+
advertiser: {
|
|
19171
|
+
type: "string",
|
|
19172
|
+
description: "Source advertiser recorded in _definition.md (default: the brand the deconstruct identified)"
|
|
19173
|
+
},
|
|
19174
|
+
platform: {
|
|
19175
|
+
type: "string",
|
|
19176
|
+
description: "Ad platform for _definition.md (meta|google|linkedin|tiktok|youtube|x|other; default meta)"
|
|
19177
|
+
},
|
|
18484
19178
|
"deconstruct-model": { type: "string", description: "Override the video_deconstruct model id" },
|
|
18485
19179
|
"select-model": { type: "string", description: "Override the text_generate model id for element selection" },
|
|
18486
19180
|
"image-model": { type: "string", description: "Override the image_generate model id for frames" },
|
|
@@ -18495,11 +19189,33 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18495
19189
|
}
|
|
18496
19190
|
},
|
|
18497
19191
|
async run({ args }) {
|
|
18498
|
-
const
|
|
18499
|
-
const
|
|
18500
|
-
|
|
18501
|
-
|
|
18502
|
-
|
|
19192
|
+
const fileArg2 = String(args.file);
|
|
19193
|
+
const slug = args.slug ? String(args.slug) : void 0;
|
|
19194
|
+
if (slug && !isValidScaffoldSlug(slug)) {
|
|
19195
|
+
process.stderr.write(
|
|
19196
|
+
`${JSON.stringify({ ok: false, error: { code: "invalid_slug", message: "--slug must be lowercase kebab (a-z, 0-9, hyphens), max 100 chars" } }, null, 2)}
|
|
19197
|
+
`
|
|
19198
|
+
);
|
|
19199
|
+
process.exit(2);
|
|
19200
|
+
}
|
|
19201
|
+
const isUrl = /^https?:\/\//i.test(fileArg2);
|
|
19202
|
+
if (isUrl && !slug && !args.out) {
|
|
19203
|
+
return fail2(
|
|
19204
|
+
"missing_output_target",
|
|
19205
|
+
"When the reference is a URL, pass --slug (writes src/creatives/<slug>/) or --out <path> so the scaffolded canvas has a home in the repo."
|
|
19206
|
+
);
|
|
19207
|
+
}
|
|
19208
|
+
let videoPath;
|
|
19209
|
+
try {
|
|
19210
|
+
videoPath = await materializeReferenceVideo(fileArg2);
|
|
19211
|
+
} catch (e) {
|
|
19212
|
+
return fail2("download", e instanceof Error ? e.message : String(e));
|
|
19213
|
+
}
|
|
19214
|
+
const base = path12.basename(videoPath, path12.extname(videoPath));
|
|
19215
|
+
const outPath = args.out ? path12.resolve(String(args.out)) : slug ? path12.join(process.cwd(), "src", "creatives", slug, `${slug}.canvas.json`) : path12.join(path12.dirname(videoPath), `${base}.video.canvas.json`);
|
|
19216
|
+
const outDir = path12.dirname(outPath);
|
|
19217
|
+
const blueprintPath = path12.join(outDir, "prompt.json");
|
|
19218
|
+
const blueprintStylePath = path12.join(outDir, "prompt.style.json");
|
|
18503
19219
|
const frames = args.frames === "reuse" ? "reuse" : "generate";
|
|
18504
19220
|
const maxScenes = args["max-scenes"] ? Number(args["max-scenes"]) : void 0;
|
|
18505
19221
|
if (Number.isFinite(maxScenes)) {
|
|
@@ -18518,10 +19234,16 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18518
19234
|
shotCuts
|
|
18519
19235
|
});
|
|
18520
19236
|
const { blueprint, elements, transcript, creditsSpent } = await runAnalysisPasses(deconstructCanvas, selectModel);
|
|
18521
|
-
await
|
|
19237
|
+
await mkdir3(outDir, { recursive: true });
|
|
18522
19238
|
const annotated = annotateBlueprintWithElements(blueprint, elements);
|
|
18523
|
-
await
|
|
19239
|
+
await writeFile3(blueprintPath, `${JSON.stringify(annotated, null, 2)}
|
|
18524
19240
|
`, "utf8");
|
|
19241
|
+
await writeFile3(
|
|
19242
|
+
blueprintStylePath,
|
|
19243
|
+
`${JSON.stringify(slimBlueprintForFrameStyle(annotated), null, 2)}
|
|
19244
|
+
`,
|
|
19245
|
+
"utf8"
|
|
19246
|
+
);
|
|
18525
19247
|
let aspect;
|
|
18526
19248
|
try {
|
|
18527
19249
|
aspect = resolveAspect(
|
|
@@ -18539,12 +19261,12 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18539
19261
|
`
|
|
18540
19262
|
);
|
|
18541
19263
|
}
|
|
18542
|
-
const compositionDest =
|
|
19264
|
+
const compositionDest = path12.join(outDir, "video-overlay-composition");
|
|
18543
19265
|
await cp(SHIPPED_COMPOSITION_DIR, compositionDest, { recursive: true });
|
|
18544
19266
|
await stampCompositionDims(compositionDest, outDims);
|
|
18545
|
-
const indexPath =
|
|
19267
|
+
const indexPath = path12.join(compositionDest, "index.html");
|
|
18546
19268
|
const overlayHtml = buildOverlayHtml(blueprint, { captionsActive: Boolean(transcript) });
|
|
18547
|
-
const indexHtml = await
|
|
19269
|
+
const indexHtml = await readFile7(indexPath, "utf8");
|
|
18548
19270
|
const injected = indexHtml.replace("<!--OVERLAYS-->", () => overlayHtml);
|
|
18549
19271
|
if (injected === indexHtml && overlayHtml.trim()) {
|
|
18550
19272
|
fail2(
|
|
@@ -18552,17 +19274,19 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18552
19274
|
`video-overlay-composition/index.html is missing the <!--OVERLAYS--> marker \u2014 cannot inject the overlay layer`
|
|
18553
19275
|
);
|
|
18554
19276
|
}
|
|
18555
|
-
await
|
|
19277
|
+
await writeFile3(indexPath, injected, "utf8");
|
|
18556
19278
|
const captions = await stageCaptions(outDir, transcript);
|
|
18557
19279
|
if (captions.compositionPath) await stampCompositionDims(captions.compositionPath, outDims);
|
|
18558
19280
|
const opts = {
|
|
18559
19281
|
imageModel,
|
|
18560
19282
|
videoModel,
|
|
18561
|
-
overlayCompositionPath:
|
|
18562
|
-
captionsCompositionPath: captions.compositionPath ?
|
|
18563
|
-
blueprintPath:
|
|
19283
|
+
overlayCompositionPath: path12.relative(outDir, compositionDest),
|
|
19284
|
+
captionsCompositionPath: captions.compositionPath ? path12.relative(outDir, captions.compositionPath) : void 0,
|
|
19285
|
+
blueprintPath: path12.relative(outDir, blueprintPath),
|
|
19286
|
+
blueprintStylePath: path12.relative(outDir, blueprintStylePath),
|
|
18564
19287
|
frames,
|
|
18565
19288
|
ambient: Boolean(args.ambient),
|
|
19289
|
+
seamDedup: resolveSeamDedup(args["seam-dedup"]),
|
|
18566
19290
|
...args.aspect ? { aspect: String(args.aspect) } : {},
|
|
18567
19291
|
...args.resolution ? { resolution: String(args.resolution) } : {}
|
|
18568
19292
|
};
|
|
@@ -18581,7 +19305,7 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18581
19305
|
todo.blocking_validation_issues = validation.issues;
|
|
18582
19306
|
meta.todo = todo;
|
|
18583
19307
|
}
|
|
18584
|
-
await
|
|
19308
|
+
await writeFile3(outPath, `${JSON.stringify(canvas, null, 2)}
|
|
18585
19309
|
`, "utf8");
|
|
18586
19310
|
if (!validation.ok) {
|
|
18587
19311
|
process.stderr.write(
|
|
@@ -18601,12 +19325,42 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18601
19325
|
process.exit(2);
|
|
18602
19326
|
}
|
|
18603
19327
|
await ensureGitignore(process.cwd(), ["canvas/", ".context/"]);
|
|
19328
|
+
const sourceRef = videoSourceReference(blueprint, fileArg2);
|
|
19329
|
+
if (slug) {
|
|
19330
|
+
const definitionPath = path12.join(outDir, "_definition.md");
|
|
19331
|
+
if (!await fileExists2(definitionPath)) {
|
|
19332
|
+
await writeFile3(
|
|
19333
|
+
definitionPath,
|
|
19334
|
+
buildCreativeDefinition({
|
|
19335
|
+
title: titleFromSlug(slug),
|
|
19336
|
+
kind: "video",
|
|
19337
|
+
platform: resolvePlatform(args.platform ? String(args.platform) : void 0),
|
|
19338
|
+
formats: resolveFormats(aspect.outAr),
|
|
19339
|
+
sourceReferenceUrl: sourceRef.url,
|
|
19340
|
+
sourceAdvertiser: args.advertiser ? String(args.advertiser) : sourceRef.advertiser,
|
|
19341
|
+
sourceKind: "video",
|
|
19342
|
+
description: videoDefinitionDescription(blueprint)
|
|
19343
|
+
}),
|
|
19344
|
+
"utf8"
|
|
19345
|
+
);
|
|
19346
|
+
}
|
|
19347
|
+
}
|
|
19348
|
+
if (slug) {
|
|
19349
|
+
await syncCreativeDefinitionBestEffort({
|
|
19350
|
+
slug,
|
|
19351
|
+
title: titleFromSlug(slug),
|
|
19352
|
+
formats: [aspect.outAr],
|
|
19353
|
+
canvas,
|
|
19354
|
+
sourceReferenceUrl: sourceRef.url
|
|
19355
|
+
});
|
|
19356
|
+
}
|
|
18604
19357
|
process.stdout.write(
|
|
18605
19358
|
`${JSON.stringify(
|
|
18606
19359
|
{
|
|
18607
19360
|
ok: true,
|
|
18608
19361
|
canvas_path: outPath,
|
|
18609
19362
|
prompt_path: blueprintPath,
|
|
19363
|
+
source_reference: sourceRef.url,
|
|
18610
19364
|
composition_dir: compositionDest,
|
|
18611
19365
|
output: canvas.output,
|
|
18612
19366
|
frames_mode: frames,
|
|
@@ -18618,7 +19372,7 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18618
19372
|
run_estimated_credits: validation.estimatedCredits
|
|
18619
19373
|
},
|
|
18620
19374
|
checklist: {
|
|
18621
|
-
edit_prompt: `Edit ${
|
|
19375
|
+
edit_prompt: `Edit ${path12.basename(blueprintPath)} \u2014 the blueprint deconstructed from your video; rewrite it into the ad you want (cast, palette, copy, claims). Every scene frame reads it via target_blueprint.`,
|
|
18622
19376
|
recurring_elements_to_supply: report.elements,
|
|
18623
19377
|
voices_to_confirm: report.dialogue.map((d) => ({
|
|
18624
19378
|
scene: d.scene,
|
|
@@ -18632,6 +19386,13 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18632
19386
|
scenes_clamped_to_15s: report.clamped_scenes,
|
|
18633
19387
|
oversize_scenes: report.oversize_scenes,
|
|
18634
19388
|
overstuffed_scenes: report.overstuffed_scenes,
|
|
19389
|
+
// A photoreal on-camera person/animal on Seedance can trip ByteDance's
|
|
19390
|
+
// real-person-likeness filter (422 content_policy_blocked, NON-retryable — no
|
|
19391
|
+
// prompt reframe clears it). Surface the escape BEFORE the billed run so a
|
|
19392
|
+
// face-heavy ad isn't discovered broken mid-render.
|
|
19393
|
+
...report.elements.some((e) => e.type === "person" || e.type === "animal") && /seedance/i.test(videoModel) ? {
|
|
19394
|
+
content_policy_risk: "This ad has a photoreal on-camera cast generating on Seedance. ByteDance's real-person-likeness filter can reject a photoreal AI face with a NON-retryable 422 (content_policy_blocked) \u2014 no prompt change clears it. If clips fail that way, regenerate on Veo (re-run with `--video-model google/veo-3.1-fast`) or make the frame less photoreal."
|
|
19395
|
+
} : {},
|
|
18635
19396
|
note: "Drop ONE real source image at each el_* [TODO] (reused across every frame that element appears in), confirm each voice_select casting, then `baker canvas validate` and `baker canvas run`. Running generates many billed image/video/audio assets \u2014 it is not free."
|
|
18636
19397
|
}
|
|
18637
19398
|
},
|
|
@@ -18644,8 +19405,8 @@ var scaffoldVideoCommand = defineCommand90({
|
|
|
18644
19405
|
});
|
|
18645
19406
|
|
|
18646
19407
|
// src/commands/canvas/set-prompt.ts
|
|
18647
|
-
import { readFile as
|
|
18648
|
-
import
|
|
19408
|
+
import { readFile as readFile8, writeFile as writeFile4 } from "fs/promises";
|
|
19409
|
+
import path13 from "path";
|
|
18649
19410
|
import { defineCommand as defineCommand91 } from "citty";
|
|
18650
19411
|
function setNodePrompt(canvas, nodeId, text) {
|
|
18651
19412
|
const nodes = canvas?.nodes;
|
|
@@ -18673,17 +19434,17 @@ var setPromptCommand = defineCommand91({
|
|
|
18673
19434
|
"text-file": { type: "string", description: "Read the new prompt from a UTF-8 file (preserves accents/newlines)" }
|
|
18674
19435
|
},
|
|
18675
19436
|
async run({ args }) {
|
|
18676
|
-
const filePath =
|
|
19437
|
+
const filePath = path13.resolve(String(args.file));
|
|
18677
19438
|
let canvas;
|
|
18678
19439
|
try {
|
|
18679
|
-
canvas = JSON.parse(await
|
|
19440
|
+
canvas = JSON.parse(await readFile8(filePath, "utf8"));
|
|
18680
19441
|
} catch (e) {
|
|
18681
19442
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "parse", message: String(e) } }, null, 2)}
|
|
18682
19443
|
`);
|
|
18683
19444
|
process.exit(2);
|
|
18684
19445
|
}
|
|
18685
19446
|
let text;
|
|
18686
|
-
if (args["text-file"]) text = await
|
|
19447
|
+
if (args["text-file"]) text = await readFile8(path13.resolve(String(args["text-file"])), "utf8");
|
|
18687
19448
|
else if (args.text !== void 0) text = String(args.text);
|
|
18688
19449
|
else {
|
|
18689
19450
|
process.stderr.write(
|
|
@@ -18704,14 +19465,14 @@ var setPromptCommand = defineCommand91({
|
|
|
18704
19465
|
process.exit(2);
|
|
18705
19466
|
return;
|
|
18706
19467
|
}
|
|
18707
|
-
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated,
|
|
19468
|
+
const validation = await validateCanvasDeep(resolveRelativeCanvasPaths(updated, path13.dirname(filePath)), defaultRegistry());
|
|
18708
19469
|
if (!validation.ok) {
|
|
18709
19470
|
process.stderr.write(`${JSON.stringify({ ok: false, error: { code: "validation", issues: validation.issues } }, null, 2)}
|
|
18710
19471
|
`);
|
|
18711
19472
|
process.exit(2);
|
|
18712
19473
|
return;
|
|
18713
19474
|
}
|
|
18714
|
-
await
|
|
19475
|
+
await writeFile4(filePath, `${JSON.stringify(updated, null, 2)}
|
|
18715
19476
|
`, "utf8");
|
|
18716
19477
|
process.stdout.write(`${JSON.stringify({ ok: true, node: String(args.node), bytes: text.length }, null, 2)}
|
|
18717
19478
|
`);
|
|
@@ -18719,8 +19480,8 @@ var setPromptCommand = defineCommand91({
|
|
|
18719
19480
|
});
|
|
18720
19481
|
|
|
18721
19482
|
// src/commands/canvas/validate.ts
|
|
18722
|
-
import { readFile as
|
|
18723
|
-
import
|
|
19483
|
+
import { readFile as readFile9 } from "fs/promises";
|
|
19484
|
+
import path14 from "path";
|
|
18724
19485
|
import { defineCommand as defineCommand92 } from "citty";
|
|
18725
19486
|
var validateCommand = defineCommand92({
|
|
18726
19487
|
meta: {
|
|
@@ -18729,8 +19490,8 @@ var validateCommand = defineCommand92({
|
|
|
18729
19490
|
},
|
|
18730
19491
|
args: { file: { type: "positional", required: true, description: "Path to canvas JSON" } },
|
|
18731
19492
|
async run({ args }) {
|
|
18732
|
-
const filePath =
|
|
18733
|
-
const raw = await
|
|
19493
|
+
const filePath = path14.resolve(String(args.file));
|
|
19494
|
+
const raw = await readFile9(filePath, "utf8");
|
|
18734
19495
|
let parsed;
|
|
18735
19496
|
try {
|
|
18736
19497
|
parsed = JSON.parse(raw);
|
|
@@ -18740,7 +19501,7 @@ var validateCommand = defineCommand92({
|
|
|
18740
19501
|
`);
|
|
18741
19502
|
process.exit(2);
|
|
18742
19503
|
}
|
|
18743
|
-
parsed = resolveRelativeCanvasPaths(parsed,
|
|
19504
|
+
parsed = resolveRelativeCanvasPaths(parsed, path14.dirname(filePath));
|
|
18744
19505
|
const result = await validateCanvasDeep(parsed, defaultRegistry());
|
|
18745
19506
|
if (!result.ok) {
|
|
18746
19507
|
process.stderr.write(`${JSON.stringify({ ok: false, issues: result.issues }, null, 2)}
|
|
@@ -18799,7 +19560,7 @@ import { defineCommand as defineCommand95 } from "citty";
|
|
|
18799
19560
|
import { defineCommand as defineCommand94 } from "citty";
|
|
18800
19561
|
|
|
18801
19562
|
// src/commands/images/api.ts
|
|
18802
|
-
import { readFile as
|
|
19563
|
+
import { readFile as readFile10 } from "fs/promises";
|
|
18803
19564
|
import { extname } from "path";
|
|
18804
19565
|
var imageProcessingTimeoutMs = 18e4;
|
|
18805
19566
|
var imageReadyPollIntervalMs = 2e3;
|
|
@@ -18813,7 +19574,7 @@ var mimeMap = {
|
|
|
18813
19574
|
".avif": "image/avif"
|
|
18814
19575
|
};
|
|
18815
19576
|
var defaultImageApiDeps = {
|
|
18816
|
-
readFile:
|
|
19577
|
+
readFile: readFile10,
|
|
18817
19578
|
post: apiPost,
|
|
18818
19579
|
get: apiGet,
|
|
18819
19580
|
sleep: (ms) => new Promise((resolve5) => setTimeout(resolve5, ms))
|
|
@@ -18877,6 +19638,16 @@ registerSchema({
|
|
|
18877
19638
|
type: "string",
|
|
18878
19639
|
description: "Optional URL of the original reference ad",
|
|
18879
19640
|
required: false
|
|
19641
|
+
},
|
|
19642
|
+
slug: {
|
|
19643
|
+
type: "string",
|
|
19644
|
+
description: "Creative slug (src/creatives/<slug>/) \u2014 attaches the image to that creative's row",
|
|
19645
|
+
required: false
|
|
19646
|
+
},
|
|
19647
|
+
runId: {
|
|
19648
|
+
type: "string",
|
|
19649
|
+
description: "Canvas run id (r_\u2026) of the approved generation to pin as published",
|
|
19650
|
+
required: false
|
|
18880
19651
|
}
|
|
18881
19652
|
}
|
|
18882
19653
|
});
|
|
@@ -18886,6 +19657,13 @@ function detectCreativeContentType(filePath) {
|
|
|
18886
19657
|
unsupportedMessage: "Unsupported creative image extension. Use PNG, JPG, or WebP."
|
|
18887
19658
|
});
|
|
18888
19659
|
}
|
|
19660
|
+
function chatIdFromEnv() {
|
|
19661
|
+
try {
|
|
19662
|
+
return getEnv().BAKER_CHAT_ID || void 0;
|
|
19663
|
+
} catch {
|
|
19664
|
+
return void 0;
|
|
19665
|
+
}
|
|
19666
|
+
}
|
|
18889
19667
|
function parseOptionalUrl(value) {
|
|
18890
19668
|
if (value === void 0 || value.trim() === "") {
|
|
18891
19669
|
return void 0;
|
|
@@ -18916,7 +19694,11 @@ async function publishCreative(args, deps = defaultImageApiDeps) {
|
|
|
18916
19694
|
return publishImageAsCreative(deps, {
|
|
18917
19695
|
imageId: upload.imageId,
|
|
18918
19696
|
title,
|
|
18919
|
-
sourceReferenceUrl
|
|
19697
|
+
sourceReferenceUrl,
|
|
19698
|
+
slug: args.slug,
|
|
19699
|
+
runId: args.runId,
|
|
19700
|
+
// Attribute the publish to the driving chat (injected by the bridge).
|
|
19701
|
+
chatId: chatIdFromEnv()
|
|
18920
19702
|
});
|
|
18921
19703
|
}
|
|
18922
19704
|
var publishCommand = defineCommand94({
|
|
@@ -18932,6 +19714,16 @@ var publishCommand = defineCommand94({
|
|
|
18932
19714
|
type: "string",
|
|
18933
19715
|
description: "Optional URL of the original reference ad",
|
|
18934
19716
|
required: false
|
|
19717
|
+
},
|
|
19718
|
+
slug: {
|
|
19719
|
+
type: "string",
|
|
19720
|
+
description: "Creative slug (src/creatives/<slug>/) \u2014 attaches the image to that creative's row",
|
|
19721
|
+
required: false
|
|
19722
|
+
},
|
|
19723
|
+
runId: {
|
|
19724
|
+
type: "string",
|
|
19725
|
+
description: "Canvas run id (r_\u2026) of the approved generation to pin as published",
|
|
19726
|
+
required: false
|
|
18935
19727
|
}
|
|
18936
19728
|
},
|
|
18937
19729
|
run: async ({ args }) => {
|
|
@@ -18950,7 +19742,9 @@ var publishCommand = defineCommand94({
|
|
|
18950
19742
|
file,
|
|
18951
19743
|
title,
|
|
18952
19744
|
context: args.context,
|
|
18953
|
-
sourceReferenceUrl: args.sourceReferenceUrl
|
|
19745
|
+
sourceReferenceUrl: args.sourceReferenceUrl,
|
|
19746
|
+
slug: args.slug,
|
|
19747
|
+
runId: args.runId
|
|
18954
19748
|
});
|
|
18955
19749
|
writeJson({ ok: true, data });
|
|
18956
19750
|
} catch (err) {
|
|
@@ -19810,7 +20604,7 @@ function cropSprite(input, region) {
|
|
|
19810
20604
|
|
|
19811
20605
|
// src/lib/image/io.ts
|
|
19812
20606
|
import { randomBytes } from "crypto";
|
|
19813
|
-
import { glob as fsGlob, readFile as
|
|
20607
|
+
import { glob as fsGlob, readFile as readFile11, rename, stat as stat2, writeFile as writeFile5 } from "fs/promises";
|
|
19814
20608
|
import { dirname as dirname2, extname as extname2, join as join3, resolve as resolve4 } from "path";
|
|
19815
20609
|
var REMOTE_RE = /^https?:\/\//i;
|
|
19816
20610
|
var GLOB_RE = /[*?[\]{}]/;
|
|
@@ -19846,11 +20640,11 @@ async function readImageBuffer(pathOrUrl) {
|
|
|
19846
20640
|
}
|
|
19847
20641
|
return Buffer.from(await response.arrayBuffer());
|
|
19848
20642
|
}
|
|
19849
|
-
return
|
|
20643
|
+
return readFile11(pathOrUrl);
|
|
19850
20644
|
}
|
|
19851
|
-
async function isDirectory(
|
|
20645
|
+
async function isDirectory(path15) {
|
|
19852
20646
|
try {
|
|
19853
|
-
const s = await stat2(
|
|
20647
|
+
const s = await stat2(path15);
|
|
19854
20648
|
return s.isDirectory();
|
|
19855
20649
|
} catch {
|
|
19856
20650
|
return false;
|
|
@@ -19869,7 +20663,7 @@ async function atomicWrite(targetPath, data) {
|
|
|
19869
20663
|
const absolute = resolve4(targetPath);
|
|
19870
20664
|
const dir = dirname2(absolute);
|
|
19871
20665
|
const tmp = join3(dir, `.baker-image-${randomBytes(8).toString("hex")}.tmp`);
|
|
19872
|
-
await
|
|
20666
|
+
await writeFile5(tmp, data);
|
|
19873
20667
|
await rename(tmp, absolute);
|
|
19874
20668
|
}
|
|
19875
20669
|
|
|
@@ -20212,7 +21006,7 @@ var findCommand = defineCommand108({
|
|
|
20212
21006
|
});
|
|
20213
21007
|
|
|
20214
21008
|
// src/commands/images/generate.ts
|
|
20215
|
-
import { readFile as
|
|
21009
|
+
import { readFile as readFile12 } from "fs/promises";
|
|
20216
21010
|
import { defineCommand as defineCommand109 } from "citty";
|
|
20217
21011
|
import sharp2 from "sharp";
|
|
20218
21012
|
var GENERATE_TIMEOUT_MS = 18e4;
|
|
@@ -20302,7 +21096,7 @@ async function resolveReferences(spec) {
|
|
|
20302
21096
|
}
|
|
20303
21097
|
let raw;
|
|
20304
21098
|
try {
|
|
20305
|
-
raw = await
|
|
21099
|
+
raw = await readFile12(entry);
|
|
20306
21100
|
} catch {
|
|
20307
21101
|
throw new ApiError("VALIDATION_ERROR", `Reference file not found: ${entry}`);
|
|
20308
21102
|
}
|
|
@@ -23912,121 +24706,11 @@ var schemaCommand = defineCommand147({
|
|
|
23912
24706
|
}
|
|
23913
24707
|
});
|
|
23914
24708
|
|
|
23915
|
-
// src/commands/tags/index.ts
|
|
23916
|
-
import { defineCommand as defineCommand148 } from "citty";
|
|
23917
|
-
|
|
23918
|
-
// src/commands/tags/shared.ts
|
|
23919
|
-
function failApi3(err) {
|
|
23920
|
-
if (err instanceof ApiError) {
|
|
23921
|
-
writeJson({ ok: false, error: { code: err.code, message: err.message } });
|
|
23922
|
-
process.exit(1);
|
|
23923
|
-
}
|
|
23924
|
-
if (err instanceof Error) {
|
|
23925
|
-
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: err.message } });
|
|
23926
|
-
process.exit(1);
|
|
23927
|
-
}
|
|
23928
|
-
writeJson({ ok: false, error: { code: "INTERNAL_ERROR", message: "Unexpected error" } });
|
|
23929
|
-
process.exit(1);
|
|
23930
|
-
}
|
|
23931
|
-
function renderEffectiveEntry(entry) {
|
|
23932
|
-
const parts = [entry.ref, entry.type];
|
|
23933
|
-
if (entry.identifier !== void 0) {
|
|
23934
|
-
parts.push(entry.identifier);
|
|
23935
|
-
}
|
|
23936
|
-
if (entry.staged !== void 0) {
|
|
23937
|
-
parts.push(`[staged: ${entry.staged}]`);
|
|
23938
|
-
}
|
|
23939
|
-
const secretBits = [
|
|
23940
|
-
...entry.secretsSet.map((field) => `${field} \u2713 set`),
|
|
23941
|
-
...entry.secretsPending.map((field) => `${field} \u23F3 pending`)
|
|
23942
|
-
];
|
|
23943
|
-
if (secretBits.length > 0) {
|
|
23944
|
-
parts.push(`[secrets: ${secretBits.join(", ")}]`);
|
|
23945
|
-
}
|
|
23946
|
-
return parts.join(" ");
|
|
23947
|
-
}
|
|
23948
|
-
|
|
23949
|
-
// src/commands/tags/index.ts
|
|
23950
|
-
registerSchema({
|
|
23951
|
-
command: "tags.list",
|
|
23952
|
-
description: "Effective marketing tags for this chat: production tags overlaid with the changes staged in this chat. The printed refs (tag ids or tag_temp_*) are exactly what flow side-effect tagIds should reference. Read-only \u2014 every tag change (create, edit, delete) goes through the request_tag_input approval tool.",
|
|
23953
|
-
args: {
|
|
23954
|
-
json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list", required: false }
|
|
23955
|
-
}
|
|
23956
|
-
});
|
|
23957
|
-
async function listTags(json) {
|
|
23958
|
-
try {
|
|
23959
|
-
const chatId = requireChatId();
|
|
23960
|
-
const response = await apiPost("/api/tags/list", { chatId });
|
|
23961
|
-
if (json) {
|
|
23962
|
-
writeJson({ ok: true, data: response });
|
|
23963
|
-
return;
|
|
23964
|
-
}
|
|
23965
|
-
if (response.tags.length === 0) {
|
|
23966
|
-
process.stdout.write("No tags configured. Propose one with the request_tag_input tool (baker_ui).\n");
|
|
23967
|
-
return;
|
|
23968
|
-
}
|
|
23969
|
-
process.stdout.write(`${response.tags.map(renderEffectiveEntry).join("\n")}
|
|
23970
|
-
`);
|
|
23971
|
-
} catch (err) {
|
|
23972
|
-
failApi3(err);
|
|
23973
|
-
}
|
|
23974
|
-
}
|
|
23975
|
-
var listCommand7 = defineCommand148({
|
|
23976
|
-
meta: {
|
|
23977
|
-
name: "list",
|
|
23978
|
-
description: "Effective tags for this chat (production + staged). Refs printed here are what flow side-effect tagIds should use. Example: baker tags list"
|
|
23979
|
-
},
|
|
23980
|
-
args: { json: { type: "boolean", description: "Print the raw JSON envelope instead of the readable list" } },
|
|
23981
|
-
run: async ({ args }) => {
|
|
23982
|
-
await listTags(args.json === true);
|
|
23983
|
-
}
|
|
23984
|
-
});
|
|
23985
|
-
async function listDraft3() {
|
|
23986
|
-
try {
|
|
23987
|
-
const chatId = requireChatId();
|
|
23988
|
-
const response = await apiPost("/api/tags/draft", { chatId });
|
|
23989
|
-
writeJson({ ok: true, data: response });
|
|
23990
|
-
} catch (err) {
|
|
23991
|
-
failApi3(err);
|
|
23992
|
-
}
|
|
23993
|
-
}
|
|
23994
|
-
var draftCommand3 = defineCommand148({
|
|
23995
|
-
meta: {
|
|
23996
|
-
name: "draft",
|
|
23997
|
-
description: "Review the tag changes staged in this chat (read-only). Staged changes were approved via request_tag_input and apply when the chat is published; to amend or drop one, propose a follow-up change through the same tool (a delete on a tag_temp_* ref drops the staged create)."
|
|
23998
|
-
},
|
|
23999
|
-
run: async () => {
|
|
24000
|
-
await listDraft3();
|
|
24001
|
-
}
|
|
24002
|
-
});
|
|
24003
|
-
var tagsCommand3 = defineCommand148({
|
|
24004
|
-
meta: {
|
|
24005
|
-
name: "tags",
|
|
24006
|
-
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.
|
|
24007
|
-
|
|
24008
|
-
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.
|
|
24009
|
-
|
|
24010
|
-
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
|
-
|
|
24012
|
-
Examples:
|
|
24013
|
-
baker tags list # production + staged, with secret status
|
|
24014
|
-
baker tags draft # review the staged changes awaiting publish`
|
|
24015
|
-
},
|
|
24016
|
-
subCommands: {
|
|
24017
|
-
list: listCommand7,
|
|
24018
|
-
draft: draftCommand3
|
|
24019
|
-
},
|
|
24020
|
-
run: async () => {
|
|
24021
|
-
await listTags(false);
|
|
24022
|
-
}
|
|
24023
|
-
});
|
|
24024
|
-
|
|
24025
24709
|
// src/commands/testimonials/index.ts
|
|
24026
|
-
import { defineCommand as
|
|
24710
|
+
import { defineCommand as defineCommand151 } from "citty";
|
|
24027
24711
|
|
|
24028
24712
|
// src/commands/testimonials/get.ts
|
|
24029
|
-
import { defineCommand as
|
|
24713
|
+
import { defineCommand as defineCommand148 } from "citty";
|
|
24030
24714
|
registerSchema({
|
|
24031
24715
|
command: "testimonials.get",
|
|
24032
24716
|
description: "Get a single testimonial by ID",
|
|
@@ -24034,7 +24718,7 @@ registerSchema({
|
|
|
24034
24718
|
id: { type: "string", description: "Testimonial ID", required: true }
|
|
24035
24719
|
}
|
|
24036
24720
|
});
|
|
24037
|
-
var getCommand4 =
|
|
24721
|
+
var getCommand4 = defineCommand148({
|
|
24038
24722
|
meta: { name: "get", description: "Get a single testimonial by ID. Example: baker testimonials get j571abc123" },
|
|
24039
24723
|
args: {
|
|
24040
24724
|
id: { type: "positional", description: "Testimonial ID", required: false },
|
|
@@ -24071,7 +24755,7 @@ var getCommand4 = defineCommand149({
|
|
|
24071
24755
|
});
|
|
24072
24756
|
|
|
24073
24757
|
// src/commands/testimonials/list.ts
|
|
24074
|
-
import { defineCommand as
|
|
24758
|
+
import { defineCommand as defineCommand149 } from "citty";
|
|
24075
24759
|
registerSchema({
|
|
24076
24760
|
command: "testimonials.list",
|
|
24077
24761
|
description: "List testimonials with optional filters.",
|
|
@@ -24101,7 +24785,7 @@ registerSchema({
|
|
|
24101
24785
|
limit: { type: "number", description: "Max results (default 50)", required: false, default: 50 }
|
|
24102
24786
|
}
|
|
24103
24787
|
});
|
|
24104
|
-
var
|
|
24788
|
+
var listCommand7 = defineCommand149({
|
|
24105
24789
|
meta: {
|
|
24106
24790
|
name: "list",
|
|
24107
24791
|
description: "List testimonials with optional filters. Example: baker testimonials list --source google --sentiment positive"
|
|
@@ -24150,7 +24834,7 @@ var listCommand8 = defineCommand150({
|
|
|
24150
24834
|
});
|
|
24151
24835
|
|
|
24152
24836
|
// src/commands/testimonials/search.ts
|
|
24153
|
-
import { defineCommand as
|
|
24837
|
+
import { defineCommand as defineCommand150 } from "citty";
|
|
24154
24838
|
registerSchema({
|
|
24155
24839
|
command: "testimonials.search",
|
|
24156
24840
|
description: "Search testimonials by text query. Uses hybrid BM25 + vector + reranking.",
|
|
@@ -24181,7 +24865,7 @@ registerSchema({
|
|
|
24181
24865
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
24182
24866
|
}
|
|
24183
24867
|
});
|
|
24184
|
-
var searchCommand2 =
|
|
24868
|
+
var searchCommand2 = defineCommand150({
|
|
24185
24869
|
meta: {
|
|
24186
24870
|
name: "search",
|
|
24187
24871
|
description: "Semantic search testimonials by text query. Uses hybrid BM25 + vector + reranking. Example: baker testimonials search 'great service' --rating-min 4"
|
|
@@ -24252,10 +24936,10 @@ var searchCommand2 = defineCommand151({
|
|
|
24252
24936
|
});
|
|
24253
24937
|
|
|
24254
24938
|
// src/commands/testimonials/tags.ts
|
|
24255
|
-
var
|
|
24939
|
+
var tagsCommand3 = makeTagsCommand("testimonials", "testimonial", "/api/testimonials/tags");
|
|
24256
24940
|
|
|
24257
24941
|
// src/commands/testimonials/index.ts
|
|
24258
|
-
var testimonialsCommand =
|
|
24942
|
+
var testimonialsCommand = defineCommand151({
|
|
24259
24943
|
meta: {
|
|
24260
24944
|
name: "testimonials",
|
|
24261
24945
|
description: `Find and browse testimonials in Baker. Subcommands: search, get, list, tags.
|
|
@@ -24270,16 +24954,16 @@ Examples:
|
|
|
24270
24954
|
subCommands: {
|
|
24271
24955
|
get: getCommand4,
|
|
24272
24956
|
search: searchCommand2,
|
|
24273
|
-
list:
|
|
24274
|
-
tags:
|
|
24957
|
+
list: listCommand7,
|
|
24958
|
+
tags: tagsCommand3
|
|
24275
24959
|
}
|
|
24276
24960
|
});
|
|
24277
24961
|
|
|
24278
24962
|
// src/commands/videos/index.ts
|
|
24279
|
-
import { defineCommand as
|
|
24963
|
+
import { defineCommand as defineCommand156 } from "citty";
|
|
24280
24964
|
|
|
24281
24965
|
// src/commands/videos/delete.ts
|
|
24282
|
-
import { defineCommand as
|
|
24966
|
+
import { defineCommand as defineCommand152 } from "citty";
|
|
24283
24967
|
registerSchema({
|
|
24284
24968
|
command: "videos.delete",
|
|
24285
24969
|
description: "Delete a video by ID",
|
|
@@ -24293,7 +24977,7 @@ registerSchema({
|
|
|
24293
24977
|
}
|
|
24294
24978
|
}
|
|
24295
24979
|
});
|
|
24296
|
-
var deleteCommand3 =
|
|
24980
|
+
var deleteCommand3 = defineCommand152({
|
|
24297
24981
|
meta: {
|
|
24298
24982
|
name: "delete",
|
|
24299
24983
|
description: "Delete a video by ID. Use --dry-run to preview. Example: baker videos delete j571abc123 --dry-run"
|
|
@@ -24334,7 +25018,7 @@ var deleteCommand3 = defineCommand153({
|
|
|
24334
25018
|
});
|
|
24335
25019
|
|
|
24336
25020
|
// src/commands/videos/get.ts
|
|
24337
|
-
import { defineCommand as
|
|
25021
|
+
import { defineCommand as defineCommand153 } from "citty";
|
|
24338
25022
|
registerSchema({
|
|
24339
25023
|
command: "videos.get",
|
|
24340
25024
|
description: "Get a single video by ID",
|
|
@@ -24342,7 +25026,7 @@ registerSchema({
|
|
|
24342
25026
|
id: { type: "string", description: "Video ID", required: true }
|
|
24343
25027
|
}
|
|
24344
25028
|
});
|
|
24345
|
-
var getCommand5 =
|
|
25029
|
+
var getCommand5 = defineCommand153({
|
|
24346
25030
|
meta: { name: "get", description: "Get a single video by ID. Example: baker videos get j571abc123" },
|
|
24347
25031
|
args: {
|
|
24348
25032
|
id: { type: "positional", description: "Video ID", required: false },
|
|
@@ -24379,7 +25063,7 @@ var getCommand5 = defineCommand154({
|
|
|
24379
25063
|
});
|
|
24380
25064
|
|
|
24381
25065
|
// src/commands/videos/search.ts
|
|
24382
|
-
import { defineCommand as
|
|
25066
|
+
import { defineCommand as defineCommand154 } from "citty";
|
|
24383
25067
|
registerSchema({
|
|
24384
25068
|
command: "videos.search",
|
|
24385
25069
|
description: "Search videos by text query. Only returns ready videos.",
|
|
@@ -24389,7 +25073,7 @@ registerSchema({
|
|
|
24389
25073
|
tags: { type: "string", description: "Comma-separated tags to filter by", required: false }
|
|
24390
25074
|
}
|
|
24391
25075
|
});
|
|
24392
|
-
var searchCommand3 =
|
|
25076
|
+
var searchCommand3 = defineCommand154({
|
|
24393
25077
|
meta: {
|
|
24394
25078
|
name: "search",
|
|
24395
25079
|
description: "Semantic search videos by text query. Uses hybrid BM25 + vector + reranking. Example: baker videos search 'product demo' --tags tutorial"
|
|
@@ -24436,12 +25120,12 @@ var searchCommand3 = defineCommand155({
|
|
|
24436
25120
|
});
|
|
24437
25121
|
|
|
24438
25122
|
// src/commands/videos/tags.ts
|
|
24439
|
-
var
|
|
25123
|
+
var tagsCommand4 = makeTagsCommand("videos", "video", "/api/videos/tags");
|
|
24440
25124
|
|
|
24441
25125
|
// src/commands/videos/upload.ts
|
|
24442
|
-
import { readFile as
|
|
25126
|
+
import { readFile as readFile13, stat as stat3 } from "fs/promises";
|
|
24443
25127
|
import { extname as extname3 } from "path";
|
|
24444
|
-
import { defineCommand as
|
|
25128
|
+
import { defineCommand as defineCommand155 } from "citty";
|
|
24445
25129
|
var MIME_MAP = {
|
|
24446
25130
|
".mp4": "video/mp4",
|
|
24447
25131
|
".mov": "video/quicktime",
|
|
@@ -24475,7 +25159,7 @@ function detectContentType(filePath) {
|
|
|
24475
25159
|
}
|
|
24476
25160
|
return mime;
|
|
24477
25161
|
}
|
|
24478
|
-
var uploadCommand2 =
|
|
25162
|
+
var uploadCommand2 = defineCommand155({
|
|
24479
25163
|
meta: {
|
|
24480
25164
|
name: "upload",
|
|
24481
25165
|
description: "Upload a video file to Baker via Mux direct upload. Auto-detects content type. Example: baker videos upload ./demo.mp4"
|
|
@@ -24504,7 +25188,7 @@ var uploadCommand2 = defineCommand156({
|
|
|
24504
25188
|
return;
|
|
24505
25189
|
}
|
|
24506
25190
|
const { uploadUrl, videoId } = await apiPost("/api/videos/upload", {});
|
|
24507
|
-
const fileBuffer = await
|
|
25191
|
+
const fileBuffer = await readFile13(filePath);
|
|
24508
25192
|
const uploadResponse = await fetch(uploadUrl, {
|
|
24509
25193
|
method: "PUT",
|
|
24510
25194
|
headers: { "Content-Type": contentType },
|
|
@@ -24529,7 +25213,7 @@ var uploadCommand2 = defineCommand156({
|
|
|
24529
25213
|
});
|
|
24530
25214
|
|
|
24531
25215
|
// src/commands/videos/index.ts
|
|
24532
|
-
var videosCommand =
|
|
25216
|
+
var videosCommand = defineCommand156({
|
|
24533
25217
|
meta: {
|
|
24534
25218
|
name: "videos",
|
|
24535
25219
|
description: `Find and manage videos in Baker. Subcommands: search, get, upload, delete, tags.
|
|
@@ -24547,15 +25231,15 @@ Examples:
|
|
|
24547
25231
|
search: searchCommand3,
|
|
24548
25232
|
upload: uploadCommand2,
|
|
24549
25233
|
delete: deleteCommand3,
|
|
24550
|
-
tags:
|
|
25234
|
+
tags: tagsCommand4
|
|
24551
25235
|
}
|
|
24552
25236
|
});
|
|
24553
25237
|
|
|
24554
25238
|
// src/commands/winning-ads/index.ts
|
|
24555
|
-
import { defineCommand as
|
|
25239
|
+
import { defineCommand as defineCommand159 } from "citty";
|
|
24556
25240
|
|
|
24557
25241
|
// src/commands/winning-ads/advertisers.ts
|
|
24558
|
-
import { defineCommand as
|
|
25242
|
+
import { defineCommand as defineCommand157 } from "citty";
|
|
24559
25243
|
registerSchema({
|
|
24560
25244
|
command: "winning-ads.advertisers",
|
|
24561
25245
|
description: "Resolve a brand name to advertiser_id(s) in the ad-dna corpus \u2014 to find your OWN advertiser (to --exclude-advertiser) or a competitor (to --advertiser-id).",
|
|
@@ -24568,7 +25252,7 @@ registerSchema({
|
|
|
24568
25252
|
function identity(record) {
|
|
24569
25253
|
return record;
|
|
24570
25254
|
}
|
|
24571
|
-
var advertisersCommand2 =
|
|
25255
|
+
var advertisersCommand2 = defineCommand157({
|
|
24572
25256
|
meta: {
|
|
24573
25257
|
name: "advertisers",
|
|
24574
25258
|
description: 'Resolve a brand name to advertiser_id(s). Use it to find your own advertiser for --exclude-advertiser, or a competitor for --advertiser-id. Example: baker winning-ads advertisers "Deel" --output md'
|
|
@@ -24619,7 +25303,7 @@ var advertisersCommand2 = defineCommand158({
|
|
|
24619
25303
|
});
|
|
24620
25304
|
|
|
24621
25305
|
// src/commands/winning-ads/search.ts
|
|
24622
|
-
import { defineCommand as
|
|
25306
|
+
import { defineCommand as defineCommand158 } from "citty";
|
|
24623
25307
|
registerSchema({
|
|
24624
25308
|
command: "winning-ads.search",
|
|
24625
25309
|
description: "Search the ad-dna corpus of scored winning ads. Returns a lean shortlist (advertiser, summary, scores, media_url) to pick a reference to reproduce.",
|
|
@@ -24727,7 +25411,7 @@ function buildSearchBody(args) {
|
|
|
24727
25411
|
}
|
|
24728
25412
|
return body;
|
|
24729
25413
|
}
|
|
24730
|
-
var searchCommand4 =
|
|
25414
|
+
var searchCommand4 = defineCommand158({
|
|
24731
25415
|
meta: {
|
|
24732
25416
|
name: "search",
|
|
24733
25417
|
description: "Search winning reference ads. Example: baker winning-ads search 'B2B SaaS before/after AI automation' --platform meta --format static --winner-category winner --exclude-advertiser adv_123 --output md"
|
|
@@ -24839,7 +25523,7 @@ var searchCommand4 = defineCommand159({
|
|
|
24839
25523
|
});
|
|
24840
25524
|
|
|
24841
25525
|
// src/commands/winning-ads/index.ts
|
|
24842
|
-
var winningAdsCommand =
|
|
25526
|
+
var winningAdsCommand = defineCommand159({
|
|
24843
25527
|
meta: {
|
|
24844
25528
|
name: "winning-ads",
|
|
24845
25529
|
description: `Search the ad-dna corpus of scored "winning" ads for reference creatives to reproduce. Proxied through the Baker backend (BAKER_API_KEY) \u2014 no separate token needed.
|
|
@@ -24879,11 +25563,11 @@ function getCliVersion() {
|
|
|
24879
25563
|
}
|
|
24880
25564
|
|
|
24881
25565
|
// src/cli.ts
|
|
24882
|
-
var main =
|
|
25566
|
+
var main = defineCommand160({
|
|
24883
25567
|
meta: {
|
|
24884
25568
|
name: "baker",
|
|
24885
25569
|
version: getCliVersion(),
|
|
24886
|
-
description: `AI-agent CLI for finding and managing images, videos, testimonials, action items, scheduled actions,
|
|
25570
|
+
description: `AI-agent CLI for finding and managing images, videos, testimonials, action items, scheduled actions, and ad platform data in Baker.
|
|
24887
25571
|
|
|
24888
25572
|
Auth: Set BAKER_API_KEY (starts with bk_) and BAKER_API_URL environment variables.
|
|
24889
25573
|
Chat: Set BAKER_CHAT_ID for action and scheduled-action commands that stage changes against a chat.
|
|
@@ -24903,7 +25587,6 @@ Introspection: Run 'baker schema <command>' to inspect argument schemas.`
|
|
|
24903
25587
|
videos: videosCommand,
|
|
24904
25588
|
testimonials: testimonialsCommand,
|
|
24905
25589
|
canvas: canvasCommand,
|
|
24906
|
-
tags: tagsCommand3,
|
|
24907
25590
|
"winning-ads": winningAdsCommand,
|
|
24908
25591
|
mcp: mcpCommand,
|
|
24909
25592
|
schema: schemaCommand
|