@mevdragon/vidfarm-devcli 0.20.5 → 0.20.8
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/.agents/skills/editor-capabilities/SKILL.md +15 -1
- package/.agents/skills/vidfarm-director/SKILL.md +106 -0
- package/.agents/skills/vidfarm-director/recipes/find-and-fork-template.md +16 -0
- package/.agents/skills/vidfarm-director/recipes/local-edit-render-approve.md +13 -0
- package/.agents/skills/vidfarm-director/recipes/onboard-a-new-director.md +13 -0
- package/.agents/skills/vidfarm-director/recipes/retheme-template.md +17 -0
- package/.agents/skills/vidfarm-director/references/assets-and-sourcing.md +111 -0
- package/.agents/skills/vidfarm-director/references/automation-and-local-dev.md +243 -0
- package/.agents/skills/vidfarm-director/references/core-workflows.md +273 -0
- package/.agents/skills/vidfarm-director/references/editor-workflows.md +365 -0
- package/.agents/skills/vidfarm-director/references/onboarding.md +28 -0
- package/.agents/skills/vidfarm-director/references/primitives.md +265 -0
- package/SKILL.director.md +521 -261
- package/dist/src/cli.js +404 -96
- package/dist/src/devcli/clips.js +15 -10
- package/dist/src/devcli/doctor.js +2 -2
- package/dist/src/devcli/local-backend.js +46 -2
- package/dist/src/services/clip-curation/cost.js +5 -1
- package/dist/src/services/clip-curation/gemini.js +10 -0
- package/package.json +30 -10
- package/SKILL.platform.md +0 -432
- package/demo/README.md +0 -28
- package/demo/dist/app.css +0 -1
- package/demo/dist/app.js +0 -1850
- package/demo/dist/chunks/chunk-DXB73IDG.js +0 -1
- package/demo/dist/chunks/chunk-S7OWAJDS.js +0 -36
- package/demo/dist/chunks/chunk-VTIBZ6AN.js +0 -1
- package/demo/dist/chunks/dist-ADSJKBVE.js +0 -332
- package/demo/dist/chunks/domEditingLayers-VZMLL4AP-SGHWPND4.js +0 -1
- package/demo/dist/chunks/hyperframes-player-XB65TCD6.js +0 -425
- package/demo/dist/chunks/lib-XAQ37YOE.js +0 -1
- package/demo/dist/chunks/src-TJ2QYA4U.js +0 -207
- package/demo/dist/favicon.ico +0 -0
- package/demo/dist/icons/timeline/audio.svg +0 -7
- package/demo/dist/icons/timeline/captions.svg +0 -5
- package/demo/dist/icons/timeline/composition.svg +0 -12
- package/demo/dist/icons/timeline/image.svg +0 -18
- package/demo/dist/icons/timeline/music.svg +0 -10
- package/demo/dist/icons/timeline/text.svg +0 -3
- package/demo/dist/index.html +0 -15
- package/dist/src/account-pages-legacy.js +0 -9396
- package/dist/src/account-pages.js +0 -61
- package/dist/src/app.js +0 -21602
- package/dist/src/composition-runtime.js +0 -1053
- package/dist/src/config.js +0 -217
- package/dist/src/context.js +0 -447
- package/dist/src/dev-app-legacy.js +0 -739
- package/dist/src/dev-app.js +0 -6
- package/dist/src/devcli/migrate-local.js +0 -140
- package/dist/src/devcli/sync.js +0 -368
- package/dist/src/domain.js +0 -5
- package/dist/src/editor-chat-history.js +0 -82
- package/dist/src/editor-chat.js +0 -756
- package/dist/src/editor-dark-theme.js +0 -1128
- package/dist/src/frontend/debug.js +0 -71
- package/dist/src/frontend/discover-client.js +0 -130
- package/dist/src/frontend/discover-store.js +0 -23
- package/dist/src/frontend/file-directory.js +0 -1018
- package/dist/src/frontend/flockposter-cache-store.js +0 -124
- package/dist/src/frontend/homepage-client.js +0 -446
- package/dist/src/frontend/homepage-shared.js +0 -201
- package/dist/src/frontend/homepage-store.js +0 -66
- package/dist/src/frontend/homepage-view.js +0 -705
- package/dist/src/frontend/page-runtime-client.js +0 -132
- package/dist/src/frontend/page-runtime-store.js +0 -9
- package/dist/src/frontend/sentry.js +0 -42
- package/dist/src/frontend/template-editor-chat.js +0 -4181
- package/dist/src/help-page.js +0 -346
- package/dist/src/homepage.js +0 -1458
- package/dist/src/index.js +0 -16
- package/dist/src/instrument.js +0 -30
- package/dist/src/landing-page.js +0 -384
- package/dist/src/page-runtime.js +0 -2
- package/dist/src/page-shell.js +0 -1452
- package/dist/src/primitive-context.js +0 -416
- package/dist/src/primitive-registry.js +0 -3940
- package/dist/src/primitive-sdk.js +0 -4
- package/dist/src/primitives/hyperframes-media.js +0 -108
- package/dist/src/react-page-shell.js +0 -35
- package/dist/src/ready-post-schedule-component.js +0 -1540
- package/dist/src/registry.js +0 -296
- package/dist/src/reskin/agency-page.js +0 -299
- package/dist/src/reskin/calendar-page.js +0 -568
- package/dist/src/reskin/chat-page.js +0 -942
- package/dist/src/reskin/discover-page.js +0 -1788
- package/dist/src/reskin/document.js +0 -1587
- package/dist/src/reskin/help-page.js +0 -357
- package/dist/src/reskin/index-page.js +0 -62
- package/dist/src/reskin/inpaint-clipper-page.js +0 -890
- package/dist/src/reskin/inpaint-page.js +0 -2554
- package/dist/src/reskin/inpaint-video-page.js +0 -1339
- package/dist/src/reskin/job-runs-page.js +0 -477
- package/dist/src/reskin/library-page.js +0 -1634
- package/dist/src/reskin/login-page.js +0 -262
- package/dist/src/reskin/portfolio-page.js +0 -687
- package/dist/src/reskin/pricing-page.js +0 -390
- package/dist/src/reskin/settings-page.js +0 -732
- package/dist/src/reskin/theme.js +0 -711
- package/dist/src/runtime.js +0 -35
- package/dist/src/services/api-call-history.js +0 -249
- package/dist/src/services/auth.js +0 -152
- package/dist/src/services/billing-pricing.js +0 -39
- package/dist/src/services/billing.js +0 -241
- package/dist/src/services/cast.js +0 -127
- package/dist/src/services/chat-threads.js +0 -92
- package/dist/src/services/clip-records.js +0 -250
- package/dist/src/services/clip-search.js +0 -77
- package/dist/src/services/clip-vectors.js +0 -125
- package/dist/src/services/composition-sanitize.js +0 -124
- package/dist/src/services/composition-watch.js +0 -79
- package/dist/src/services/elevenlabs.js +0 -222
- package/dist/src/services/file-directory.js +0 -117
- package/dist/src/services/fork-access.js +0 -93
- package/dist/src/services/fork-manifest.js +0 -42
- package/dist/src/services/ghostcut.js +0 -179
- package/dist/src/services/hyperframes.js +0 -3517
- package/dist/src/services/job-capacity.js +0 -14
- package/dist/src/services/job-logs.js +0 -197
- package/dist/src/services/jobs.js +0 -136
- package/dist/src/services/local-dynamo.js +0 -0
- package/dist/src/services/media-processing.js +0 -766
- package/dist/src/services/primitive-media-lambda.js +0 -280
- package/dist/src/services/providers.js +0 -2748
- package/dist/src/services/rate-limits.js +0 -262
- package/dist/src/services/scene-annotations.js +0 -32
- package/dist/src/services/serverless-auth.js +0 -382
- package/dist/src/services/serverless-jobs.js +0 -1084
- package/dist/src/services/serverless-provider-keys.js +0 -409
- package/dist/src/services/serverless-records.js +0 -1515
- package/dist/src/services/serverless-template-configs.js +0 -75
- package/dist/src/services/storage.js +0 -461
- package/dist/src/services/swipe-customize.js +0 -437
- package/dist/src/services/template-certification.js +0 -413
- package/dist/src/services/template-loader.js +0 -99
- package/dist/src/services/template-runtime-bundles.js +0 -217
- package/dist/src/services/template-sources.js +0 -1017
- package/dist/src/services/upstream.js +0 -248
- package/dist/src/services/video-normalization.js +0 -2
- package/dist/src/services/webhooks.js +0 -62
- package/dist/src/template-editor-pages.js +0 -2576
- package/dist/src/template-editor-shell.js +0 -2893
- package/dist/src/template-sdk.js +0 -4
- package/dist/src/worker.js +0 -17
- package/public/assets/discover-client-app.js +0 -1
- package/public/assets/file-directory-app.js +0 -3
- 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/public/assets/placeholders/scene-placeholder.png +0 -0
- package/src/assets/SELLING_AWARENESS_STAGES.md +0 -579
- package/src/assets/SELLING_WITH_HOOKS.md +0 -377
- package/src/assets/SELLING_WITH_VSLS.md +0 -606
- package/src/assets/favicon.ico +0 -0
- package/src/assets/logo-vidfarm.png +0 -0
package/dist/src/config.js
DELETED
|
@@ -1,217 +0,0 @@
|
|
|
1
|
-
import { mkdirSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import dotenv from "dotenv";
|
|
4
|
-
import { z } from "zod";
|
|
5
|
-
dotenv.config();
|
|
6
|
-
function normalizeEnvValue(value) {
|
|
7
|
-
if (typeof value !== "string") {
|
|
8
|
-
return value;
|
|
9
|
-
}
|
|
10
|
-
const trimmed = value.trim();
|
|
11
|
-
const unquoted = ((trimmed.startsWith("\"") && trimmed.endsWith("\""))
|
|
12
|
-
|| (trimmed.startsWith("'") && trimmed.endsWith("'")))
|
|
13
|
-
? trimmed.slice(1, -1).trim()
|
|
14
|
-
: trimmed;
|
|
15
|
-
return unquoted === "" ? undefined : unquoted;
|
|
16
|
-
}
|
|
17
|
-
const optionalSampleRate = z.preprocess((value) => value === "" || value == null ? undefined : value, z.coerce.number().min(0).max(1).optional());
|
|
18
|
-
const schema = z.object({
|
|
19
|
-
NODE_ENV: z.enum(["development", "test", "production"]).default("development"),
|
|
20
|
-
VIDFARM_DEPLOYMENT_TARGET: z.string().optional(),
|
|
21
|
-
VIDFARM_RUNTIME_BACKEND: z.literal("serverless").default("serverless"),
|
|
22
|
-
VIDFARM_DEBUG_LOGS: z.string().default("false"),
|
|
23
|
-
VIDFARM_LOAD_LOCAL_TEMPLATES: z.enum(["auto", "true", "false"]).default("auto"),
|
|
24
|
-
PORT: z.coerce.number().default(3000),
|
|
25
|
-
VIDFARM_DATA_DIR: z.string().default("./data"),
|
|
26
|
-
ENCRYPTION_SECRET: z.string().default("development-encryption-secret-change-me"),
|
|
27
|
-
API_KEY_SALT: z.string().default("development-api-key-salt"),
|
|
28
|
-
WEBHOOK_SECRET: z.string().default("development-webhook-secret"),
|
|
29
|
-
DEFAULT_JOB_DELAY_SECONDS: z.coerce.number().default(20),
|
|
30
|
-
MAX_JOB_WALL_CLOCK_SECONDS: z.coerce.number().default(6 * 60 * 60),
|
|
31
|
-
STALE_RUNNING_JOB_SECONDS: z.coerce.number().default(60 * 60),
|
|
32
|
-
PROVIDER_LEASE_WAIT_TIMEOUT_SECONDS: z.coerce.number().default(5 * 60),
|
|
33
|
-
PROVIDER_LEASE_INLINE_WAIT_SECONDS: z.coerce.number().default(5),
|
|
34
|
-
PROVIDER_LEASE_STEP_WAIT_SECONDS: z.coerce.number().int().min(1).default(60),
|
|
35
|
-
RENDER_POLL_WAIT_SECONDS: z.coerce.number().int().min(1).default(20),
|
|
36
|
-
PROVIDER_LEASE_MAX_STEP_WAIT_SECONDS: z.coerce.number().int().min(1).default(12 * 60 * 60),
|
|
37
|
-
MAX_ACTIVE_JOBS_PER_CUSTOMER: z.coerce.number().int().min(1).default(30),
|
|
38
|
-
STORAGE_DRIVER: z.enum(["local", "s3"]).default("local"),
|
|
39
|
-
// Records/auth backend. Defaults to `dynamo` so deployed environments are
|
|
40
|
-
// unaffected; `vidfarm serve` sets this to `local` to run fully on disk.
|
|
41
|
-
RECORDS_DRIVER: z.enum(["local", "dynamo"]).default("dynamo"),
|
|
42
|
-
AWS_REGION: z.string().default("us-east-1"),
|
|
43
|
-
AWS_S3_BUCKET: z.string().default(""),
|
|
44
|
-
AWS_S3_ENDPOINT: z.string().optional(),
|
|
45
|
-
AWS_ACCESS_KEY_ID: z.string().optional(),
|
|
46
|
-
AWS_SECRET_ACCESS_KEY: z.string().optional(),
|
|
47
|
-
PUBLIC_BASE_URL: z.string().optional(),
|
|
48
|
-
PUBLIC_EDITOR_CHAT_URL: z.string().url().optional(),
|
|
49
|
-
VIDFARM_USER_ID: z.string().optional(),
|
|
50
|
-
VIDFARM_API_KEY: z.string().optional(),
|
|
51
|
-
VIDFARM_STAGING_BOOTSTRAP_USER_ID: z.string().optional(),
|
|
52
|
-
VIDFARM_STAGING_BOOTSTRAP_EMAIL: z.string().optional(),
|
|
53
|
-
VIDFARM_STAGING_BOOTSTRAP_API_KEY: z.string().optional(),
|
|
54
|
-
EDITOR_CHAT_URL: z.string().url().optional(),
|
|
55
|
-
RESEND_API_KEY: z.string().optional(),
|
|
56
|
-
RESEND_FROM_EMAIL: z.string().default("vidfarm@fwd.zoomgtm.com"),
|
|
57
|
-
FLOCKPOSTER_API_URL: z.string().url().optional(),
|
|
58
|
-
VIDFARM_ZIP_LAMBDA_URL: z.string().url().optional(),
|
|
59
|
-
VIDFARM_ZIP_LAMBDA_SECRET: z.string().optional(),
|
|
60
|
-
VIDFARM_PRIMITIVE_MEDIA_LAMBDA_URL: z.string().url().optional(),
|
|
61
|
-
VIDFARM_PRIMITIVE_MEDIA_LAMBDA_SECRET: z.string().optional(),
|
|
62
|
-
VIDFARM_BILLING_URL: z.string().url().optional(),
|
|
63
|
-
VIDFARM_BILLING_SECRET: z.string().optional(),
|
|
64
|
-
VIDFARM_MIN_RENDER_WALLET_BALANCE_USD: z.coerce.number().default(1),
|
|
65
|
-
OPENAI_API_KEY: z.string().optional(),
|
|
66
|
-
OPENROUTER_API_KEY: z.string().optional(),
|
|
67
|
-
GEMINI_API_KEY: z.string().optional(),
|
|
68
|
-
PERPLEXITY_API_KEY: z.string().optional(),
|
|
69
|
-
// Platform ElevenLabs key — the fallback used for music / TTS / STT primitives
|
|
70
|
-
// when the caller opts into `use_wallet_credits` (the default) or has no usable
|
|
71
|
-
// BYOK audio key. Usage on this key is billed to the customer's vidfarm wallet.
|
|
72
|
-
ELEVENLABS_API_KEY: z.string().optional(),
|
|
73
|
-
// Base provider costs for ElevenLabs primitives billed to the wallet (the
|
|
74
|
-
// billing lambda applies the standard markup on top). Defaults track published
|
|
75
|
-
// ElevenLabs list pricing: TTS multilingual v2/v3 $0.10 / 1K chars (flash/turbo
|
|
76
|
-
// is $0.05), Music $0.15 / minute, Scribe STT $0.22 / hour ≈ $0.003667 / min.
|
|
77
|
-
// Override per-environment as pricing shifts.
|
|
78
|
-
ELEVENLABS_MUSIC_USD_PER_MIN: z.coerce.number().min(0).default(0.15),
|
|
79
|
-
ELEVENLABS_TTS_USD_PER_1K_CHARS: z.coerce.number().min(0).default(0.10),
|
|
80
|
-
ELEVENLABS_STT_USD_PER_MIN: z.coerce.number().min(0).default(0.003667),
|
|
81
|
-
GHOSTCUT_KEY: z.string().optional(),
|
|
82
|
-
GHOSTCUT_SECRET: z.string().optional(),
|
|
83
|
-
GHOSTCUT_USD_PER_30S: z.coerce.number().min(0).default(0.10),
|
|
84
|
-
// GhostCut caption removal is for SHORT clips, never long-form sources —
|
|
85
|
-
// clip hunting must select text-free scenes instead of laundering a whole
|
|
86
|
-
// long video through GhostCut. 15 min default ceiling (per-30s pricing makes
|
|
87
|
-
// long-form runs absurd anyway).
|
|
88
|
-
GHOSTCUT_MAX_DURATION_SEC: z.coerce.number().min(0).default(900),
|
|
89
|
-
// Auto-decompose kicks off GhostCut subtitle removal on the source video, but
|
|
90
|
-
// GhostCut prices per-30s chunk and is meant for short clips. Skip it entirely
|
|
91
|
-
// when the decompose source is longer than this — the composition just keeps
|
|
92
|
-
// the original burned-in captions rather than paying to launder a multi-minute
|
|
93
|
-
// video. 0 = never skip on length. Distinct from GHOSTCUT_MAX_DURATION_SEC
|
|
94
|
-
// (the standalone remove-captions primitive's hard ceiling).
|
|
95
|
-
GHOSTCUT_DECOMPOSE_MAX_DURATION_SEC: z.coerce.number().min(0).default(180),
|
|
96
|
-
// Inspiration ingest + auto-decompose used to hard-cap sources at 120s (a
|
|
97
|
-
// short-form-only assumption). Longer inspiration is now allowed. 0 = unlimited;
|
|
98
|
-
// set a positive ceiling to re-impose a limit. Kept separate from
|
|
99
|
-
// MAX_PROBE_DURATION_SECONDS (the per-primitive probe cost-guard) so raising the
|
|
100
|
-
// inspiration ceiling never loosens the generic probe default.
|
|
101
|
-
INSPIRATION_MAX_DURATION_SEC: z.coerce.number().min(0).default(0),
|
|
102
|
-
// Cloud passthrough for `vidfarm serve`: the local box keeps editing/render
|
|
103
|
-
// fully local but lists the upstream (prod) catalog in /discover and
|
|
104
|
-
// /library, seeds forks on demand, and can hand a render off to the cloud.
|
|
105
|
-
// Set by the serve command from --host + the ambient cloud VIDFARM_API_KEY;
|
|
106
|
-
// never set on deployed environments.
|
|
107
|
-
VIDFARM_UPSTREAM_HOST: z.string().optional(),
|
|
108
|
-
VIDFARM_UPSTREAM_API_KEY: z.string().optional(),
|
|
109
|
-
RAPIDAPI_KEY: z.string().optional(),
|
|
110
|
-
RAPIDAPI_VIDEO_DOWNLOAD_URL: z.string().url().default("https://snap-video3.p.rapidapi.com/download"),
|
|
111
|
-
RAPIDAPI_VIDEO_DOWNLOAD_HOST: z.string().default("snap-video3.p.rapidapi.com"),
|
|
112
|
-
RAPIDAPI_VIDEO_DOWNLOAD_BASE_COST_USD: z.coerce.number().min(0).default(0.005),
|
|
113
|
-
RAPIDAPI_VIDEO_DOWNLOAD_LAMBDA_COST_USD: z.coerce.number().min(0).default(0.0002),
|
|
114
|
-
RAPIDAPI_REMOVE_BACKGROUND_URL: z.string().url().default("https://remove-background18.p.rapidapi.com/public/remove-background/url"),
|
|
115
|
-
RAPIDAPI_REMOVE_BACKGROUND_HOST: z.string().default("remove-background18.p.rapidapi.com"),
|
|
116
|
-
RAPIDAPI_REMOVE_BACKGROUND_COST_USD: z.coerce.number().min(0).default(0.005),
|
|
117
|
-
// Local sharp chroma-key background removal (no third-party API) — a small
|
|
118
|
-
// compute/convenience wallet fee, far cheaper than the RapidAPI matting above.
|
|
119
|
-
GREENSCREEN_CHROMA_KEY_COST_USD: z.coerce.number().min(0).default(0.002),
|
|
120
|
-
// Two-step "create media overlay": BYOK AI image gen (provider cost rides the
|
|
121
|
-
// caller's own key) + local chroma key. This fee bills only the platform
|
|
122
|
-
// convenience/compute leg of the combined primitive.
|
|
123
|
-
MEDIA_OVERLAY_COST_USD: z.coerce.number().min(0).default(0.008),
|
|
124
|
-
SUPERAGENCY_KEY: z.string().optional(),
|
|
125
|
-
ARTIFACT_SIGNING_SECRET: z.string().optional(),
|
|
126
|
-
HYPERFRAMES_LAMBDA_STACK_NAME: z.string().default("hyperframes-vidfarm-prod"),
|
|
127
|
-
HYPERFRAMES_RENDER_REGION: z.string().optional(),
|
|
128
|
-
HYPERFRAMES_RENDER_BUCKET_NAME: z.string().optional(),
|
|
129
|
-
HYPERFRAMES_LAMBDA_BUCKET_NAME: z.string().optional(),
|
|
130
|
-
HYPERFRAMES_RENDER_STATE_MACHINE_ARN: z.string().optional(),
|
|
131
|
-
HYPERFRAMES_STATE_MACHINE_ARN: z.string().optional(),
|
|
132
|
-
HYPERFRAMES_RENDER_MEMORY_MB: z.coerce.number().int().min(512).default(3072),
|
|
133
|
-
// Force the in-process (single-machine, headless-Chromium) HyperFrames
|
|
134
|
-
// renderer even when a cloud render stack is configured. When unset, the
|
|
135
|
-
// renderer auto-selects local whenever no cloud render stack is wired (the
|
|
136
|
-
// `vidfarm serve` case). Accepts "1"/"true"/"yes".
|
|
137
|
-
VIDFARM_LOCAL_RENDER: z
|
|
138
|
-
.string()
|
|
139
|
-
.optional()
|
|
140
|
-
.transform((value) => /^(1|true|yes)$/i.test(String(value ?? ""))),
|
|
141
|
-
MOCK_PROVIDER_RESPONSES: z.string().optional(),
|
|
142
|
-
VIDFARM_ADMIN_EMAILS: z.string().default(""),
|
|
143
|
-
VIDFARM_GITHUB_TOKEN: z.string().optional(),
|
|
144
|
-
GITHUB_TOKEN: z.string().optional(),
|
|
145
|
-
TEMPLATE_SOURCE_ROOT: z.string().default("./data/template-sources"),
|
|
146
|
-
VIDFARM_RATE_LIMITS_TABLE_NAME: z.string().optional(),
|
|
147
|
-
VIDFARM_SERVERLESS_MAIN_TABLE_NAME: z.string().optional(),
|
|
148
|
-
VIDFARM_JOB_STATE_MACHINE_ARN: z.string().optional(),
|
|
149
|
-
// Clip-curation scan pipeline (Step Functions). Optional: when unset, the
|
|
150
|
-
// /clips/scan route records the scan request but doesn't dispatch the machine.
|
|
151
|
-
VIDFARM_CLIP_SCAN_STATE_MACHINE_ARN: z.string().optional(),
|
|
152
|
-
VIDFARM_CLIP_VECTOR_BACKEND: z.enum(["dynamo", "s3vectors"]).default("dynamo"),
|
|
153
|
-
VIDFARM_CLIP_VECTORS_BUCKET: z.string().optional(),
|
|
154
|
-
VIDFARM_CLIP_VECTORS_INDEX: z.string().optional(),
|
|
155
|
-
SENTRY_DSN: z.string().optional(),
|
|
156
|
-
SENTRY_ENVIRONMENT: z.string().optional(),
|
|
157
|
-
SENTRY_RELEASE: z.string().optional(),
|
|
158
|
-
SENTRY_TRACES_SAMPLE_RATE: optionalSampleRate,
|
|
159
|
-
SENTRY_ENABLE_LOGS: z.string().default("true"),
|
|
160
|
-
SENTRY_SEND_DEFAULT_PII: z.string().default("false"),
|
|
161
|
-
SENTRY_DEBUG_ROUTE: z.string().default("false"),
|
|
162
|
-
PUBLIC_SENTRY_DSN: z.string().optional(),
|
|
163
|
-
PUBLIC_SENTRY_TRACES_SAMPLE_RATE: optionalSampleRate,
|
|
164
|
-
PUBLIC_SENTRY_REPLAYS_SESSION_SAMPLE_RATE: optionalSampleRate,
|
|
165
|
-
PUBLIC_SENTRY_REPLAYS_ON_ERROR_SAMPLE_RATE: optionalSampleRate,
|
|
166
|
-
PUBLIC_SENTRY_ENABLE_LOGS: z.string().default("true"),
|
|
167
|
-
PUBLIC_SENTRY_SEND_DEFAULT_PII: z.string().default("true"),
|
|
168
|
-
PUBLIC_SENTRY_TRACE_PROPAGATION_TARGETS: z.string().default("")
|
|
169
|
-
}).superRefine((data, ctx) => {
|
|
170
|
-
if (data.NODE_ENV === "production" && data.VIDFARM_RUNTIME_BACKEND === "serverless") {
|
|
171
|
-
if (!data.VIDFARM_SERVERLESS_MAIN_TABLE_NAME) {
|
|
172
|
-
ctx.addIssue({
|
|
173
|
-
code: z.ZodIssueCode.custom,
|
|
174
|
-
path: ["VIDFARM_SERVERLESS_MAIN_TABLE_NAME"],
|
|
175
|
-
message: "VIDFARM_SERVERLESS_MAIN_TABLE_NAME is required when VIDFARM_RUNTIME_BACKEND=serverless in production"
|
|
176
|
-
});
|
|
177
|
-
}
|
|
178
|
-
if (!data.VIDFARM_RATE_LIMITS_TABLE_NAME) {
|
|
179
|
-
ctx.addIssue({
|
|
180
|
-
code: z.ZodIssueCode.custom,
|
|
181
|
-
path: ["VIDFARM_RATE_LIMITS_TABLE_NAME"],
|
|
182
|
-
message: "VIDFARM_RATE_LIMITS_TABLE_NAME is required when VIDFARM_RUNTIME_BACKEND=serverless in production"
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
if (!data.VIDFARM_JOB_STATE_MACHINE_ARN) {
|
|
186
|
-
ctx.addIssue({
|
|
187
|
-
code: z.ZodIssueCode.custom,
|
|
188
|
-
path: ["VIDFARM_JOB_STATE_MACHINE_ARN"],
|
|
189
|
-
message: "VIDFARM_JOB_STATE_MACHINE_ARN is required when VIDFARM_RUNTIME_BACKEND=serverless in production"
|
|
190
|
-
});
|
|
191
|
-
}
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
const normalizedEnv = Object.fromEntries(Object.entries(process.env).map(([key, value]) => [key, normalizeEnvValue(value)]));
|
|
195
|
-
const parsed = schema.parse(normalizedEnv);
|
|
196
|
-
const dataDir = path.resolve(parsed.VIDFARM_DATA_DIR);
|
|
197
|
-
mkdirSync(dataDir, { recursive: true });
|
|
198
|
-
const templateSourceRoot = path.resolve(parsed.TEMPLATE_SOURCE_ROOT);
|
|
199
|
-
mkdirSync(templateSourceRoot, { recursive: true });
|
|
200
|
-
const publicBaseUrl = parsed.PUBLIC_BASE_URL || `http://localhost:${parsed.PORT}`;
|
|
201
|
-
export const config = {
|
|
202
|
-
...parsed,
|
|
203
|
-
VIDFARM_DATA_DIR: dataDir,
|
|
204
|
-
TEMPLATE_SOURCE_ROOT: templateSourceRoot,
|
|
205
|
-
PUBLIC_BASE_URL: publicBaseUrl,
|
|
206
|
-
ARTIFACT_SIGNING_SECRET: parsed.ARTIFACT_SIGNING_SECRET || parsed.SUPERAGENCY_KEY || "",
|
|
207
|
-
isProduction: parsed.NODE_ENV === "production",
|
|
208
|
-
mockProviders: parsed.MOCK_PROVIDER_RESPONSES === "true" ||
|
|
209
|
-
(parsed.MOCK_PROVIDER_RESPONSES == null && parsed.NODE_ENV !== "production"),
|
|
210
|
-
minRenderWalletBalanceUsd: parsed.VIDFARM_MIN_RENDER_WALLET_BALANCE_USD,
|
|
211
|
-
debugLogs: parsed.VIDFARM_DEBUG_LOGS === "true" || parsed.VIDFARM_DEPLOYMENT_TARGET === "serverless-staging",
|
|
212
|
-
adminEmails: parsed.VIDFARM_ADMIN_EMAILS
|
|
213
|
-
.split(",")
|
|
214
|
-
.map((value) => value.trim().toLowerCase())
|
|
215
|
-
.filter(Boolean),
|
|
216
|
-
};
|
|
217
|
-
//# sourceMappingURL=config.js.map
|
package/dist/src/context.js
DELETED
|
@@ -1,447 +0,0 @@
|
|
|
1
|
-
import { existsSync, statSync } from "node:fs";
|
|
2
|
-
import path from "node:path";
|
|
3
|
-
import * as Sentry from "@sentry/node";
|
|
4
|
-
import { config } from "./config.js";
|
|
5
|
-
import { devErrorFields, devLog } from "./lib/dev-log.js";
|
|
6
|
-
import { createId } from "./lib/ids.js";
|
|
7
|
-
import { estimateNormalizationCostUsd, normalizeVideoAsset } from "./services/video-normalization.js";
|
|
8
|
-
export function createTemplateJobContext(input) {
|
|
9
|
-
const templateConfig = {};
|
|
10
|
-
const prefix = input.storage.templateJobPrefix(input.template.id, input.customer.id, input.job.id);
|
|
11
|
-
const buildArtifactUrl = (storageKey, fallbackUrl) => fallbackUrl ?? input.storage.getPublicUrl(storageKey) ?? buildTemplateMediaUrl(storageKey);
|
|
12
|
-
const templatePreviewMediaUrls = resolveTemplatePreviewMediaUrls(input.template, input.storage);
|
|
13
|
-
const jobPreviewMediaPolicy = parseTemplatePreviewMediaPolicy(input.job.payload._templatePreviewMediaPolicy);
|
|
14
|
-
// Billing records are written after the billable work (render, normalize,
|
|
15
|
-
// etc.) has already succeeded. A transient billing failure must never fail
|
|
16
|
-
// the job — the customer would retry and pay for the expensive work twice.
|
|
17
|
-
const recordBillingNonFatal = async (record) => {
|
|
18
|
-
try {
|
|
19
|
-
await input.billing.record(record);
|
|
20
|
-
}
|
|
21
|
-
catch (error) {
|
|
22
|
-
Sentry.captureException(error);
|
|
23
|
-
devLog("billing.record_failed_nonfatal", {
|
|
24
|
-
job_id: input.job.id,
|
|
25
|
-
customer_id: input.customer.id,
|
|
26
|
-
template_id: input.template.id,
|
|
27
|
-
cost_center_slug: record.costCenterSlug ?? null,
|
|
28
|
-
idempotency_key: record.idempotencyKey ?? null,
|
|
29
|
-
...devErrorFields(error)
|
|
30
|
-
}, "warn");
|
|
31
|
-
}
|
|
32
|
-
};
|
|
33
|
-
return {
|
|
34
|
-
env: config.isProduction ? "production" : "development",
|
|
35
|
-
customer: input.customer,
|
|
36
|
-
template: {
|
|
37
|
-
id: input.template.id,
|
|
38
|
-
slugId: input.template.slugId,
|
|
39
|
-
previewMediaUrls: templatePreviewMediaUrls,
|
|
40
|
-
previewMediaPolicy: jobPreviewMediaPolicy
|
|
41
|
-
},
|
|
42
|
-
templateConfig,
|
|
43
|
-
logger: {
|
|
44
|
-
debug(message, metadata = {}) {
|
|
45
|
-
void input.jobs.addLogEvent({
|
|
46
|
-
id: createId("evt"),
|
|
47
|
-
jobId: input.job.id,
|
|
48
|
-
level: "debug",
|
|
49
|
-
message,
|
|
50
|
-
metadata,
|
|
51
|
-
progress: null,
|
|
52
|
-
artifactKey: null,
|
|
53
|
-
createdAt: new Date().toISOString()
|
|
54
|
-
});
|
|
55
|
-
},
|
|
56
|
-
info(message, metadata = {}) {
|
|
57
|
-
void input.jobs.addLogEvent({
|
|
58
|
-
id: createId("evt"),
|
|
59
|
-
jobId: input.job.id,
|
|
60
|
-
level: "info",
|
|
61
|
-
message,
|
|
62
|
-
metadata,
|
|
63
|
-
progress: null,
|
|
64
|
-
artifactKey: null,
|
|
65
|
-
createdAt: new Date().toISOString()
|
|
66
|
-
});
|
|
67
|
-
},
|
|
68
|
-
warn(message, metadata = {}) {
|
|
69
|
-
void input.jobs.addLogEvent({
|
|
70
|
-
id: createId("evt"),
|
|
71
|
-
jobId: input.job.id,
|
|
72
|
-
level: "warn",
|
|
73
|
-
message,
|
|
74
|
-
metadata,
|
|
75
|
-
progress: null,
|
|
76
|
-
artifactKey: null,
|
|
77
|
-
createdAt: new Date().toISOString()
|
|
78
|
-
});
|
|
79
|
-
},
|
|
80
|
-
error(message, metadata = {}) {
|
|
81
|
-
void input.jobs.addLogEvent({
|
|
82
|
-
id: createId("evt"),
|
|
83
|
-
jobId: input.job.id,
|
|
84
|
-
level: "error",
|
|
85
|
-
message,
|
|
86
|
-
metadata,
|
|
87
|
-
progress: null,
|
|
88
|
-
artifactKey: null,
|
|
89
|
-
createdAt: new Date().toISOString()
|
|
90
|
-
});
|
|
91
|
-
},
|
|
92
|
-
progress(progress, message, metadata = {}) {
|
|
93
|
-
void input.jobs.addLogEvent({
|
|
94
|
-
id: createId("evt"),
|
|
95
|
-
jobId: input.job.id,
|
|
96
|
-
level: "info",
|
|
97
|
-
message,
|
|
98
|
-
metadata,
|
|
99
|
-
progress,
|
|
100
|
-
artifactKey: typeof metadata.artifactKey === "string" ? metadata.artifactKey : null,
|
|
101
|
-
createdAt: new Date().toISOString()
|
|
102
|
-
});
|
|
103
|
-
void input.jobs.updateJobStatus({
|
|
104
|
-
id: input.job.id,
|
|
105
|
-
status: "running",
|
|
106
|
-
progress
|
|
107
|
-
});
|
|
108
|
-
}
|
|
109
|
-
},
|
|
110
|
-
jobs: {
|
|
111
|
-
async enqueueChild({ operationName, workflowName, payload, providerHint }) {
|
|
112
|
-
const child = await input.jobs.enqueueChildJob({
|
|
113
|
-
templateId: input.template.id,
|
|
114
|
-
operationName,
|
|
115
|
-
workflowName,
|
|
116
|
-
tracer: input.job.tracer,
|
|
117
|
-
payload,
|
|
118
|
-
customerId: input.customer.id,
|
|
119
|
-
parentJobId: input.job.id,
|
|
120
|
-
providerHint
|
|
121
|
-
});
|
|
122
|
-
return { jobId: child.id };
|
|
123
|
-
}
|
|
124
|
-
},
|
|
125
|
-
storage: {
|
|
126
|
-
async putJson(key, value) {
|
|
127
|
-
const stored = await input.storage.putJson(`${prefix}/${key}`, value);
|
|
128
|
-
const artifactUrl = buildArtifactUrl(stored.key, stored.url);
|
|
129
|
-
await insertArtifact("json", stored.key, artifactUrl, {});
|
|
130
|
-
return {
|
|
131
|
-
...stored,
|
|
132
|
-
url: artifactUrl
|
|
133
|
-
};
|
|
134
|
-
},
|
|
135
|
-
async putText(key, value, contentType = "text/plain; charset=utf-8") {
|
|
136
|
-
const stored = await input.storage.putText(`${prefix}/${key}`, value, contentType);
|
|
137
|
-
const artifactUrl = buildArtifactUrl(stored.key, stored.url);
|
|
138
|
-
await insertArtifact("text", stored.key, artifactUrl, { contentType });
|
|
139
|
-
return {
|
|
140
|
-
...stored,
|
|
141
|
-
url: artifactUrl
|
|
142
|
-
};
|
|
143
|
-
},
|
|
144
|
-
async putBuffer(key, value, options = {}) {
|
|
145
|
-
const stored = await input.storage.putBuffer(`${prefix}/${key}`, value, options.contentType);
|
|
146
|
-
const artifactUrl = buildArtifactUrl(stored.key, stored.url);
|
|
147
|
-
await insertArtifact(options.kind ?? "binary", stored.key, artifactUrl, {
|
|
148
|
-
...(options.metadata ?? {}),
|
|
149
|
-
contentType: options.contentType ?? null
|
|
150
|
-
});
|
|
151
|
-
return {
|
|
152
|
-
...stored,
|
|
153
|
-
url: artifactUrl
|
|
154
|
-
};
|
|
155
|
-
},
|
|
156
|
-
getPublicUrl(key) {
|
|
157
|
-
const storageKey = `${prefix}/${key}`;
|
|
158
|
-
return input.storage.getPublicUrl(storageKey) ?? buildTemplateMediaUrl(storageKey);
|
|
159
|
-
}
|
|
160
|
-
},
|
|
161
|
-
billing: {
|
|
162
|
-
async record(record) {
|
|
163
|
-
await recordBillingNonFatal({
|
|
164
|
-
customerId: input.customer.id,
|
|
165
|
-
jobId: input.job.id,
|
|
166
|
-
tracer: input.job.tracer,
|
|
167
|
-
templateId: input.template.id,
|
|
168
|
-
type: record.type,
|
|
169
|
-
costUsd: record.costUsd,
|
|
170
|
-
chargeUsd: record.chargeUsd ?? Number((record.costUsd * 1.2).toFixed(4)),
|
|
171
|
-
costCenterSlug: record.costCenterSlug,
|
|
172
|
-
idempotencyKey: record.idempotencyKey,
|
|
173
|
-
occurredAtMs: record.occurredAtMs,
|
|
174
|
-
metadata: record.metadata
|
|
175
|
-
});
|
|
176
|
-
}
|
|
177
|
-
},
|
|
178
|
-
providers: {
|
|
179
|
-
listAvailableProviders() {
|
|
180
|
-
return input.providers.listAvailableProviders(input.customer.id);
|
|
181
|
-
},
|
|
182
|
-
async generateText(request) {
|
|
183
|
-
return input.providers.generateText({
|
|
184
|
-
customerId: input.customer.id,
|
|
185
|
-
jobId: input.job.id,
|
|
186
|
-
workerId: input.workerId,
|
|
187
|
-
...request
|
|
188
|
-
});
|
|
189
|
-
},
|
|
190
|
-
async generateSpeech(request) {
|
|
191
|
-
return input.providers.generateSpeech({
|
|
192
|
-
customerId: input.customer.id,
|
|
193
|
-
jobId: input.job.id,
|
|
194
|
-
workerId: input.workerId,
|
|
195
|
-
...request
|
|
196
|
-
});
|
|
197
|
-
},
|
|
198
|
-
async generateImage(request) {
|
|
199
|
-
return input.providers.generateImage({
|
|
200
|
-
customerId: input.customer.id,
|
|
201
|
-
jobId: input.job.id,
|
|
202
|
-
workerId: input.workerId,
|
|
203
|
-
...request
|
|
204
|
-
});
|
|
205
|
-
},
|
|
206
|
-
async analyzeImageLayout(request) {
|
|
207
|
-
return input.providers.analyzeImageLayout({
|
|
208
|
-
customerId: input.customer.id,
|
|
209
|
-
jobId: input.job.id,
|
|
210
|
-
workerId: input.workerId,
|
|
211
|
-
...request
|
|
212
|
-
});
|
|
213
|
-
},
|
|
214
|
-
async transcribeSpeech(request) {
|
|
215
|
-
return input.providers.transcribeSpeech({
|
|
216
|
-
customerId: input.customer.id,
|
|
217
|
-
jobId: input.job.id,
|
|
218
|
-
workerId: input.workerId,
|
|
219
|
-
...request
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
},
|
|
223
|
-
hyperframes: {
|
|
224
|
-
async render(request) {
|
|
225
|
-
const hyperframesOutputKey = `renders/vidfarm/templates/${input.template.id}/${input.customer.id}/${input.job.id}/output.mp4`;
|
|
226
|
-
const render = await input.hyperframes.render({
|
|
227
|
-
...request,
|
|
228
|
-
outputKey: hyperframesOutputKey,
|
|
229
|
-
wait: true
|
|
230
|
-
});
|
|
231
|
-
let outputUrl = render.outputUrl;
|
|
232
|
-
let platformOutputKey = null;
|
|
233
|
-
if (render.outputS3Uri) {
|
|
234
|
-
const output = await input.hyperframes.readRenderOutput(render.outputS3Uri);
|
|
235
|
-
platformOutputKey = `${prefix}/${request.outputKey || "renders/output.mp4"}`;
|
|
236
|
-
const stored = await input.storage.putBuffer(platformOutputKey, output.bytes, output.contentType);
|
|
237
|
-
outputUrl = buildArtifactUrl(stored.key, stored.url);
|
|
238
|
-
await insertArtifact("video", stored.key, outputUrl, {
|
|
239
|
-
operation: "hyperframes-render",
|
|
240
|
-
contentType: output.contentType,
|
|
241
|
-
renderId: render.renderId,
|
|
242
|
-
outputS3Uri: render.outputS3Uri
|
|
243
|
-
});
|
|
244
|
-
}
|
|
245
|
-
const estimatedCostUsd = typeof render.metadata.estimatedCostUsd === "number" ? render.metadata.estimatedCostUsd : null;
|
|
246
|
-
const chargeUsd = typeof render.metadata.chargeUsd === "number" ? render.metadata.chargeUsd : null;
|
|
247
|
-
if (estimatedCostUsd !== null && estimatedCostUsd > 0) {
|
|
248
|
-
await recordBillingNonFatal({
|
|
249
|
-
customerId: input.customer.id,
|
|
250
|
-
jobId: input.job.id,
|
|
251
|
-
tracer: input.job.tracer,
|
|
252
|
-
templateId: input.template.id,
|
|
253
|
-
type: "render",
|
|
254
|
-
costUsd: estimatedCostUsd,
|
|
255
|
-
chargeUsd: typeof chargeUsd === "number" ? chargeUsd : undefined,
|
|
256
|
-
costCenterSlug: "hyperframes_lambda",
|
|
257
|
-
idempotencyKey: `hyperframes_lambda:${render.renderId}`,
|
|
258
|
-
occurredAtMs: Date.now(),
|
|
259
|
-
metadata: {
|
|
260
|
-
...render.metadata,
|
|
261
|
-
render_id: render.renderId,
|
|
262
|
-
output_url: outputUrl,
|
|
263
|
-
signed_output_url: render.outputUrl,
|
|
264
|
-
output_s3_uri: render.outputS3Uri,
|
|
265
|
-
platform_output_key: platformOutputKey
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
}
|
|
269
|
-
return {
|
|
270
|
-
renderId: render.renderId,
|
|
271
|
-
outputUrl,
|
|
272
|
-
outputS3Uri: render.outputS3Uri,
|
|
273
|
-
metadata: {
|
|
274
|
-
...render.metadata,
|
|
275
|
-
outputUrl,
|
|
276
|
-
signedOutputUrl: render.outputUrl,
|
|
277
|
-
platformOutputKey
|
|
278
|
-
}
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
},
|
|
282
|
-
media: {
|
|
283
|
-
async normalizeVideo(request) {
|
|
284
|
-
const normalized = await normalizeVideoAsset({
|
|
285
|
-
sourceUrl: request.sourceUrl,
|
|
286
|
-
maxWidth: request.maxWidth,
|
|
287
|
-
maxHeight: request.maxHeight,
|
|
288
|
-
crf: request.crf,
|
|
289
|
-
preset: request.preset,
|
|
290
|
-
maxVideoBitrateKbps: request.maxVideoBitrateKbps,
|
|
291
|
-
audioBitrateKbps: request.audioBitrateKbps
|
|
292
|
-
});
|
|
293
|
-
const stored = await input.storage.putBuffer(`${prefix}/${request.outputKey}`, normalized.bytes, normalized.contentType);
|
|
294
|
-
const artifactUrl = buildArtifactUrl(stored.key, stored.url);
|
|
295
|
-
await insertArtifact("video", stored.key, artifactUrl, {
|
|
296
|
-
...(request.metadata ?? {}),
|
|
297
|
-
operation: "normalize-video",
|
|
298
|
-
contentType: normalized.contentType,
|
|
299
|
-
normalization: normalized.metadata
|
|
300
|
-
});
|
|
301
|
-
await recordBillingNonFatal({
|
|
302
|
-
customerId: input.customer.id,
|
|
303
|
-
jobId: input.job.id,
|
|
304
|
-
tracer: input.job.tracer,
|
|
305
|
-
templateId: input.template.id,
|
|
306
|
-
type: "cpu_estimate",
|
|
307
|
-
costUsd: estimateNormalizationCostUsd(normalized.metadata),
|
|
308
|
-
costCenterSlug: "video_normalization",
|
|
309
|
-
idempotencyKey: `video_normalization:${input.job.id}:${request.outputKey}`,
|
|
310
|
-
occurredAtMs: Date.now(),
|
|
311
|
-
metadata: {
|
|
312
|
-
...(request.metadata ?? {}),
|
|
313
|
-
operation: "normalize-video",
|
|
314
|
-
source_url: request.sourceUrl,
|
|
315
|
-
output_key: `${prefix}/${request.outputKey}`,
|
|
316
|
-
normalization: normalized.metadata
|
|
317
|
-
}
|
|
318
|
-
});
|
|
319
|
-
return {
|
|
320
|
-
...stored,
|
|
321
|
-
url: artifactUrl,
|
|
322
|
-
contentType: normalized.contentType,
|
|
323
|
-
normalization: normalized.metadata
|
|
324
|
-
};
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
};
|
|
328
|
-
async function insertArtifact(kind, storageKey, publicUrl, metadata) {
|
|
329
|
-
await input.jobs.insertArtifact({
|
|
330
|
-
id: createId("art"),
|
|
331
|
-
jobId: input.job.id,
|
|
332
|
-
customerId: input.customer.id,
|
|
333
|
-
templateId: input.template.id,
|
|
334
|
-
kind,
|
|
335
|
-
storageKey,
|
|
336
|
-
publicUrl,
|
|
337
|
-
metadata
|
|
338
|
-
});
|
|
339
|
-
}
|
|
340
|
-
}
|
|
341
|
-
function parseTemplatePreviewMediaPolicy(value) {
|
|
342
|
-
if (value === "include" || value === "exclude" || value === "auto") {
|
|
343
|
-
return value;
|
|
344
|
-
}
|
|
345
|
-
if (value === true) {
|
|
346
|
-
return "include";
|
|
347
|
-
}
|
|
348
|
-
if (value === false) {
|
|
349
|
-
return "exclude";
|
|
350
|
-
}
|
|
351
|
-
return "auto";
|
|
352
|
-
}
|
|
353
|
-
function resolveTemplatePreviewMediaUrls(template, storage) {
|
|
354
|
-
return uniqueStrings(template.about.preview_media
|
|
355
|
-
.filter(isImagePreviewReference)
|
|
356
|
-
.map((entry) => resolveTemplatePreviewMediaUrl(template, storage, entry))
|
|
357
|
-
.filter((entry) => Boolean(entry)));
|
|
358
|
-
}
|
|
359
|
-
function resolveTemplatePreviewMediaUrl(template, storage, entry) {
|
|
360
|
-
if (/^https?:\/\//i.test(entry)) {
|
|
361
|
-
return entry;
|
|
362
|
-
}
|
|
363
|
-
const normalizedEntry = entry.replace(/^\/+/, "");
|
|
364
|
-
if (normalizedEntry.startsWith("templates/")) {
|
|
365
|
-
return resolveBundledTemplateAssetFromStorageKey(template, normalizedEntry)
|
|
366
|
-
? buildTemplateMediaUrl(normalizedEntry)
|
|
367
|
-
: storage.getPublicUrl(normalizedEntry) ?? buildTemplateMediaUrl(normalizedEntry);
|
|
368
|
-
}
|
|
369
|
-
const templateAboutPrefix = storage.templateAboutKey(template.id, "");
|
|
370
|
-
const storageAssetPath = normalizedEntry.startsWith(templateAboutPrefix)
|
|
371
|
-
? normalizedEntry.slice(templateAboutPrefix.length)
|
|
372
|
-
: normalizedEntry.startsWith("about/")
|
|
373
|
-
? normalizedEntry.slice("about/".length)
|
|
374
|
-
: normalizedEntry;
|
|
375
|
-
const storageKey = storage.templateAboutKey(template.id, storageAssetPath);
|
|
376
|
-
return resolveBundledTemplateAsset(template, storageAssetPath)
|
|
377
|
-
? buildTemplateMediaUrl(storageKey)
|
|
378
|
-
: storage.getPublicUrl(storageKey) ?? buildTemplateMediaUrl(storageKey);
|
|
379
|
-
}
|
|
380
|
-
function isImagePreviewReference(entry) {
|
|
381
|
-
return /\.(png|jpe?g|webp)(?:$|[?#])/i.test(entry);
|
|
382
|
-
}
|
|
383
|
-
function uniqueStrings(values) {
|
|
384
|
-
return Array.from(new Set(values.filter((value) => value.trim().length > 0)));
|
|
385
|
-
}
|
|
386
|
-
function buildTemplateMediaUrl(storageKey) {
|
|
387
|
-
return `${config.PUBLIC_BASE_URL.replace(/\/$/, "")}/template-media?key=${encodeURIComponent(storageKey)}`;
|
|
388
|
-
}
|
|
389
|
-
function resolveBundledTemplateAssetFromStorageKey(template, storageKey) {
|
|
390
|
-
const match = /^templates\/([^/]+)\/about\/(.+)$/.exec(storageKey.replace(/^\/+/, ""));
|
|
391
|
-
if (!match || match[1] !== template.id) {
|
|
392
|
-
return null;
|
|
393
|
-
}
|
|
394
|
-
return resolveBundledTemplateAsset(template, match[2]);
|
|
395
|
-
}
|
|
396
|
-
function resolveBundledTemplateAsset(template, assetPath) {
|
|
397
|
-
if (!template.skillPath || !assetPath) {
|
|
398
|
-
return null;
|
|
399
|
-
}
|
|
400
|
-
const normalizedAssetPath = assetPath.replace(/^\/+/, "");
|
|
401
|
-
if (!normalizedAssetPath || normalizedAssetPath.includes("\\") || normalizedAssetPath.split("/").some((segment) => !segment || segment === "." || segment === "..")) {
|
|
402
|
-
return null;
|
|
403
|
-
}
|
|
404
|
-
let templateRoot = path.resolve(template.skillPath);
|
|
405
|
-
try {
|
|
406
|
-
if (existsSync(templateRoot) && !statSync(templateRoot).isDirectory()) {
|
|
407
|
-
templateRoot = path.dirname(templateRoot);
|
|
408
|
-
}
|
|
409
|
-
}
|
|
410
|
-
catch {
|
|
411
|
-
return null;
|
|
412
|
-
}
|
|
413
|
-
const candidates = normalizedAssetPath.startsWith("about/")
|
|
414
|
-
? [normalizedAssetPath]
|
|
415
|
-
: [`about/${normalizedAssetPath}`, normalizedAssetPath];
|
|
416
|
-
for (const candidate of candidates) {
|
|
417
|
-
const resolved = path.resolve(templateRoot, candidate);
|
|
418
|
-
if ((resolved === templateRoot || resolved.startsWith(`${templateRoot}${path.sep}`)) && existsSync(resolved)) {
|
|
419
|
-
return resolved;
|
|
420
|
-
}
|
|
421
|
-
}
|
|
422
|
-
return null;
|
|
423
|
-
}
|
|
424
|
-
function findTemplateRoot(modulePath) {
|
|
425
|
-
let current = path.dirname(modulePath);
|
|
426
|
-
for (let index = 0; index < 10; index += 1) {
|
|
427
|
-
if (existsSync(path.join(current, "template.config.json"))) {
|
|
428
|
-
return current;
|
|
429
|
-
}
|
|
430
|
-
const parent = path.dirname(current);
|
|
431
|
-
if (parent === current) {
|
|
432
|
-
break;
|
|
433
|
-
}
|
|
434
|
-
current = parent;
|
|
435
|
-
}
|
|
436
|
-
return null;
|
|
437
|
-
}
|
|
438
|
-
function nonBlank(value) {
|
|
439
|
-
const trimmed = value?.trim();
|
|
440
|
-
return trimmed ? trimmed : undefined;
|
|
441
|
-
}
|
|
442
|
-
function normalizePositiveInteger(value) {
|
|
443
|
-
return typeof value === "number" && Number.isFinite(value) && value > 0
|
|
444
|
-
? Math.floor(value)
|
|
445
|
-
: undefined;
|
|
446
|
-
}
|
|
447
|
-
//# sourceMappingURL=context.js.map
|