@koda-sl/baker-cli 0.102.0 → 0.104.0
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 +5 -0
- package/dist/cli.js +372 -122
- package/dist/cli.js.map +1 -1
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -4279,6 +4279,11 @@ This CLI is designed for AI agent consumption. Key patterns:
|
|
|
4279
4279
|
10. **Parse the JSON envelope** — check `ok` field before accessing `data`
|
|
4280
4280
|
11. **Always check `query_context`** in keyword/research responses — it shows the actual location and language used. If `defaults_warning` is present, the query used US/English defaults. Always pass `--location` and `--language` explicitly when targeting a specific market.
|
|
4281
4281
|
|
|
4282
|
+
## Internal Notes
|
|
4283
|
+
|
|
4284
|
+
- **0.103.0**: `baker actions ...` commands now type their `/api/actions/...` request/response payloads from the shared `@baker/api` contract package (also consumed by the backend) instead of hand-written local interfaces. No command, flag, or output-shape changes.
|
|
4285
|
+
- **0.104.0**: `baker actions tags ...` commands now type their `/api/actions/tags...` request/response payloads from the shared `@baker/api` contract package instead of hand-written local interfaces. No command, flag, or output-shape changes.
|
|
4286
|
+
|
|
4282
4287
|
## Publishing
|
|
4283
4288
|
|
|
4284
4289
|
### Auto-publish (CI)
|
package/dist/cli.js
CHANGED
|
@@ -632,11 +632,11 @@ registerSchema({
|
|
|
632
632
|
async function recommendSkills(actionId) {
|
|
633
633
|
const skills = readSkillCatalog(process.cwd());
|
|
634
634
|
if (skills.length === 0) return [];
|
|
635
|
-
const response = await apiPost(
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
);
|
|
639
|
-
return response.data
|
|
635
|
+
const response = await apiPost("/api/actions/recommend-skills", {
|
|
636
|
+
actionId,
|
|
637
|
+
skills
|
|
638
|
+
});
|
|
639
|
+
return response.data.recommendations;
|
|
640
640
|
}
|
|
641
641
|
function buildHints(recommendations) {
|
|
642
642
|
if (recommendations.length === 0) {
|
|
@@ -684,9 +684,259 @@ var claimCommand = defineCommand({
|
|
|
684
684
|
}
|
|
685
685
|
});
|
|
686
686
|
|
|
687
|
+
// ../api/src/actions.ts
|
|
688
|
+
import { z as z2 } from "zod";
|
|
689
|
+
var actionStatusSchema = z2.enum(["pending", "in_progress", "completed", "discarded"]);
|
|
690
|
+
var actionPrioritySchema = z2.enum(["urgent", "high", "medium", "low"]);
|
|
691
|
+
var actionRefResolutionStatusSchema = z2.union([actionStatusSchema, z2.enum(["draft", "not_found"])]);
|
|
692
|
+
var actionDocSchema = z2.object({
|
|
693
|
+
_id: z2.string(),
|
|
694
|
+
_creationTime: z2.number(),
|
|
695
|
+
companyId: z2.string(),
|
|
696
|
+
name: z2.string(),
|
|
697
|
+
description: z2.string(),
|
|
698
|
+
status: actionStatusSchema,
|
|
699
|
+
priority: actionPrioritySchema.optional(),
|
|
700
|
+
requiresHuman: z2.boolean().optional(),
|
|
701
|
+
tags: z2.array(z2.string()).optional(),
|
|
702
|
+
createdByType: z2.enum(["user", "chat", "schedule"]),
|
|
703
|
+
createdByUserId: z2.string().optional(),
|
|
704
|
+
createdByChatId: z2.string().optional(),
|
|
705
|
+
createdByScheduleId: z2.string().optional(),
|
|
706
|
+
createdFromTempId: z2.string().optional(),
|
|
707
|
+
scheduledActionTrigger: z2.enum(["scheduled", "manual"]).optional(),
|
|
708
|
+
scheduledActionTriggerChatId: z2.string().optional(),
|
|
709
|
+
scheduledActionTriggerActorType: z2.enum(["user", "api", "system"]).optional(),
|
|
710
|
+
scheduledActionTriggerActorUserId: z2.string().optional(),
|
|
711
|
+
assigneeUserId: z2.string().optional(),
|
|
712
|
+
activeChatId: z2.string().optional(),
|
|
713
|
+
activeClaimedAt: z2.number().optional(),
|
|
714
|
+
completedAt: z2.number().optional(),
|
|
715
|
+
completedByType: z2.enum(["user", "chat"]).optional(),
|
|
716
|
+
completedByChatId: z2.string().optional(),
|
|
717
|
+
completedNote: z2.string().optional(),
|
|
718
|
+
discardedAt: z2.number().optional(),
|
|
719
|
+
discardedReason: z2.string().optional(),
|
|
720
|
+
createdAt: z2.number(),
|
|
721
|
+
updatedAt: z2.number(),
|
|
722
|
+
searchText: z2.string().optional()
|
|
723
|
+
});
|
|
724
|
+
var actionAssigneeSchema = z2.object({
|
|
725
|
+
userId: z2.string(),
|
|
726
|
+
name: z2.string(),
|
|
727
|
+
image: z2.string().optional()
|
|
728
|
+
});
|
|
729
|
+
var actionDepRefSchema = z2.object({
|
|
730
|
+
id: z2.string(),
|
|
731
|
+
name: z2.string(),
|
|
732
|
+
status: actionStatusSchema
|
|
733
|
+
});
|
|
734
|
+
var actionWithMetaSchema = z2.object({
|
|
735
|
+
action: actionDocSchema,
|
|
736
|
+
blockerCount: z2.number(),
|
|
737
|
+
openBlockerCount: z2.number(),
|
|
738
|
+
isBlocked: z2.boolean(),
|
|
739
|
+
openBlockingCount: z2.number(),
|
|
740
|
+
blockers: z2.array(actionDepRefSchema),
|
|
741
|
+
blocking: z2.array(actionDepRefSchema),
|
|
742
|
+
assignee: actionAssigneeSchema.nullable()
|
|
743
|
+
});
|
|
744
|
+
var actionBucketEntrySchema = z2.object({
|
|
745
|
+
id: z2.string(),
|
|
746
|
+
name: z2.string(),
|
|
747
|
+
description: z2.string(),
|
|
748
|
+
status: actionStatusSchema,
|
|
749
|
+
hint: z2.string(),
|
|
750
|
+
blockedBy: z2.array(z2.object({ id: z2.string(), name: z2.string() })).optional(),
|
|
751
|
+
claimedByChat: z2.object({ id: z2.string(), title: z2.string() }).optional(),
|
|
752
|
+
draftStatus: z2.enum(["in-progress", "completing", "discarding", "updating"]).optional()
|
|
753
|
+
});
|
|
754
|
+
var draftActionEntrySchema = z2.object({
|
|
755
|
+
tempId: z2.string(),
|
|
756
|
+
name: z2.string(),
|
|
757
|
+
description: z2.string(),
|
|
758
|
+
tags: z2.array(z2.string()),
|
|
759
|
+
hint: z2.string(),
|
|
760
|
+
draftStatus: z2.literal("creating")
|
|
761
|
+
});
|
|
762
|
+
var actionBucketsSchema = z2.object({
|
|
763
|
+
claimable: z2.array(actionBucketEntrySchema),
|
|
764
|
+
myClaims: z2.array(actionBucketEntrySchema),
|
|
765
|
+
blocked: z2.array(actionBucketEntrySchema),
|
|
766
|
+
claimedByOthers: z2.array(actionBucketEntrySchema),
|
|
767
|
+
completed: z2.array(actionBucketEntrySchema),
|
|
768
|
+
discarded: z2.array(actionBucketEntrySchema),
|
|
769
|
+
draftCreates: z2.array(draftActionEntrySchema)
|
|
770
|
+
});
|
|
771
|
+
var actionRefStatusResultSchema = z2.object({
|
|
772
|
+
ref: z2.string(),
|
|
773
|
+
status: actionRefResolutionStatusSchema,
|
|
774
|
+
actionId: z2.string().optional(),
|
|
775
|
+
name: z2.string().optional(),
|
|
776
|
+
hint: z2.string().optional()
|
|
777
|
+
});
|
|
778
|
+
var actionDetailSchema = z2.object({
|
|
779
|
+
action: actionDocSchema,
|
|
780
|
+
blockers: z2.array(actionDocSchema),
|
|
781
|
+
blocked: z2.array(actionDocSchema),
|
|
782
|
+
activeChat: z2.object({ _id: z2.string(), title: z2.string() }).nullable()
|
|
783
|
+
});
|
|
784
|
+
var actionDraftOpKindSchema = z2.enum(["create", "update", "complete", "discard", "link", "unlink"]);
|
|
785
|
+
var chatActionDraftOpViewSchema = z2.object({
|
|
786
|
+
kind: actionDraftOpKindSchema,
|
|
787
|
+
targetName: z2.string(),
|
|
788
|
+
targetActionId: z2.string().optional(),
|
|
789
|
+
tempId: z2.string().optional(),
|
|
790
|
+
tags: z2.array(z2.string()).optional(),
|
|
791
|
+
reason: z2.string().optional(),
|
|
792
|
+
note: z2.string().optional(),
|
|
793
|
+
name: z2.string().optional(),
|
|
794
|
+
description: z2.string().optional(),
|
|
795
|
+
blockerName: z2.string().optional(),
|
|
796
|
+
blockerActionId: z2.string().optional(),
|
|
797
|
+
blockerTempId: z2.string().optional()
|
|
798
|
+
});
|
|
799
|
+
var chatDraftViewSchema = z2.object({
|
|
800
|
+
status: z2.enum(["active", "publishing", "discarded", "applied", "none"]),
|
|
801
|
+
count: z2.number(),
|
|
802
|
+
ops: z2.array(chatActionDraftOpViewSchema),
|
|
803
|
+
warnings: z2.array(z2.object({ tempId: z2.string(), name: z2.string() }))
|
|
804
|
+
});
|
|
805
|
+
var actionsClaimDataSchema = z2.object({
|
|
806
|
+
id: z2.string(),
|
|
807
|
+
name: z2.string(),
|
|
808
|
+
description: z2.string(),
|
|
809
|
+
tags: z2.array(z2.string())
|
|
810
|
+
});
|
|
811
|
+
var skillRecommendationSchema = z2.object({
|
|
812
|
+
name: z2.string(),
|
|
813
|
+
reason: z2.string()
|
|
814
|
+
});
|
|
815
|
+
var removeDraftOpTargetSchema = z2.discriminatedUnion("kind", [
|
|
816
|
+
z2.object({ kind: z2.literal("tempId"), tempId: z2.string() }),
|
|
817
|
+
z2.object({
|
|
818
|
+
kind: z2.literal("actionId"),
|
|
819
|
+
actionId: z2.string(),
|
|
820
|
+
opKind: z2.enum(["update", "complete", "discard"])
|
|
821
|
+
})
|
|
822
|
+
]);
|
|
823
|
+
function okResponse(data) {
|
|
824
|
+
return z2.object({ ok: z2.literal(true), data });
|
|
825
|
+
}
|
|
826
|
+
function okOnlySchema() {
|
|
827
|
+
return z2.object({ ok: z2.literal(true) });
|
|
828
|
+
}
|
|
829
|
+
var actionsCreateRequestSchema = z2.object({
|
|
830
|
+
chatId: z2.string(),
|
|
831
|
+
tempId: z2.string(),
|
|
832
|
+
name: z2.string().min(1),
|
|
833
|
+
description: z2.string(),
|
|
834
|
+
tags: z2.array(z2.string()).optional(),
|
|
835
|
+
priority: actionPrioritySchema.optional()
|
|
836
|
+
});
|
|
837
|
+
var actionsCreateResponseSchema = okResponse(z2.object({ tempId: z2.string() }));
|
|
838
|
+
var actionsGetRequestSchema = z2.object({ actionId: z2.string().min(1) });
|
|
839
|
+
var actionsGetResponseSchema = okResponse(actionDetailSchema.nullable());
|
|
840
|
+
var actionsListRequestSchema = z2.object({
|
|
841
|
+
chatId: z2.string().optional(),
|
|
842
|
+
status: actionStatusSchema.optional(),
|
|
843
|
+
bucketed: z2.boolean().optional(),
|
|
844
|
+
q: z2.string().optional(),
|
|
845
|
+
sort: z2.enum(["recent", "priority"]).optional()
|
|
846
|
+
});
|
|
847
|
+
var actionsListResponseSchema = okResponse(z2.union([actionBucketsSchema, z2.array(actionWithMetaSchema)]));
|
|
848
|
+
var actionsStatusRequestSchema = z2.object({
|
|
849
|
+
refs: z2.array(z2.string().min(1)).max(50),
|
|
850
|
+
chatId: z2.string().min(1).optional()
|
|
851
|
+
});
|
|
852
|
+
var actionsStatusResponseSchema = okResponse(z2.array(actionRefStatusResultSchema));
|
|
853
|
+
var actionsUpdateRequestSchema = z2.object({
|
|
854
|
+
chatId: z2.string(),
|
|
855
|
+
actionId: z2.string(),
|
|
856
|
+
name: z2.string().optional(),
|
|
857
|
+
description: z2.string().optional(),
|
|
858
|
+
tags: z2.array(z2.string()).optional(),
|
|
859
|
+
priority: actionPrioritySchema.nullable().optional()
|
|
860
|
+
});
|
|
861
|
+
var actionsUpdateResponseSchema = okOnlySchema();
|
|
862
|
+
var ACTION_COMPLETE_NOTE_MIN = 20;
|
|
863
|
+
var actionsCompleteNoteMessage = `A completion note (\u2265${ACTION_COMPLETE_NOTE_MIN} chars) describing what was done is required: what changed, where, and the outcome.`;
|
|
864
|
+
var actionsCompleteRequestSchema = z2.object({
|
|
865
|
+
chatId: z2.string(),
|
|
866
|
+
actionRef: z2.string().min(1, "actionRef is required"),
|
|
867
|
+
note: z2.string().optional()
|
|
868
|
+
}).transform((body) => ({ ...body, note: (body.note ?? "").trim() })).refine((body) => body.note.length >= ACTION_COMPLETE_NOTE_MIN, {
|
|
869
|
+
message: actionsCompleteNoteMessage,
|
|
870
|
+
path: ["note"]
|
|
871
|
+
});
|
|
872
|
+
var actionsCompleteResponseSchema = okOnlySchema();
|
|
873
|
+
var actionsDiscardRequestSchema = z2.object({
|
|
874
|
+
chatId: z2.string(),
|
|
875
|
+
actionId: z2.string(),
|
|
876
|
+
reason: z2.string().optional()
|
|
877
|
+
});
|
|
878
|
+
var actionsDiscardResponseSchema = okOnlySchema();
|
|
879
|
+
var actionsLinkRequestSchema = z2.object({
|
|
880
|
+
chatId: z2.string(),
|
|
881
|
+
blockerRef: z2.string(),
|
|
882
|
+
blockedRef: z2.string()
|
|
883
|
+
});
|
|
884
|
+
var actionsLinkResponseSchema = okOnlySchema();
|
|
885
|
+
var actionsUnlinkRequestSchema = z2.object({
|
|
886
|
+
chatId: z2.string(),
|
|
887
|
+
blockerId: z2.string(),
|
|
888
|
+
blockedId: z2.string()
|
|
889
|
+
});
|
|
890
|
+
var actionsUnlinkResponseSchema = okOnlySchema();
|
|
891
|
+
var actionsClaimRequestSchema = z2.object({
|
|
892
|
+
actionId: z2.string(),
|
|
893
|
+
chatId: z2.string()
|
|
894
|
+
});
|
|
895
|
+
var actionsClaimResponseSchema = okResponse(actionsClaimDataSchema.nullable());
|
|
896
|
+
var actionsReleaseRequestSchema = z2.object({
|
|
897
|
+
actionId: z2.string(),
|
|
898
|
+
chatId: z2.string()
|
|
899
|
+
});
|
|
900
|
+
var actionsReleaseResponseSchema = okOnlySchema();
|
|
901
|
+
var actionsRecommendSkillsRequestSchema = z2.object({
|
|
902
|
+
actionId: z2.string().min(1),
|
|
903
|
+
// Bounds track what the CLI actually sends (descriptions truncated to ~600 chars); they
|
|
904
|
+
// cap the prompt size a direct API-key caller can push into the billed LLM call.
|
|
905
|
+
skills: z2.array(
|
|
906
|
+
z2.object({
|
|
907
|
+
name: z2.string().min(1).max(120),
|
|
908
|
+
description: z2.string().max(800)
|
|
909
|
+
})
|
|
910
|
+
).max(80)
|
|
911
|
+
});
|
|
912
|
+
var actionsRecommendSkillsResponseSchema = okResponse(
|
|
913
|
+
z2.object({ recommendations: z2.array(skillRecommendationSchema) })
|
|
914
|
+
);
|
|
915
|
+
var actionsLogRequestSchema = z2.object({
|
|
916
|
+
fromMs: z2.number(),
|
|
917
|
+
toMs: z2.number()
|
|
918
|
+
});
|
|
919
|
+
var actionsLogResponseSchema = okResponse(z2.array(actionWithMetaSchema));
|
|
920
|
+
var actionsDraftRequestSchema = z2.object({ chatId: z2.string() });
|
|
921
|
+
var actionsDraftResponseSchema = okResponse(chatDraftViewSchema);
|
|
922
|
+
var actionsDraftClearRequestSchema = z2.object({ chatId: z2.string() });
|
|
923
|
+
var actionsDraftClearResponseSchema = okOnlySchema();
|
|
924
|
+
var actionsRemoveDraftOpRequestSchema = z2.object({
|
|
925
|
+
chatId: z2.string(),
|
|
926
|
+
target: removeDraftOpTargetSchema
|
|
927
|
+
});
|
|
928
|
+
var actionsRemoveDraftOpResponseSchema = okOnlySchema();
|
|
929
|
+
var actionsTagsListResponseSchema = z2.object({ markdown: z2.string() });
|
|
930
|
+
var actionsTagsCreateRequestSchema = z2.object({
|
|
931
|
+
name: z2.string(),
|
|
932
|
+
description: z2.string().optional()
|
|
933
|
+
});
|
|
934
|
+
var actionsTagsCreateResponseSchema = okResponse(z2.object({ tagId: z2.string() }));
|
|
935
|
+
var actionsTagsDeleteRequestSchema = z2.object({ name: z2.string() });
|
|
936
|
+
var actionsTagsDeleteResponseSchema = okOnlySchema();
|
|
937
|
+
|
|
687
938
|
// src/commands/actions/complete.ts
|
|
688
939
|
import { defineCommand as defineCommand2 } from "citty";
|
|
689
|
-
var COMPLETE_NOTE_MIN = 20;
|
|
690
940
|
registerSchema({
|
|
691
941
|
command: "actions.complete",
|
|
692
942
|
description: "Stage completion of an action (it was DONE). Accepts a real action ID (must be claimed) or a temp ID from the same draft. The action becomes completed when the chat is published. --note is REQUIRED and must describe what you actually did to resolve it (what changed, where, and the outcome) \u2014 it is the client-facing record surfaced by `baker actions log`. Do NOT use this to drop an action you no longer want \u2014 to remove a staged op use `actions draft remove`, to close an unwanted published action use `actions discard`.",
|
|
@@ -723,9 +973,9 @@ var completeCommand = defineCommand2({
|
|
|
723
973
|
validateConvexId(id);
|
|
724
974
|
}
|
|
725
975
|
const note = args.note?.trim() ?? "";
|
|
726
|
-
if (note.length <
|
|
976
|
+
if (note.length < ACTION_COMPLETE_NOTE_MIN) {
|
|
727
977
|
failValidation(
|
|
728
|
-
`--note is required and must describe what you did (\u2265${
|
|
978
|
+
`--note is required and must describe what you did (\u2265${ACTION_COMPLETE_NOTE_MIN} chars): what changed, where, and the outcome. This becomes the client-facing record surfaced by \`baker actions log\`.`
|
|
729
979
|
);
|
|
730
980
|
}
|
|
731
981
|
const chatId = requireChatId();
|
|
@@ -8559,27 +8809,27 @@ import path5 from "path";
|
|
|
8559
8809
|
import { defineCommand as defineCommand82 } from "citty";
|
|
8560
8810
|
|
|
8561
8811
|
// src/engine/scaffold/staticAd.ts
|
|
8562
|
-
import { z as
|
|
8812
|
+
import { z as z3 } from "zod";
|
|
8563
8813
|
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"]);
|
|
8564
8814
|
var DEFAULT_ASPECT_RATIO = "9:16";
|
|
8565
|
-
var Blueprint =
|
|
8566
|
-
meta:
|
|
8567
|
-
text_content:
|
|
8815
|
+
var Blueprint = z3.object({
|
|
8816
|
+
meta: z3.object({ estimated_aspect_ratio: z3.string().optional() }).loose().optional(),
|
|
8817
|
+
text_content: z3.array(z3.object({ text: z3.string().optional() }).loose()).optional()
|
|
8568
8818
|
}).loose();
|
|
8569
|
-
var ElementLocator =
|
|
8570
|
-
collection:
|
|
8571
|
-
index:
|
|
8819
|
+
var ElementLocator = z3.object({
|
|
8820
|
+
collection: z3.enum(["subjects", "people", "brands_logos"]),
|
|
8821
|
+
index: z3.number().int().nonnegative()
|
|
8572
8822
|
}).loose();
|
|
8573
|
-
var MainElement =
|
|
8823
|
+
var MainElement = z3.object({
|
|
8574
8824
|
// logo | product | person | animal | badge | other
|
|
8575
|
-
type:
|
|
8576
|
-
label:
|
|
8577
|
-
description:
|
|
8578
|
-
expression:
|
|
8579
|
-
reason:
|
|
8825
|
+
type: z3.string(),
|
|
8826
|
+
label: z3.string().optional(),
|
|
8827
|
+
description: z3.string().optional(),
|
|
8828
|
+
expression: z3.string().nullable().optional(),
|
|
8829
|
+
reason: z3.string().optional(),
|
|
8580
8830
|
locator: ElementLocator.optional()
|
|
8581
8831
|
}).loose();
|
|
8582
|
-
var MainElements =
|
|
8832
|
+
var MainElements = z3.array(MainElement);
|
|
8583
8833
|
function sanitizeId(raw, fallback) {
|
|
8584
8834
|
const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
8585
8835
|
return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
|
|
@@ -9074,7 +9324,7 @@ import { toCardinal as nwKo } from "n2words/ko-KR";
|
|
|
9074
9324
|
import { toCardinal as nwNl } from "n2words/nl-NL";
|
|
9075
9325
|
import { toCardinal as nwPl } from "n2words/pl-PL";
|
|
9076
9326
|
import { toCardinal as nwPt } from "n2words/pt-PT";
|
|
9077
|
-
import { z as
|
|
9327
|
+
import { z as z4 } from "zod";
|
|
9078
9328
|
|
|
9079
9329
|
// src/engine/scaffold/lib/shoot-modes.ts
|
|
9080
9330
|
var SHOOT_MODES = [
|
|
@@ -9331,49 +9581,49 @@ function trimArgs(durationS, offsetS = 0) {
|
|
|
9331
9581
|
"{{out.video}}"
|
|
9332
9582
|
];
|
|
9333
9583
|
}
|
|
9334
|
-
var FrameAsset =
|
|
9335
|
-
var DialogueLine =
|
|
9336
|
-
speaker:
|
|
9337
|
-
line:
|
|
9584
|
+
var FrameAsset = z4.object({ url: z4.string().optional() }).loose().optional();
|
|
9585
|
+
var DialogueLine = z4.object({
|
|
9586
|
+
speaker: z4.string().optional(),
|
|
9587
|
+
line: z4.string().optional(),
|
|
9338
9588
|
// Absolute seconds on the source timeline (the deconstruct emits both).
|
|
9339
|
-
start_s:
|
|
9340
|
-
end_s:
|
|
9341
|
-
delivery:
|
|
9342
|
-
voice_description:
|
|
9589
|
+
start_s: z4.number().optional(),
|
|
9590
|
+
end_s: z4.number().optional(),
|
|
9591
|
+
delivery: z4.string().optional(),
|
|
9592
|
+
voice_description: z4.string().optional()
|
|
9343
9593
|
}).loose();
|
|
9344
|
-
var Sfx =
|
|
9345
|
-
at_s:
|
|
9346
|
-
duration_s:
|
|
9347
|
-
sound_effect_prompt:
|
|
9348
|
-
description:
|
|
9594
|
+
var Sfx = z4.object({
|
|
9595
|
+
at_s: z4.number().optional(),
|
|
9596
|
+
duration_s: z4.number().optional(),
|
|
9597
|
+
sound_effect_prompt: z4.string().optional(),
|
|
9598
|
+
description: z4.string().optional()
|
|
9349
9599
|
}).loose();
|
|
9350
|
-
var CompositionRegion =
|
|
9600
|
+
var CompositionRegion = z4.object({
|
|
9351
9601
|
// full | top | bottom | left | right | inset
|
|
9352
|
-
panel:
|
|
9602
|
+
panel: z4.string().optional(),
|
|
9353
9603
|
// 9-grid anchor for an `inset` presenter box.
|
|
9354
|
-
position:
|
|
9355
|
-
is_presenter:
|
|
9604
|
+
position: z4.string().optional(),
|
|
9605
|
+
is_presenter: z4.boolean().optional(),
|
|
9356
9606
|
// The cast id shown/speaking in this region (routes lip-sync + element refs).
|
|
9357
|
-
cast_ref:
|
|
9358
|
-
summary:
|
|
9359
|
-
frame_prompt:
|
|
9360
|
-
motion_prompt:
|
|
9607
|
+
cast_ref: z4.string().optional(),
|
|
9608
|
+
summary: z4.string().optional(),
|
|
9609
|
+
frame_prompt: z4.string().optional(),
|
|
9610
|
+
motion_prompt: z4.string().optional()
|
|
9361
9611
|
}).loose();
|
|
9362
|
-
var SceneComposition =
|
|
9612
|
+
var SceneComposition = z4.object({
|
|
9363
9613
|
// full_frame (default) | split_screen | pip | keyed_overlay
|
|
9364
|
-
layout:
|
|
9614
|
+
layout: z4.string().optional(),
|
|
9365
9615
|
// split_screen only: vertical (top/bottom) | horizontal (left/right).
|
|
9366
|
-
split_axis:
|
|
9367
|
-
regions:
|
|
9616
|
+
split_axis: z4.string().optional(),
|
|
9617
|
+
regions: z4.array(CompositionRegion).optional()
|
|
9368
9618
|
}).loose();
|
|
9369
|
-
var CameraMotion =
|
|
9370
|
-
var TranscriptWord =
|
|
9371
|
-
var Scene =
|
|
9372
|
-
start_s:
|
|
9373
|
-
end_s:
|
|
9374
|
-
duration_s:
|
|
9375
|
-
summary:
|
|
9376
|
-
action_detail:
|
|
9619
|
+
var CameraMotion = z4.object({ movement: z4.string().optional(), detail: z4.string().optional() }).loose();
|
|
9620
|
+
var TranscriptWord = z4.object({ text: z4.string().optional() }).loose();
|
|
9621
|
+
var Scene = z4.object({
|
|
9622
|
+
start_s: z4.number().optional(),
|
|
9623
|
+
end_s: z4.number().optional(),
|
|
9624
|
+
duration_s: z4.number().optional(),
|
|
9625
|
+
summary: z4.string().optional(),
|
|
9626
|
+
action_detail: z4.string().optional(),
|
|
9377
9627
|
// The scene's spatial layout. Absent/full_frame ⇒ one uncut shot (default path).
|
|
9378
9628
|
// A layered layout (split_screen/pip/keyed_overlay) with regions ⇒ the scaffold
|
|
9379
9629
|
// builds one clip per region and stacks/overlays them into the scene picture.
|
|
@@ -9381,77 +9631,77 @@ var Scene = z3.object({
|
|
|
9381
9631
|
// The capture "look" for this scene — selected from the ad-native shoot-mode
|
|
9382
9632
|
// grammar (see lib/shoot-modes.ts). When absent the scaffold auto-derives a
|
|
9383
9633
|
// UGC/product mode; a human can override per scene by setting this.
|
|
9384
|
-
shoot_mode:
|
|
9634
|
+
shoot_mode: z4.string().optional(),
|
|
9385
9635
|
// Diegetic ambient the clip's native audio should carry (no music). When
|
|
9386
9636
|
// absent the scene falls back to its shoot mode's default ambience.
|
|
9387
|
-
ambient:
|
|
9637
|
+
ambient: z4.string().optional(),
|
|
9388
9638
|
camera_motion: CameraMotion.optional(),
|
|
9389
|
-
start_frame_prompt:
|
|
9390
|
-
end_frame_prompt:
|
|
9391
|
-
motion_prompt:
|
|
9639
|
+
start_frame_prompt: z4.string().optional(),
|
|
9640
|
+
end_frame_prompt: z4.string().optional(),
|
|
9641
|
+
motion_prompt: z4.string().optional(),
|
|
9392
9642
|
// The scene's role in the ad's persuasion arc (DECON-supplied); drives the
|
|
9393
9643
|
// script re-craft checklist. Inferred from position when absent.
|
|
9394
|
-
narrative_role:
|
|
9644
|
+
narrative_role: z4.string().optional(),
|
|
9395
9645
|
// DECON-supplied on the HOOK scene: the engineered physical/emotional state that
|
|
9396
9646
|
// makes the first frame stop the scroll (sweaty/breathless/urgent …). Injected
|
|
9397
9647
|
// into the hook's start-frame description so the generator renders that state,
|
|
9398
9648
|
// not a calm influencer (CCA-11).
|
|
9399
|
-
hook_mechanic:
|
|
9649
|
+
hook_mechanic: z4.object({ mechanic: z4.string().optional(), why_it_stops_scroll: z4.string().optional() }).loose().optional(),
|
|
9400
9650
|
// DECON-supplied per-scene location (so a gym hook isn't flattened to "home").
|
|
9401
|
-
scene_setting:
|
|
9651
|
+
scene_setting: z4.string().optional(),
|
|
9402
9652
|
// How this scene cuts to the next (DECON-supplied). A recognized non-cut type
|
|
9403
9653
|
// (fade/whip/zoom/dissolve/swipe) is reproduced as an ffmpeg xfade at the
|
|
9404
9654
|
// boundary; cut/match_cut/none/other stay hard cuts. The last scene's value is
|
|
9405
9655
|
// ignored (nothing follows it).
|
|
9406
|
-
transition_out:
|
|
9407
|
-
dialogue:
|
|
9408
|
-
sfx:
|
|
9409
|
-
overlays:
|
|
9410
|
-
floating_elements:
|
|
9411
|
-
transcript_slice:
|
|
9656
|
+
transition_out: z4.object({ type: z4.string().optional(), description: z4.string().optional() }).loose().optional(),
|
|
9657
|
+
dialogue: z4.array(DialogueLine).optional(),
|
|
9658
|
+
sfx: z4.array(Sfx).optional(),
|
|
9659
|
+
overlays: z4.array(z4.unknown()).optional(),
|
|
9660
|
+
floating_elements: z4.array(z4.unknown()).optional(),
|
|
9661
|
+
transcript_slice: z4.array(TranscriptWord).optional(),
|
|
9412
9662
|
start_frame_asset: FrameAsset,
|
|
9413
9663
|
end_frame_asset: FrameAsset,
|
|
9414
9664
|
// DECON-supplied: true when this scene is a length-split CONTINUATION of the
|
|
9415
9665
|
// previous one (the SAME physical shot, broken up only because it exceeded the
|
|
9416
9666
|
// clip ceiling). The scaffold then shares the splice keyframe — this scene's
|
|
9417
9667
|
// start frame IS the previous scene's end frame — so the join is seamless.
|
|
9418
|
-
continues_previous:
|
|
9668
|
+
continues_previous: z4.boolean().optional()
|
|
9419
9669
|
}).loose();
|
|
9420
|
-
var VideoBlueprint =
|
|
9421
|
-
source:
|
|
9422
|
-
global:
|
|
9423
|
-
music:
|
|
9424
|
-
present:
|
|
9425
|
-
music_prompt:
|
|
9670
|
+
var VideoBlueprint = z4.object({
|
|
9671
|
+
source: z4.object({ aspect_ratio: z4.string().optional(), duration_s: z4.number().optional() }).loose().optional(),
|
|
9672
|
+
global: z4.object({
|
|
9673
|
+
music: z4.object({
|
|
9674
|
+
present: z4.boolean().optional(),
|
|
9675
|
+
music_prompt: z4.string().optional(),
|
|
9426
9676
|
// Absolute second the music enters in the reference (the bed often
|
|
9427
9677
|
// kicks in mid-ad, after the hook). We start the regenerated track here
|
|
9428
9678
|
// instead of at 0 so the timing matches.
|
|
9429
|
-
starts_at_s:
|
|
9679
|
+
starts_at_s: z4.number().optional(),
|
|
9430
9680
|
// Populated by the deconstruct when AudD (Shazam-style) recognizes the
|
|
9431
9681
|
// reference track. We never reuse it — only style the regenerated bed.
|
|
9432
|
-
identified_track:
|
|
9682
|
+
identified_track: z4.object({ title: z4.string().optional(), artist: z4.string().optional() }).loose().nullish()
|
|
9433
9683
|
}).loose().optional(),
|
|
9434
|
-
cast:
|
|
9435
|
-
|
|
9436
|
-
id:
|
|
9437
|
-
description:
|
|
9684
|
+
cast: z4.array(
|
|
9685
|
+
z4.object({
|
|
9686
|
+
id: z4.string().optional(),
|
|
9687
|
+
description: z4.string().optional(),
|
|
9438
9688
|
// The deconstruct's note on the target-market localization (e.g. "native
|
|
9439
9689
|
// French speaker") — read to derive the spoken-track language code.
|
|
9440
|
-
market_localization_note:
|
|
9690
|
+
market_localization_note: z4.string().optional()
|
|
9441
9691
|
}).loose()
|
|
9442
9692
|
).optional(),
|
|
9443
|
-
voiceover:
|
|
9693
|
+
voiceover: z4.object({
|
|
9444
9694
|
// on_camera | mixed → mouths are on screen (lip-sync candidates);
|
|
9445
9695
|
// voiceover | none → narration over the picture (no lip-sync).
|
|
9446
|
-
mode:
|
|
9447
|
-
voice_description:
|
|
9448
|
-
persona:
|
|
9696
|
+
mode: z4.string().optional(),
|
|
9697
|
+
voice_description: z4.string().optional(),
|
|
9698
|
+
persona: z4.string().optional()
|
|
9449
9699
|
}).loose().optional(),
|
|
9450
9700
|
// Visual palette — read only to colour a clean brand-card/CTA plate (the
|
|
9451
9701
|
// first hex is the dominant brand colour); never to drive frame generation.
|
|
9452
|
-
style:
|
|
9702
|
+
style: z4.object({ palette: z4.array(z4.object({ hex: z4.string().optional() }).loose()).optional() }).loose().optional()
|
|
9453
9703
|
}).loose().optional(),
|
|
9454
|
-
scenes:
|
|
9704
|
+
scenes: z4.array(Scene).min(1)
|
|
9455
9705
|
}).loose();
|
|
9456
9706
|
function injectHookPhysicality(blueprint) {
|
|
9457
9707
|
for (const scene of blueprint.scenes) {
|
|
@@ -9461,26 +9711,26 @@ function injectHookPhysicality(blueprint) {
|
|
|
9461
9711
|
scene.start_frame_prompt = `${prompt} The subject's physical state IS the scroll-stopper \u2014 render it explicitly, not a calm pose: ${why}.`;
|
|
9462
9712
|
}
|
|
9463
9713
|
}
|
|
9464
|
-
var AppearsItem =
|
|
9465
|
-
var RecurringElement =
|
|
9714
|
+
var AppearsItem = z4.union([z4.number(), z4.object({ scene: z4.number(), edge: z4.string().optional() }).loose()]);
|
|
9715
|
+
var RecurringElement = z4.object({
|
|
9466
9716
|
// person | animal | product | logo | badge | other
|
|
9467
|
-
type:
|
|
9468
|
-
label:
|
|
9469
|
-
description:
|
|
9470
|
-
expression:
|
|
9717
|
+
type: z4.string(),
|
|
9718
|
+
label: z4.string().optional(),
|
|
9719
|
+
description: z4.string().optional(),
|
|
9720
|
+
expression: z4.string().nullable().optional(),
|
|
9471
9721
|
// When the element maps to a global cast entry, its stable id (for annotation).
|
|
9472
|
-
cast_id:
|
|
9722
|
+
cast_id: z4.string().nullable().optional(),
|
|
9473
9723
|
// The label of another element that is the SAME individual as this one, shown
|
|
9474
9724
|
// in a DIFFERENT wardrobe/persona/state (e.g. one creator playing skeptic in a
|
|
9475
9725
|
// pink shirt and believer in a white shirt). Each look gets its own reference
|
|
9476
9726
|
// slot, but the face/identity must stay identical across them.
|
|
9477
|
-
same_as:
|
|
9727
|
+
same_as: z4.string().nullable().optional(),
|
|
9478
9728
|
// Scenes the element appears in. Either a bare list of scene indices (both
|
|
9479
9729
|
// edges) or per-{scene,edge} entries. Both forms are accepted and merged.
|
|
9480
|
-
scenes:
|
|
9481
|
-
appears_in:
|
|
9730
|
+
scenes: z4.array(z4.number()).optional(),
|
|
9731
|
+
appears_in: z4.array(AppearsItem).optional()
|
|
9482
9732
|
}).loose();
|
|
9483
|
-
var RecurringElements =
|
|
9733
|
+
var RecurringElements = z4.array(RecurringElement);
|
|
9484
9734
|
function sanitizeId2(raw, fallback) {
|
|
9485
9735
|
const id = raw.toLowerCase().replace(/[^a-z0-9]+/g, "_").replace(/^_+|_+$/g, "");
|
|
9486
9736
|
return /^[a-z]/.test(id) ? id : `${fallback}_${id}`.replace(/_+$/g, "") || fallback;
|
|
@@ -10943,25 +11193,25 @@ function buildSfxMusic(blueprint, nodes) {
|
|
|
10943
11193
|
}
|
|
10944
11194
|
return tracks;
|
|
10945
11195
|
}
|
|
10946
|
-
var OverlayStyle =
|
|
10947
|
-
var Overlay =
|
|
10948
|
-
text:
|
|
10949
|
-
appears_at_s:
|
|
10950
|
-
duration_s:
|
|
10951
|
-
position:
|
|
10952
|
-
role:
|
|
10953
|
-
animation:
|
|
10954
|
-
animation_detail:
|
|
11196
|
+
var OverlayStyle = z4.object({ color_hex: z4.string().optional(), background: z4.string().optional(), size: z4.string().optional() }).loose();
|
|
11197
|
+
var Overlay = z4.object({
|
|
11198
|
+
text: z4.string().optional(),
|
|
11199
|
+
appears_at_s: z4.number().optional(),
|
|
11200
|
+
duration_s: z4.number().optional(),
|
|
11201
|
+
position: z4.string().optional(),
|
|
11202
|
+
role: z4.string().optional(),
|
|
11203
|
+
animation: z4.string().optional(),
|
|
11204
|
+
animation_detail: z4.string().optional(),
|
|
10955
11205
|
style: OverlayStyle.optional()
|
|
10956
11206
|
}).loose();
|
|
10957
|
-
var FloatingElement =
|
|
10958
|
-
kind:
|
|
10959
|
-
description:
|
|
10960
|
-
brand_name:
|
|
10961
|
-
what_it_represents:
|
|
10962
|
-
appears_at_s:
|
|
10963
|
-
duration_s:
|
|
10964
|
-
position:
|
|
11207
|
+
var FloatingElement = z4.object({
|
|
11208
|
+
kind: z4.string().optional(),
|
|
11209
|
+
description: z4.string().optional(),
|
|
11210
|
+
brand_name: z4.string().nullish(),
|
|
11211
|
+
what_it_represents: z4.string().optional(),
|
|
11212
|
+
appears_at_s: z4.number().optional(),
|
|
11213
|
+
duration_s: z4.number().optional(),
|
|
11214
|
+
position: z4.string().optional()
|
|
10965
11215
|
}).loose();
|
|
10966
11216
|
function escapeHtml(s) {
|
|
10967
11217
|
return s.replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">").replace(/"/g, """);
|
|
@@ -10993,7 +11243,7 @@ function positionClass(position) {
|
|
|
10993
11243
|
function collectCaptions(blueprint) {
|
|
10994
11244
|
return blueprint.scenes.flatMap((scene) => {
|
|
10995
11245
|
const sceneStart = scene.start_s ?? 0;
|
|
10996
|
-
const overlays =
|
|
11246
|
+
const overlays = z4.array(Overlay).safeParse(scene.overlays ?? []);
|
|
10997
11247
|
return overlays.success ? overlays.data.filter((ov) => Boolean(ov.text?.trim())).map((ov) => {
|
|
10998
11248
|
const at = ov.appears_at_s ?? sceneStart;
|
|
10999
11249
|
return { text: ov.text.trim(), at, end: at + (ov.duration_s ?? 2.5), ov };
|
|
@@ -11102,7 +11352,7 @@ function buildOverlayHtml(input) {
|
|
|
11102
11352
|
if (ovParts.length > 0) blocks.push(ovParts.join("\n"));
|
|
11103
11353
|
for (const scene of blueprint.scenes) {
|
|
11104
11354
|
const sceneStart = scene.start_s ?? 0;
|
|
11105
|
-
const floats =
|
|
11355
|
+
const floats = z4.array(FloatingElement).safeParse(scene.floating_elements ?? []);
|
|
11106
11356
|
const parts = (floats.success ? floats.data.map((fe) => floatingStub(fe, sceneStart)) : []).filter(Boolean);
|
|
11107
11357
|
const pip = uiPipStub(scene);
|
|
11108
11358
|
if (pip) parts.push(pip);
|
|
@@ -11359,8 +11609,8 @@ function buildMotionBoard(blueprint) {
|
|
|
11359
11609
|
const end_s = scene.end_s ?? start_s + sceneDurationS(scene);
|
|
11360
11610
|
cursor = end_s;
|
|
11361
11611
|
const spoken = sceneSpokenText(scene);
|
|
11362
|
-
const overlays =
|
|
11363
|
-
const floats =
|
|
11612
|
+
const overlays = z4.array(Overlay).safeParse(scene.overlays ?? []);
|
|
11613
|
+
const floats = z4.array(FloatingElement).safeParse(scene.floating_elements ?? []);
|
|
11364
11614
|
const graphics = [
|
|
11365
11615
|
...(overlays.success ? overlays.data : []).filter((ov) => ov.text?.trim()).map((ov) => ({
|
|
11366
11616
|
kind: "text",
|