@mevdragon/vidfarm-devcli 0.2.13 → 0.3.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 +11 -4
- package/SKILL.director.md +97 -0
- package/SKILL.platform.md +66 -6
- package/dist/src/cli.js +137 -1524
- package/dist/src/dev-serve.js +169 -0
- package/package.json +5 -24
- package/.env.example +0 -60
- package/auto-create-hyperframe-templates/AUTO_CREATE_HYPERFRAME_TEMPLATES.md +0 -275
- package/auto-create-hyperframe-templates/extractor-system-prompt.md +0 -128
- package/auto-create-hyperframe-templates/input-manifest.schema.json +0 -56
- package/auto-create-hyperframe-templates/lambda-memory-estimator.md +0 -118
- package/auto-create-hyperframe-templates/production-graph.schema.json +0 -314
- package/auto-create-hyperframe-templates/runbook.md +0 -198
- package/auto-create-hyperframe-templates/scripts/create-hyperframe-template.mjs +0 -368
- package/auto-create-hyperframe-templates/template-plan.schema.json +0 -311
- package/auto-create-hyperframe-templates/template-planner-prompt.md +0 -144
- package/auto-create-templates/AUTO_CREATE_TEMPLATES.md +0 -599
- package/auto-create-templates/extractor-system-prompt.md +0 -119
- package/auto-create-templates/production-graph.schema.json +0 -276
- package/auto-create-templates/runbook.md +0 -140
- package/auto-create-templates/template-plan.schema.json +0 -301
- package/auto-create-templates/template-planner-prompt.md +0 -137
- package/dist/src/account-pages-legacy.js +0 -9336
- package/dist/src/account-pages-legacy.js.map +0 -1
- package/dist/src/account-pages.js +0 -61
- package/dist/src/account-pages.js.map +0 -1
- package/dist/src/app.js +0 -14023
- package/dist/src/app.js.map +0 -1
- package/dist/src/cli.js.map +0 -1
- package/dist/src/composition-runtime.js +0 -581
- package/dist/src/composition-runtime.js.map +0 -1
- package/dist/src/config.js +0 -140
- package/dist/src/config.js.map +0 -1
- package/dist/src/context.js +0 -492
- package/dist/src/context.js.map +0 -1
- package/dist/src/dev-app-legacy.js +0 -694
- package/dist/src/dev-app-legacy.js.map +0 -1
- package/dist/src/dev-app.js +0 -6
- package/dist/src/dev-app.js.map +0 -1
- package/dist/src/domain.js +0 -2
- package/dist/src/domain.js.map +0 -1
- package/dist/src/editor-chat-history.js +0 -82
- package/dist/src/editor-chat-history.js.map +0 -1
- package/dist/src/editor-chat.js +0 -311
- package/dist/src/editor-chat.js.map +0 -1
- package/dist/src/frontend/flockposter-cache-store.js +0 -124
- package/dist/src/frontend/flockposter-cache-store.js.map +0 -1
- package/dist/src/frontend/homepage-client.js +0 -117
- package/dist/src/frontend/homepage-client.js.map +0 -1
- package/dist/src/frontend/homepage-shared.js +0 -4
- package/dist/src/frontend/homepage-shared.js.map +0 -1
- package/dist/src/frontend/homepage-store.js +0 -28
- package/dist/src/frontend/homepage-store.js.map +0 -1
- package/dist/src/frontend/homepage-view.js +0 -260
- package/dist/src/frontend/homepage-view.js.map +0 -1
- package/dist/src/frontend/page-runtime-client.js +0 -103
- package/dist/src/frontend/page-runtime-client.js.map +0 -1
- package/dist/src/frontend/page-runtime-store.js +0 -9
- package/dist/src/frontend/page-runtime-store.js.map +0 -1
- package/dist/src/frontend/sentry.js +0 -42
- package/dist/src/frontend/sentry.js.map +0 -1
- package/dist/src/frontend/template-editor-chat.js +0 -3767
- package/dist/src/frontend/template-editor-chat.js.map +0 -1
- package/dist/src/help-page.js +0 -333
- package/dist/src/help-page.js.map +0 -1
- package/dist/src/homepage.js +0 -919
- package/dist/src/homepage.js.map +0 -1
- package/dist/src/hyperframes/composition.js +0 -180
- package/dist/src/hyperframes/composition.js.map +0 -1
- package/dist/src/index.js +0 -16
- package/dist/src/index.js.map +0 -1
- package/dist/src/instrument.js +0 -30
- package/dist/src/instrument.js.map +0 -1
- package/dist/src/lib/crypto.js +0 -45
- package/dist/src/lib/crypto.js.map +0 -1
- package/dist/src/lib/dev-log.js +0 -54
- package/dist/src/lib/dev-log.js.map +0 -1
- package/dist/src/lib/display-name.js +0 -11
- package/dist/src/lib/display-name.js.map +0 -1
- package/dist/src/lib/ids.js +0 -24
- package/dist/src/lib/ids.js.map +0 -1
- package/dist/src/lib/images.js +0 -19
- package/dist/src/lib/images.js.map +0 -1
- package/dist/src/lib/json.js +0 -15
- package/dist/src/lib/json.js.map +0 -1
- package/dist/src/lib/template-dna.js +0 -543
- package/dist/src/lib/template-dna.js.map +0 -1
- package/dist/src/lib/template-paths.js +0 -28
- package/dist/src/lib/template-paths.js.map +0 -1
- package/dist/src/lib/template-style-options.js +0 -203
- package/dist/src/lib/template-style-options.js.map +0 -1
- package/dist/src/lib/time.js +0 -7
- package/dist/src/lib/time.js.map +0 -1
- package/dist/src/lib/video-quality-harness.js +0 -60
- package/dist/src/lib/video-quality-harness.js.map +0 -1
- package/dist/src/page-runtime.js +0 -2
- package/dist/src/page-runtime.js.map +0 -1
- package/dist/src/page-shell.js +0 -2393
- package/dist/src/page-shell.js.map +0 -1
- package/dist/src/primitive-context.js +0 -299
- package/dist/src/primitive-context.js.map +0 -1
- package/dist/src/primitive-registry.js +0 -2181
- package/dist/src/primitive-registry.js.map +0 -1
- package/dist/src/primitive-sdk.js +0 -4
- package/dist/src/primitive-sdk.js.map +0 -1
- package/dist/src/primitives/remotion/html-image.js +0 -30
- package/dist/src/primitives/remotion/html-image.js.map +0 -1
- package/dist/src/primitives/remotion/media-slideshow.js +0 -60
- package/dist/src/primitives/remotion/media-slideshow.js.map +0 -1
- package/dist/src/react-page-shell.js +0 -35
- package/dist/src/react-page-shell.js.map +0 -1
- package/dist/src/ready-post-schedule-component.js +0 -1515
- package/dist/src/ready-post-schedule-component.js.map +0 -1
- package/dist/src/registry.js +0 -296
- package/dist/src/registry.js.map +0 -1
- package/dist/src/runtime.js +0 -31
- package/dist/src/runtime.js.map +0 -1
- package/dist/src/services/api-call-history.js +0 -249
- package/dist/src/services/api-call-history.js.map +0 -1
- package/dist/src/services/auth.js +0 -152
- package/dist/src/services/auth.js.map +0 -1
- package/dist/src/services/billing.js +0 -231
- package/dist/src/services/billing.js.map +0 -1
- package/dist/src/services/chat-threads.js +0 -92
- package/dist/src/services/chat-threads.js.map +0 -1
- package/dist/src/services/fork-access.js +0 -94
- package/dist/src/services/fork-access.js.map +0 -1
- package/dist/src/services/ghostcut.js +0 -132
- package/dist/src/services/ghostcut.js.map +0 -1
- package/dist/src/services/hyperframes.js +0 -1014
- package/dist/src/services/hyperframes.js.map +0 -1
- package/dist/src/services/job-capacity.js +0 -14
- package/dist/src/services/job-capacity.js.map +0 -1
- package/dist/src/services/job-logs.js +0 -197
- package/dist/src/services/job-logs.js.map +0 -1
- package/dist/src/services/jobs.js +0 -136
- package/dist/src/services/jobs.js.map +0 -1
- package/dist/src/services/media-processing.js +0 -743
- package/dist/src/services/media-processing.js.map +0 -1
- package/dist/src/services/primitive-media-lambda.js +0 -280
- package/dist/src/services/primitive-media-lambda.js.map +0 -1
- package/dist/src/services/providers.js +0 -2870
- package/dist/src/services/providers.js.map +0 -1
- package/dist/src/services/rate-limits.js +0 -237
- package/dist/src/services/rate-limits.js.map +0 -1
- package/dist/src/services/remotion.js +0 -688
- package/dist/src/services/remotion.js.map +0 -1
- package/dist/src/services/serverless-auth.js +0 -374
- package/dist/src/services/serverless-auth.js.map +0 -1
- package/dist/src/services/serverless-jobs.js +0 -1074
- package/dist/src/services/serverless-jobs.js.map +0 -1
- package/dist/src/services/serverless-provider-keys.js +0 -401
- package/dist/src/services/serverless-provider-keys.js.map +0 -1
- package/dist/src/services/serverless-records.js +0 -1086
- package/dist/src/services/serverless-records.js.map +0 -1
- package/dist/src/services/serverless-template-configs.js +0 -67
- package/dist/src/services/serverless-template-configs.js.map +0 -1
- package/dist/src/services/storage.js +0 -382
- package/dist/src/services/storage.js.map +0 -1
- package/dist/src/services/template-certification.js +0 -475
- package/dist/src/services/template-certification.js.map +0 -1
- package/dist/src/services/template-loader.js +0 -99
- package/dist/src/services/template-loader.js.map +0 -1
- package/dist/src/services/template-runtime-bundles.js +0 -217
- package/dist/src/services/template-runtime-bundles.js.map +0 -1
- package/dist/src/services/template-sources.js +0 -1017
- package/dist/src/services/template-sources.js.map +0 -1
- package/dist/src/services/video-normalization.js +0 -2
- package/dist/src/services/video-normalization.js.map +0 -1
- package/dist/src/services/webhooks.js +0 -62
- package/dist/src/services/webhooks.js.map +0 -1
- package/dist/src/template-editor-pages.js +0 -6624
- package/dist/src/template-editor-pages.js.map +0 -1
- package/dist/src/template-editor-shell.js +0 -2713
- package/dist/src/template-editor-shell.js.map +0 -1
- package/dist/src/template-sdk.js +0 -4
- package/dist/src/template-sdk.js.map +0 -1
- package/dist/src/worker.js +0 -17
- package/dist/src/worker.js.map +0 -1
- package/public/assets/homepage-app.js +0 -54
- package/public/assets/homepage-client-app.js +0 -80
- package/public/assets/page-runtime-client-app.js +0 -94
- package/readme.secret.md +0 -89
- package/templates/vidfarm_template_0000/README.md +0 -106
- package/templates/vidfarm_template_0000/SKILL.md +0 -266
- package/templates/vidfarm_template_0000/assets/Abel-Regular.ttf +0 -0
- package/templates/vidfarm_template_0000/assets/DMSerifDisplay-Regular.ttf +0 -0
- package/templates/vidfarm_template_0000/assets/Montserrat[wght].ttf +0 -0
- package/templates/vidfarm_template_0000/assets/SourceCodePro[wght].ttf +0 -0
- package/templates/vidfarm_template_0000/assets/TikTokSans-SemiBold.ttf +0 -0
- package/templates/vidfarm_template_0000/assets/Yesteryear-Regular.ttf +0 -0
- package/templates/vidfarm_template_0000/composition.json +0 -11
- package/templates/vidfarm_template_0000/src/lib/images.js +0 -221
- package/templates/vidfarm_template_0000/src/remotion/Root.js +0 -33
- package/templates/vidfarm_template_0000/src/remotion/index.js +0 -3
- package/templates/vidfarm_template_0000/src/sdk.js +0 -3
- package/templates/vidfarm_template_0000/src/style-options.js +0 -200
- package/templates/vidfarm_template_0000/src/template-dna.js +0 -9
- package/templates/vidfarm_template_0000/src/template.js +0 -1566
- package/templates/vidfarm_template_0000/template.config.json +0 -21
|
@@ -1,368 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from "node:child_process";
|
|
3
|
-
import { cp, copyFile, mkdir, readFile, stat, writeFile } from "node:fs/promises";
|
|
4
|
-
import path from "node:path";
|
|
5
|
-
import { fileURLToPath } from "node:url";
|
|
6
|
-
|
|
7
|
-
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
-
const harnessRoot = path.resolve(scriptDir, "..");
|
|
9
|
-
const repoRoot = path.resolve(harnessRoot, "..");
|
|
10
|
-
const defaultStackName = "hyperframes-vidfarm-prod";
|
|
11
|
-
const defaultRegion = "us-east-1";
|
|
12
|
-
const memoryBuckets = [2048, 3072, 4096, 6144, 8192, 10240];
|
|
13
|
-
const videoExts = new Set([".mp4", ".mov", ".m4v", ".webm"]);
|
|
14
|
-
const imageExts = new Set([".png", ".jpg", ".jpeg", ".webp"]);
|
|
15
|
-
|
|
16
|
-
const args = process.argv.slice(2);
|
|
17
|
-
const stageSite = args.includes("--stage-site");
|
|
18
|
-
const inputArg = args.find((arg) => !arg.startsWith("--"));
|
|
19
|
-
|
|
20
|
-
if (!inputArg) {
|
|
21
|
-
console.error("Usage: node auto-create-hyperframe-templates/scripts/create-hyperframe-template.mjs <media-path|input-manifest.json> [--stage-site]");
|
|
22
|
-
process.exit(1);
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
const manifest = await loadInputManifest(inputArg);
|
|
26
|
-
const outputDir = path.resolve(repoRoot, manifest.output_path);
|
|
27
|
-
const decomposeInput = await prepareInputForDecomposer(manifest);
|
|
28
|
-
|
|
29
|
-
await runCommand(process.execPath, [
|
|
30
|
-
path.join(repoRoot, "demo", "scripts", "decompose-static.mjs"),
|
|
31
|
-
path.relative(repoRoot, decomposeInput)
|
|
32
|
-
]);
|
|
33
|
-
|
|
34
|
-
const decompositionPath = path.join(outputDir, "composition.json");
|
|
35
|
-
const decomposition = JSON.parse(await readFile(decompositionPath, "utf8"));
|
|
36
|
-
const memoryEstimate = estimateLambdaMemory(decomposition, manifest);
|
|
37
|
-
const lambdaSite = await buildLambdaSiteManifest(manifest, outputDir, memoryEstimate, stageSite);
|
|
38
|
-
|
|
39
|
-
decomposition.schema = "vidfarm.hyperframe-decomposition.v1";
|
|
40
|
-
decomposition.lambda_memory_estimate = memoryEstimate;
|
|
41
|
-
decomposition.lambda_site = lambdaSite;
|
|
42
|
-
|
|
43
|
-
await writeFile(decompositionPath, `${JSON.stringify(decomposition, null, 2)}\n`);
|
|
44
|
-
await writeFile(path.join(outputDir, "lambda-site.json"), `${JSON.stringify(lambdaSite, null, 2)}\n`);
|
|
45
|
-
|
|
46
|
-
console.log(`Wrote ${path.relative(repoRoot, path.join(outputDir, "composition.html"))}`);
|
|
47
|
-
console.log(`Wrote ${path.relative(repoRoot, decompositionPath)}`);
|
|
48
|
-
console.log(`Wrote ${path.relative(repoRoot, path.join(outputDir, "lambda-site.json"))}`);
|
|
49
|
-
console.log(`Lambda memory estimate: ${memoryEstimate.recommended_memory_mb} MB (${memoryEstimate.confidence} confidence)`);
|
|
50
|
-
if (lambdaSite.site_id) {
|
|
51
|
-
console.log(`Lambda site id: ${lambdaSite.site_id}`);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
async function loadInputManifest(input) {
|
|
55
|
-
const inputPath = path.resolve(repoRoot, input);
|
|
56
|
-
const inputStats = await stat(inputPath);
|
|
57
|
-
if (inputStats.isFile() && path.extname(inputPath).toLowerCase() === ".json") {
|
|
58
|
-
const parsed = JSON.parse(await readFile(inputPath, "utf8"));
|
|
59
|
-
if (parsed.mode === "autocreate-hyperframe-template") {
|
|
60
|
-
return normalizeManifest(parsed, inputPath);
|
|
61
|
-
}
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
const outputPath = inputStats.isDirectory() ? inputPath : path.dirname(inputPath);
|
|
65
|
-
return normalizeManifest({
|
|
66
|
-
mode: "autocreate-hyperframe-template",
|
|
67
|
-
input_path: path.relative(repoRoot, inputPath),
|
|
68
|
-
output_path: path.relative(repoRoot, outputPath),
|
|
69
|
-
slug_id: path.basename(outputPath),
|
|
70
|
-
source_mode: "auto",
|
|
71
|
-
render_defaults: {
|
|
72
|
-
width: 720,
|
|
73
|
-
height: 1280,
|
|
74
|
-
fps: 30
|
|
75
|
-
},
|
|
76
|
-
lambda: {
|
|
77
|
-
stack_name: defaultStackName,
|
|
78
|
-
region: defaultRegion,
|
|
79
|
-
memory_strategy: "estimate"
|
|
80
|
-
},
|
|
81
|
-
publish: {
|
|
82
|
-
prestage_site: false,
|
|
83
|
-
output_url_policy: "signed_https_final_output_only"
|
|
84
|
-
}
|
|
85
|
-
}, inputPath);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
function normalizeManifest(manifest, manifestPath) {
|
|
89
|
-
if (manifest.mode !== "autocreate-hyperframe-template") {
|
|
90
|
-
throw new Error(`Invalid manifest mode in ${manifestPath}`);
|
|
91
|
-
}
|
|
92
|
-
return {
|
|
93
|
-
...manifest,
|
|
94
|
-
output_path: manifest.output_path || path.dirname(manifest.input_path),
|
|
95
|
-
source_mode: manifest.source_mode || "auto",
|
|
96
|
-
render_defaults: {
|
|
97
|
-
width: Number(manifest.render_defaults?.width || 720),
|
|
98
|
-
height: Number(manifest.render_defaults?.height || 1280),
|
|
99
|
-
fps: Number(manifest.render_defaults?.fps || 30)
|
|
100
|
-
},
|
|
101
|
-
lambda: {
|
|
102
|
-
stack_name: manifest.lambda?.stack_name || defaultStackName,
|
|
103
|
-
region: manifest.lambda?.region || defaultRegion,
|
|
104
|
-
memory_strategy: manifest.lambda?.memory_strategy || "estimate",
|
|
105
|
-
fixed_memory_mb: manifest.lambda?.fixed_memory_mb
|
|
106
|
-
},
|
|
107
|
-
publish: {
|
|
108
|
-
prestage_site: Boolean(manifest.publish?.prestage_site),
|
|
109
|
-
output_url_policy: "signed_https_final_output_only"
|
|
110
|
-
}
|
|
111
|
-
};
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
async function prepareInputForDecomposer(manifest) {
|
|
115
|
-
const inputPath = path.resolve(repoRoot, manifest.input_path);
|
|
116
|
-
const outputPath = path.resolve(repoRoot, manifest.output_path);
|
|
117
|
-
const inputStats = await stat(inputPath);
|
|
118
|
-
|
|
119
|
-
if (inputStats.isDirectory()) {
|
|
120
|
-
if (path.resolve(inputPath) !== path.resolve(outputPath)) {
|
|
121
|
-
await mkdir(outputPath, { recursive: true });
|
|
122
|
-
await cp(inputPath, outputPath, { recursive: true, force: true });
|
|
123
|
-
return outputPath;
|
|
124
|
-
}
|
|
125
|
-
return inputPath;
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
const ext = path.extname(inputPath).toLowerCase();
|
|
129
|
-
if (!videoExts.has(ext) && !imageExts.has(ext)) {
|
|
130
|
-
throw new Error(`Unsupported input media: ${inputPath}`);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
if (path.resolve(path.dirname(inputPath)) !== path.resolve(outputPath)) {
|
|
134
|
-
await mkdir(outputPath, { recursive: true });
|
|
135
|
-
const target = path.join(outputPath, path.basename(inputPath));
|
|
136
|
-
await copyFile(inputPath, target);
|
|
137
|
-
return target;
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return inputPath;
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
function estimateLambdaMemory(decomposition, manifest) {
|
|
144
|
-
const renderDefaults = manifest.render_defaults;
|
|
145
|
-
const layers = Array.isArray(decomposition.editor_layers) ? decomposition.editor_layers : [];
|
|
146
|
-
const assets = Array.isArray(decomposition.source_assets) ? decomposition.source_assets : [];
|
|
147
|
-
const firstAsset = assets[0] || {};
|
|
148
|
-
const width = Number(renderDefaults?.width || firstAsset.width || 720);
|
|
149
|
-
const height = Number(renderDefaults?.height || firstAsset.height || 1280);
|
|
150
|
-
const outputPixels = width * height;
|
|
151
|
-
const durationSeconds = layers.reduce((max, layer) => {
|
|
152
|
-
return Math.max(max, Number(layer.start || 0) + Number(layer.duration || 0));
|
|
153
|
-
}, Number(firstAsset.duration || firstAsset.duration_seconds || 0));
|
|
154
|
-
const videoLayers = layers.filter((layer) => layer.type === "video" || layer.kind === "video");
|
|
155
|
-
const audioLayers = layers.filter((layer) => layer.type === "audio" || layer.kind === "audio");
|
|
156
|
-
const textLayers = layers.filter((layer) => ["caption", "text", "shape", "html"].includes(layer.type || layer.kind));
|
|
157
|
-
const uniqueVideoSources = new Set(videoLayers.map((layer) => layer.src || layer.media?.src).filter(Boolean));
|
|
158
|
-
const maxSimultaneousVideos = maxOverlap(videoLayers);
|
|
159
|
-
|
|
160
|
-
const outputPixelScoreMb = outputPixels <= 1_000_000 ? 0 : outputPixels <= 2_100_000 ? 512 : outputPixels <= 4_000_000 ? 1024 : 2048;
|
|
161
|
-
let mediaScoreMb = 0;
|
|
162
|
-
if (videoLayers.length > 0 || assets.some((asset) => asset.type === "video")) {
|
|
163
|
-
mediaScoreMb += 768;
|
|
164
|
-
mediaScoreMb += Math.max(0, maxSimultaneousVideos - 1) * 512;
|
|
165
|
-
mediaScoreMb += Math.max(0, uniqueVideoSources.size - 1) * 256;
|
|
166
|
-
if (assets.some((asset) => Number(asset.width || 0) * Number(asset.height || 0) > outputPixels * 1.75)) {
|
|
167
|
-
mediaScoreMb += 512;
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
let domCaptionScoreMb = layers.length <= 10 ? 0 : layers.length <= 30 ? 256 : 512;
|
|
172
|
-
if (textLayers.length >= 4) domCaptionScoreMb += 256;
|
|
173
|
-
if (textLayers.some((layer) => String(layer.textBackgroundStyle || layer.background_style || "").includes("highlight"))) {
|
|
174
|
-
domCaptionScoreMb += 256;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
let effectScoreMb = 0;
|
|
178
|
-
if (textLayers.some((layer) => ["outline", "radial_halo"].includes(layer.textBackgroundStyle || layer.background_style))) {
|
|
179
|
-
effectScoreMb += 256;
|
|
180
|
-
}
|
|
181
|
-
if (layers.some((layer) => layer.filter || layer.mask || layer.mixBlendMode || layer.blur)) {
|
|
182
|
-
effectScoreMb += 512;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
let durationAudioScoreMb = durationSeconds <= 30 ? 0 : durationSeconds <= 60 ? 256 : durationSeconds <= 180 ? 512 : 1024;
|
|
186
|
-
if (audioLayers.length > 0 || assets.some((asset) => asset.type === "audio" || asset.type === "video")) {
|
|
187
|
-
durationAudioScoreMb += 256;
|
|
188
|
-
}
|
|
189
|
-
if (audioLayers.length > 1) durationAudioScoreMb += 256;
|
|
190
|
-
|
|
191
|
-
const rawEstimateMb = 1536 + outputPixelScoreMb + mediaScoreMb + domCaptionScoreMb + effectScoreMb + durationAudioScoreMb;
|
|
192
|
-
let roundedBucketMb = memoryBuckets.find((bucket) => bucket >= rawEstimateMb) || 10240;
|
|
193
|
-
if (videoLayers.length === 0 && !assets.some((asset) => asset.type === "video")) {
|
|
194
|
-
roundedBucketMb = Math.min(roundedBucketMb, 2048);
|
|
195
|
-
}
|
|
196
|
-
if ((videoLayers.length > 0 || assets.some((asset) => asset.type === "video")) && maxSimultaneousVideos <= 1 && roundedBucketMb < 3072) {
|
|
197
|
-
roundedBucketMb = 3072;
|
|
198
|
-
}
|
|
199
|
-
if (roundedBucketMb > 4096 && rawEstimateMb < 6144) {
|
|
200
|
-
roundedBucketMb = 4096;
|
|
201
|
-
}
|
|
202
|
-
const fixedMemory = manifest.lambda?.memory_strategy === "fixed"
|
|
203
|
-
? Number(manifest.lambda?.fixed_memory_mb || 0)
|
|
204
|
-
: 0;
|
|
205
|
-
const recommendedMemoryMb = memoryBuckets.includes(fixedMemory) ? fixedMemory : roundedBucketMb;
|
|
206
|
-
const estimatedBilledSeconds = estimateBilledSeconds(durationSeconds, layers.length, videoLayers.length);
|
|
207
|
-
const estimatedLambdaGbSeconds = (recommendedMemoryMb / 1024) * estimatedBilledSeconds;
|
|
208
|
-
const estimatedCostUsd = estimatedLambdaGbSeconds * 0.0000166667 + 40 * 0.000025;
|
|
209
|
-
|
|
210
|
-
const riskFlags = [];
|
|
211
|
-
if (videoLayers.length > 0 || assets.some((asset) => asset.type === "video")) riskFlags.push("source_video_decode");
|
|
212
|
-
if (maxSimultaneousVideos > 1) riskFlags.push("multiple_simultaneous_video_streams");
|
|
213
|
-
if (textLayers.length >= 4) riskFlags.push("caption_or_html_overlay_density");
|
|
214
|
-
if (audioLayers.length > 0 || assets.some((asset) => asset.type === "video")) riskFlags.push("continuous_audio");
|
|
215
|
-
if (outputPixels > 2_100_000) riskFlags.push("high_output_resolution");
|
|
216
|
-
|
|
217
|
-
return {
|
|
218
|
-
recommended_memory_mb: recommendedMemoryMb,
|
|
219
|
-
confidence: riskFlags.length >= 3 ? "medium" : "high",
|
|
220
|
-
complexity_scores: {
|
|
221
|
-
output_pixels: outputPixels,
|
|
222
|
-
output_pixel_score_mb: outputPixelScoreMb,
|
|
223
|
-
max_simultaneous_video_streams: maxSimultaneousVideos,
|
|
224
|
-
unique_video_sources: uniqueVideoSources.size,
|
|
225
|
-
media_score_mb: mediaScoreMb,
|
|
226
|
-
layer_count: layers.length,
|
|
227
|
-
text_layer_count: textLayers.length,
|
|
228
|
-
dom_caption_score_mb: domCaptionScoreMb,
|
|
229
|
-
effect_score_mb: effectScoreMb,
|
|
230
|
-
duration_seconds: Number(durationSeconds.toFixed(3)),
|
|
231
|
-
duration_audio_score_mb: durationAudioScoreMb,
|
|
232
|
-
raw_estimate_mb: rawEstimateMb,
|
|
233
|
-
rounded_bucket_mb: roundedBucketMb,
|
|
234
|
-
estimated_billed_seconds: estimatedBilledSeconds
|
|
235
|
-
},
|
|
236
|
-
risk_flags: riskFlags,
|
|
237
|
-
rationale: rationaleForEstimate(recommendedMemoryMb, maxSimultaneousVideos, videoLayers.length, outputPixels, textLayers.length),
|
|
238
|
-
benchmark_override: null,
|
|
239
|
-
estimated_lambda_gb_seconds: Number(estimatedLambdaGbSeconds.toFixed(3)),
|
|
240
|
-
estimated_cost_usd: Number(estimatedCostUsd.toFixed(4))
|
|
241
|
-
};
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
function maxOverlap(layers) {
|
|
245
|
-
if (layers.length === 0) return 0;
|
|
246
|
-
const points = [];
|
|
247
|
-
for (const layer of layers) {
|
|
248
|
-
const start = Number(layer.start || 0);
|
|
249
|
-
const end = start + Number(layer.duration || 0);
|
|
250
|
-
if (end > start) {
|
|
251
|
-
points.push(start + (end - start) / 2);
|
|
252
|
-
points.push(start + 0.001);
|
|
253
|
-
}
|
|
254
|
-
}
|
|
255
|
-
return points.reduce((max, point) => {
|
|
256
|
-
const active = layers.filter((layer) => {
|
|
257
|
-
const start = Number(layer.start || 0);
|
|
258
|
-
const end = start + Number(layer.duration || 0);
|
|
259
|
-
return point >= start && point < end;
|
|
260
|
-
}).length;
|
|
261
|
-
return Math.max(max, active);
|
|
262
|
-
}, 0);
|
|
263
|
-
}
|
|
264
|
-
|
|
265
|
-
function estimateBilledSeconds(durationSeconds, layerCount, videoLayerCount) {
|
|
266
|
-
const base = videoLayerCount > 0 ? 90 : 20;
|
|
267
|
-
const durationFactor = durationSeconds * (videoLayerCount > 0 ? 8 : 4);
|
|
268
|
-
const layerFactor = layerCount * 2;
|
|
269
|
-
return Math.ceil(Math.max(base, durationFactor + layerFactor));
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
function rationaleForEstimate(memoryMb, maxVideos, videoLayerCount, outputPixels, textLayerCount) {
|
|
273
|
-
if (videoLayerCount === 0) {
|
|
274
|
-
return "No active video decode pressure; simple static/slideshow output fits the 2GB bucket.";
|
|
275
|
-
}
|
|
276
|
-
if (memoryMb === 3072) {
|
|
277
|
-
return "One active social-video stream with deterministic overlays fits the measured 3GB production baseline.";
|
|
278
|
-
}
|
|
279
|
-
if (memoryMb === 4096) {
|
|
280
|
-
return `Raised to 4GB for ${maxVideos} simultaneous video stream(s), ${Math.round(outputPixels / 1_000_000 * 10) / 10}MP output, and ${textLayerCount} text/html layer(s).`;
|
|
281
|
-
}
|
|
282
|
-
return "Raised above 4GB because composition complexity crosses the normal social-video range.";
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
async function buildLambdaSiteManifest(manifest, outputDir, memoryEstimate, shouldStageSite) {
|
|
286
|
-
const relativeOutput = path.relative(repoRoot, outputDir);
|
|
287
|
-
const stackName = manifest.lambda.stack_name || defaultStackName;
|
|
288
|
-
const region = manifest.lambda.region || defaultRegion;
|
|
289
|
-
const siteUploadCommand = `demo/node_modules/.bin/hyperframes lambda sites create ${shellQuote(relativeOutput)} --stack-name ${shellQuote(stackName)} --region ${shellQuote(region)}`;
|
|
290
|
-
const site = {
|
|
291
|
-
schema: "vidfarm.hyperframe.lambda-site.v1",
|
|
292
|
-
stack_name: stackName,
|
|
293
|
-
region,
|
|
294
|
-
site_id: null,
|
|
295
|
-
project_dir: relativeOutput,
|
|
296
|
-
composition_file: "composition.html",
|
|
297
|
-
memory_mb: memoryEstimate.recommended_memory_mb,
|
|
298
|
-
render_defaults: manifest.render_defaults,
|
|
299
|
-
output_url_policy: "signed_https_final_output_only",
|
|
300
|
-
prestage_site_requested: Boolean(manifest.publish?.prestage_site),
|
|
301
|
-
site_upload_command: siteUploadCommand,
|
|
302
|
-
estimated_lambda_gb_seconds: memoryEstimate.estimated_lambda_gb_seconds,
|
|
303
|
-
estimated_cost_usd: memoryEstimate.estimated_cost_usd
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
if (!shouldStageSite) {
|
|
307
|
-
return site;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
try {
|
|
311
|
-
const hyperframesBin = path.join(repoRoot, "demo", "node_modules", ".bin", "hyperframes");
|
|
312
|
-
const stdout = await runCommand(hyperframesBin, [
|
|
313
|
-
"lambda",
|
|
314
|
-
"sites",
|
|
315
|
-
"create",
|
|
316
|
-
relativeOutput,
|
|
317
|
-
"--stack-name",
|
|
318
|
-
stackName,
|
|
319
|
-
"--region",
|
|
320
|
-
region
|
|
321
|
-
]);
|
|
322
|
-
site.site_id = parseSiteId(stdout);
|
|
323
|
-
site.site_upload_stdout = stdout.trim();
|
|
324
|
-
} catch (error) {
|
|
325
|
-
site.site_upload_error = error instanceof Error ? error.message : String(error);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
return site;
|
|
329
|
-
}
|
|
330
|
-
|
|
331
|
-
function parseSiteId(stdout) {
|
|
332
|
-
const text = String(stdout || "");
|
|
333
|
-
const labeled = text.match(/Site ID:\s*([A-Za-z0-9_-]+)/i);
|
|
334
|
-
if (labeled) return labeled[1];
|
|
335
|
-
const generic = text.match(/\b([a-f0-9]{12,64}|[A-Za-z0-9_-]{12,64})\b/);
|
|
336
|
-
return generic ? generic[1] : null;
|
|
337
|
-
}
|
|
338
|
-
|
|
339
|
-
function runCommand(command, commandArgs) {
|
|
340
|
-
return new Promise((resolve, reject) => {
|
|
341
|
-
const child = spawn(command, commandArgs, {
|
|
342
|
-
cwd: repoRoot,
|
|
343
|
-
env: process.env,
|
|
344
|
-
stdio: ["ignore", "pipe", "pipe"]
|
|
345
|
-
});
|
|
346
|
-
let stdout = "";
|
|
347
|
-
let stderr = "";
|
|
348
|
-
child.stdout.on("data", (chunk) => {
|
|
349
|
-
stdout += String(chunk);
|
|
350
|
-
process.stdout.write(chunk);
|
|
351
|
-
});
|
|
352
|
-
child.stderr.on("data", (chunk) => {
|
|
353
|
-
stderr += String(chunk);
|
|
354
|
-
process.stderr.write(chunk);
|
|
355
|
-
});
|
|
356
|
-
child.on("error", reject);
|
|
357
|
-
child.on("close", (code) => {
|
|
358
|
-
if (code === 0) resolve(stdout);
|
|
359
|
-
else reject(new Error(`${command} exited ${code}: ${stderr}`));
|
|
360
|
-
});
|
|
361
|
-
});
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
function shellQuote(value) {
|
|
365
|
-
const text = String(value);
|
|
366
|
-
if (/^[A-Za-z0-9_./:=@-]+$/.test(text)) return text;
|
|
367
|
-
return `'${text.replaceAll("'", "'\\''")}'`;
|
|
368
|
-
}
|
|
@@ -1,311 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
|
-
"title": "HyperFrame Template Plan",
|
|
4
|
-
"type": "object",
|
|
5
|
-
"required": [
|
|
6
|
-
"template",
|
|
7
|
-
"composition",
|
|
8
|
-
"source_assets",
|
|
9
|
-
"layers",
|
|
10
|
-
"caption_layout",
|
|
11
|
-
"editor_behavior",
|
|
12
|
-
"lambda",
|
|
13
|
-
"deployment",
|
|
14
|
-
"artifacts",
|
|
15
|
-
"validation",
|
|
16
|
-
"implementation_files"
|
|
17
|
-
],
|
|
18
|
-
"properties": {
|
|
19
|
-
"template": {
|
|
20
|
-
"type": "object",
|
|
21
|
-
"required": ["slug_id", "title", "description", "source_folder", "source_type"],
|
|
22
|
-
"properties": {
|
|
23
|
-
"slug_id": { "type": "string" },
|
|
24
|
-
"title": { "type": "string" },
|
|
25
|
-
"description": { "type": "string" },
|
|
26
|
-
"source_folder": { "type": "string" },
|
|
27
|
-
"source_type": { "enum": ["single_video", "scene_split_video", "slideshow", "mixed_media"] },
|
|
28
|
-
"viral_dna": { "type": "object" },
|
|
29
|
-
"visual_dna": { "type": "object" }
|
|
30
|
-
},
|
|
31
|
-
"additionalProperties": false
|
|
32
|
-
},
|
|
33
|
-
"composition": {
|
|
34
|
-
"type": "object",
|
|
35
|
-
"required": ["composition_id", "width", "height", "fps", "duration_seconds", "background_color", "root_attributes"],
|
|
36
|
-
"properties": {
|
|
37
|
-
"composition_id": { "type": "string" },
|
|
38
|
-
"width": { "type": "integer", "minimum": 1 },
|
|
39
|
-
"height": { "type": "integer", "minimum": 1 },
|
|
40
|
-
"fps": { "type": "number", "minimum": 1 },
|
|
41
|
-
"duration_seconds": { "type": "number", "minimum": 0 },
|
|
42
|
-
"background_color": { "type": "string" },
|
|
43
|
-
"root_attributes": {
|
|
44
|
-
"type": "array",
|
|
45
|
-
"items": { "type": "string" }
|
|
46
|
-
}
|
|
47
|
-
},
|
|
48
|
-
"additionalProperties": false
|
|
49
|
-
},
|
|
50
|
-
"source_assets": {
|
|
51
|
-
"type": "array",
|
|
52
|
-
"items": { "$ref": "#/$defs/sourceAsset" }
|
|
53
|
-
},
|
|
54
|
-
"layers": {
|
|
55
|
-
"type": "array",
|
|
56
|
-
"items": { "$ref": "#/$defs/layer" }
|
|
57
|
-
},
|
|
58
|
-
"caption_layout": {
|
|
59
|
-
"type": "object",
|
|
60
|
-
"required": ["safe_areas", "default_style", "surfaces", "rules"],
|
|
61
|
-
"properties": {
|
|
62
|
-
"safe_areas": { "$ref": "#/$defs/safeAreas" },
|
|
63
|
-
"default_style": { "$ref": "#/$defs/textStyle" },
|
|
64
|
-
"surfaces": {
|
|
65
|
-
"type": "array",
|
|
66
|
-
"items": {
|
|
67
|
-
"type": "object",
|
|
68
|
-
"required": ["id", "layer_ids", "geometry", "rules"],
|
|
69
|
-
"properties": {
|
|
70
|
-
"id": { "type": "string" },
|
|
71
|
-
"layer_ids": { "type": "array", "items": { "type": "string" } },
|
|
72
|
-
"geometry": { "$ref": "#/$defs/geometry" },
|
|
73
|
-
"rules": { "type": "array", "items": { "type": "string" } }
|
|
74
|
-
},
|
|
75
|
-
"additionalProperties": false
|
|
76
|
-
}
|
|
77
|
-
},
|
|
78
|
-
"rules": { "type": "array", "items": { "type": "string" } }
|
|
79
|
-
},
|
|
80
|
-
"additionalProperties": false
|
|
81
|
-
},
|
|
82
|
-
"editor_behavior": {
|
|
83
|
-
"type": "object",
|
|
84
|
-
"required": ["publish_source_of_truth", "track_collision_policy", "delete_scene_test"],
|
|
85
|
-
"properties": {
|
|
86
|
-
"publish_source_of_truth": { "type": "string" },
|
|
87
|
-
"preview_playback_source_allowed": { "type": "boolean" },
|
|
88
|
-
"track_collision_policy": { "type": "string" },
|
|
89
|
-
"delete_scene_test": { "type": "string" }
|
|
90
|
-
},
|
|
91
|
-
"additionalProperties": false
|
|
92
|
-
},
|
|
93
|
-
"lambda": {
|
|
94
|
-
"type": "object",
|
|
95
|
-
"required": ["memory_estimate", "render_defaults"],
|
|
96
|
-
"properties": {
|
|
97
|
-
"memory_estimate": { "$ref": "#/$defs/lambdaMemoryEstimate" },
|
|
98
|
-
"render_defaults": {
|
|
99
|
-
"type": "object",
|
|
100
|
-
"required": ["width", "height", "fps"],
|
|
101
|
-
"properties": {
|
|
102
|
-
"width": { "type": "integer", "minimum": 1 },
|
|
103
|
-
"height": { "type": "integer", "minimum": 1 },
|
|
104
|
-
"fps": { "type": "number", "minimum": 1 }
|
|
105
|
-
},
|
|
106
|
-
"additionalProperties": false
|
|
107
|
-
},
|
|
108
|
-
"estimated_lambda_gb_seconds": { "type": "number", "minimum": 0 },
|
|
109
|
-
"estimated_cost_usd": { "type": "number", "minimum": 0 }
|
|
110
|
-
},
|
|
111
|
-
"additionalProperties": false
|
|
112
|
-
},
|
|
113
|
-
"deployment": {
|
|
114
|
-
"type": "object",
|
|
115
|
-
"required": ["prod_lambda_site", "output_url_policy"],
|
|
116
|
-
"properties": {
|
|
117
|
-
"prod_lambda_site": {
|
|
118
|
-
"type": "object",
|
|
119
|
-
"required": ["schema", "stack_name", "region", "site_id", "project_dir", "composition_file", "memory_mb"],
|
|
120
|
-
"properties": {
|
|
121
|
-
"schema": { "const": "vidfarm.hyperframe.lambda-site.v1" },
|
|
122
|
-
"stack_name": { "type": "string" },
|
|
123
|
-
"region": { "type": "string" },
|
|
124
|
-
"site_id": { "type": ["string", "null"] },
|
|
125
|
-
"project_dir": { "type": "string" },
|
|
126
|
-
"composition_file": { "type": "string" },
|
|
127
|
-
"memory_mb": { "enum": [2048, 3072, 4096, 6144, 8192, 10240] },
|
|
128
|
-
"render_defaults": {
|
|
129
|
-
"type": "object",
|
|
130
|
-
"properties": {
|
|
131
|
-
"width": { "type": "integer", "minimum": 1 },
|
|
132
|
-
"height": { "type": "integer", "minimum": 1 },
|
|
133
|
-
"fps": { "type": "number", "minimum": 1 }
|
|
134
|
-
},
|
|
135
|
-
"additionalProperties": false
|
|
136
|
-
},
|
|
137
|
-
"output_url_policy": { "enum": ["signed_https_final_output_only"] },
|
|
138
|
-
"prestage_site_requested": { "type": "boolean" },
|
|
139
|
-
"site_upload_command": { "type": "string" },
|
|
140
|
-
"site_upload_api": { "type": "string" },
|
|
141
|
-
"site_upload_stdout": { "type": "string" },
|
|
142
|
-
"site_upload_error": { "type": "string" },
|
|
143
|
-
"estimated_lambda_gb_seconds": { "type": "number", "minimum": 0 },
|
|
144
|
-
"estimated_cost_usd": { "type": "number", "minimum": 0 }
|
|
145
|
-
},
|
|
146
|
-
"additionalProperties": false
|
|
147
|
-
},
|
|
148
|
-
"output_url_policy": { "enum": ["signed_https_final_output_only"] }
|
|
149
|
-
},
|
|
150
|
-
"additionalProperties": false
|
|
151
|
-
},
|
|
152
|
-
"artifacts": {
|
|
153
|
-
"type": "object",
|
|
154
|
-
"required": ["files", "decomposition_shape", "lambda_site_shape"],
|
|
155
|
-
"properties": {
|
|
156
|
-
"files": { "type": "array", "items": { "type": "string" } },
|
|
157
|
-
"decomposition_shape": { "type": "object" },
|
|
158
|
-
"lambda_site_shape": { "type": "object" }
|
|
159
|
-
},
|
|
160
|
-
"additionalProperties": false
|
|
161
|
-
},
|
|
162
|
-
"validation": {
|
|
163
|
-
"type": "object",
|
|
164
|
-
"required": ["artifact_checks", "timeline_checks", "publish_checks", "caption_checks", "lambda_checks", "final_output_checks"],
|
|
165
|
-
"properties": {
|
|
166
|
-
"artifact_checks": { "type": "array", "items": { "type": "string" } },
|
|
167
|
-
"timeline_checks": { "type": "array", "items": { "type": "string" } },
|
|
168
|
-
"publish_checks": { "type": "array", "items": { "type": "string" } },
|
|
169
|
-
"caption_checks": { "type": "array", "items": { "type": "string" } },
|
|
170
|
-
"lambda_checks": { "type": "array", "items": { "type": "string" } },
|
|
171
|
-
"final_output_checks": { "type": "array", "items": { "type": "string" } }
|
|
172
|
-
},
|
|
173
|
-
"additionalProperties": false
|
|
174
|
-
},
|
|
175
|
-
"implementation_files": {
|
|
176
|
-
"type": "array",
|
|
177
|
-
"items": {
|
|
178
|
-
"type": "object",
|
|
179
|
-
"required": ["path", "purpose"],
|
|
180
|
-
"properties": {
|
|
181
|
-
"path": { "type": "string" },
|
|
182
|
-
"purpose": { "type": "string" }
|
|
183
|
-
},
|
|
184
|
-
"additionalProperties": false
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
},
|
|
188
|
-
"additionalProperties": false,
|
|
189
|
-
"$defs": {
|
|
190
|
-
"sourceAsset": {
|
|
191
|
-
"type": "object",
|
|
192
|
-
"required": ["id", "type", "path"],
|
|
193
|
-
"properties": {
|
|
194
|
-
"id": { "type": "string" },
|
|
195
|
-
"type": { "enum": ["video", "image", "audio", "font", "json", "html"] },
|
|
196
|
-
"path": { "type": "string" },
|
|
197
|
-
"width": { "type": "integer", "minimum": 0 },
|
|
198
|
-
"height": { "type": "integer", "minimum": 0 },
|
|
199
|
-
"duration_seconds": { "type": "number", "minimum": 0 },
|
|
200
|
-
"role": { "type": "string" }
|
|
201
|
-
},
|
|
202
|
-
"additionalProperties": false
|
|
203
|
-
},
|
|
204
|
-
"geometry": {
|
|
205
|
-
"type": "object",
|
|
206
|
-
"required": ["x", "y", "width", "height"],
|
|
207
|
-
"properties": {
|
|
208
|
-
"x": { "type": "number" },
|
|
209
|
-
"y": { "type": "number" },
|
|
210
|
-
"width": { "type": "number", "exclusiveMinimum": 0 },
|
|
211
|
-
"height": { "type": "number", "exclusiveMinimum": 0 },
|
|
212
|
-
"unit": { "const": "percent" }
|
|
213
|
-
},
|
|
214
|
-
"additionalProperties": false
|
|
215
|
-
},
|
|
216
|
-
"safeAreas": {
|
|
217
|
-
"type": "object",
|
|
218
|
-
"properties": {
|
|
219
|
-
"top_percent": { "type": "number", "minimum": 0 },
|
|
220
|
-
"right_percent": { "type": "number", "minimum": 0 },
|
|
221
|
-
"bottom_percent": { "type": "number", "minimum": 0 },
|
|
222
|
-
"left_percent": { "type": "number", "minimum": 0 },
|
|
223
|
-
"notes": { "type": "array", "items": { "type": "string" } }
|
|
224
|
-
},
|
|
225
|
-
"additionalProperties": false
|
|
226
|
-
},
|
|
227
|
-
"textStyle": {
|
|
228
|
-
"type": "object",
|
|
229
|
-
"properties": {
|
|
230
|
-
"font_family": { "type": "string" },
|
|
231
|
-
"font_weight": { "type": "integer" },
|
|
232
|
-
"font_size_px": { "type": "number" },
|
|
233
|
-
"line_height": { "type": "number" },
|
|
234
|
-
"color": { "type": "string" },
|
|
235
|
-
"background": { "type": "string" },
|
|
236
|
-
"background_style": { "enum": ["plain", "outline", "radial_halo", "highlight_solid", "highlight_translucent", "panel"] },
|
|
237
|
-
"text_align": { "enum": ["left", "center", "right"] },
|
|
238
|
-
"max_lines": { "type": "integer", "minimum": 1 }
|
|
239
|
-
},
|
|
240
|
-
"additionalProperties": false
|
|
241
|
-
},
|
|
242
|
-
"layer": {
|
|
243
|
-
"type": "object",
|
|
244
|
-
"required": ["id", "kind", "start", "duration", "track", "z", "mode", "source_of_truth", "geometry", "viral_alignment"],
|
|
245
|
-
"properties": {
|
|
246
|
-
"id": { "type": "string" },
|
|
247
|
-
"kind": { "enum": ["video", "image", "audio", "caption", "html", "shape", "effect"] },
|
|
248
|
-
"start": { "type": "number", "minimum": 0 },
|
|
249
|
-
"duration": { "type": "number", "exclusiveMinimum": 0 },
|
|
250
|
-
"track": { "type": "integer", "minimum": 0 },
|
|
251
|
-
"z": { "type": "integer" },
|
|
252
|
-
"mode": { "enum": ["both", "editor", "publish"] },
|
|
253
|
-
"source_of_truth": { "enum": ["source_media", "deterministic_html", "generated_asset", "user_asset", "computed_effect"] },
|
|
254
|
-
"geometry": { "$ref": "#/$defs/geometry" },
|
|
255
|
-
"media": {
|
|
256
|
-
"type": "object",
|
|
257
|
-
"properties": {
|
|
258
|
-
"src": { "type": "string" },
|
|
259
|
-
"asset_id": { "type": "string" },
|
|
260
|
-
"media_start": { "type": "number", "minimum": 0 },
|
|
261
|
-
"media_end": { "type": "number", "minimum": 0 },
|
|
262
|
-
"fit": { "enum": ["cover", "contain", "fill", "none"] },
|
|
263
|
-
"muted": { "type": "boolean" },
|
|
264
|
-
"volume": { "type": "number", "minimum": 0 }
|
|
265
|
-
},
|
|
266
|
-
"additionalProperties": false
|
|
267
|
-
},
|
|
268
|
-
"text": {
|
|
269
|
-
"type": "object",
|
|
270
|
-
"properties": {
|
|
271
|
-
"content": { "type": "string" },
|
|
272
|
-
"style": { "$ref": "#/$defs/textStyle" }
|
|
273
|
-
},
|
|
274
|
-
"additionalProperties": false
|
|
275
|
-
},
|
|
276
|
-
"html_contract": {
|
|
277
|
-
"type": "object",
|
|
278
|
-
"properties": {
|
|
279
|
-
"description": { "type": "string" },
|
|
280
|
-
"required_classes": { "type": "array", "items": { "type": "string" } },
|
|
281
|
-
"editable_fields": { "type": "array", "items": { "type": "string" } }
|
|
282
|
-
},
|
|
283
|
-
"additionalProperties": false
|
|
284
|
-
},
|
|
285
|
-
"viral_alignment": { "type": "string" },
|
|
286
|
-
"publish_notes": { "type": "string" }
|
|
287
|
-
},
|
|
288
|
-
"additionalProperties": false
|
|
289
|
-
},
|
|
290
|
-
"lambdaMemoryEstimate": {
|
|
291
|
-
"type": "object",
|
|
292
|
-
"required": ["recommended_memory_mb", "confidence", "complexity_scores", "rationale", "risk_flags"],
|
|
293
|
-
"properties": {
|
|
294
|
-
"recommended_memory_mb": { "enum": [2048, 3072, 4096, 6144, 8192, 10240] },
|
|
295
|
-
"confidence": { "enum": ["low", "medium", "high"] },
|
|
296
|
-
"complexity_scores": { "type": "object", "additionalProperties": { "type": ["number", "integer", "string", "boolean"] } },
|
|
297
|
-
"rationale": { "type": "string" },
|
|
298
|
-
"risk_flags": { "type": "array", "items": { "type": "string" } },
|
|
299
|
-
"benchmark_override": {
|
|
300
|
-
"type": ["object", "null"],
|
|
301
|
-
"properties": {
|
|
302
|
-
"memory_mb": { "enum": [2048, 3072, 4096, 6144, 8192, 10240] },
|
|
303
|
-
"reason": { "type": "string" }
|
|
304
|
-
},
|
|
305
|
-
"additionalProperties": false
|
|
306
|
-
}
|
|
307
|
-
},
|
|
308
|
-
"additionalProperties": false
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|