@koda-sl/baker-cli 0.39.29-dev.994437bd → 0.66.2
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 +1754 -7
- package/canvas/avocado-tutorial.json +89 -0
- package/canvas/hello-world-composition/index.html +83 -0
- package/canvas/hello-world-composition/meta.json +41 -0
- package/canvas/hello-world-overlay.json +37 -0
- package/canvas/phone-scroll-composition/index.html +141 -0
- package/canvas/phone-scroll-composition/meta.json +39 -0
- package/canvas/tiktok-captions-composition/index.html +126 -0
- package/canvas/tiktok-captions-composition/meta.json +46 -0
- package/canvas/video-overlay-composition/index.html +239 -0
- package/canvas/video-overlay-composition/meta.json +29 -0
- package/dist/cli.js +2 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/ads/meta/creatives.js +1 -1
- package/dist/commands/canvas/catalog.d.ts +2 -0
- package/dist/commands/canvas/catalog.d.ts.map +1 -0
- package/dist/commands/canvas/catalog.js +13 -0
- package/dist/commands/canvas/catalog.js.map +1 -0
- package/dist/commands/canvas/index.d.ts +2 -0
- package/dist/commands/canvas/index.d.ts.map +1 -0
- package/dist/commands/canvas/index.js +32 -0
- package/dist/commands/canvas/index.js.map +1 -0
- package/dist/commands/canvas/inspect.d.ts +16 -0
- package/dist/commands/canvas/inspect.d.ts.map +1 -0
- package/dist/commands/canvas/inspect.js +115 -0
- package/dist/commands/canvas/inspect.js.map +1 -0
- package/dist/commands/canvas/run.d.ts +24 -0
- package/dist/commands/canvas/run.d.ts.map +1 -0
- package/dist/commands/canvas/run.js +56 -0
- package/dist/commands/canvas/run.js.map +1 -0
- package/dist/commands/canvas/scaffold-static-ad.d.ts +40 -0
- package/dist/commands/canvas/scaffold-static-ad.d.ts.map +1 -0
- package/dist/commands/canvas/scaffold-static-ad.js +265 -0
- package/dist/commands/canvas/scaffold-static-ad.js.map +1 -0
- package/dist/commands/canvas/scaffold-video.d.ts +44 -0
- package/dist/commands/canvas/scaffold-video.d.ts.map +1 -0
- package/dist/commands/canvas/scaffold-video.js +235 -0
- package/dist/commands/canvas/scaffold-video.js.map +1 -0
- package/dist/commands/canvas/validate.d.ts +8 -0
- package/dist/commands/canvas/validate.d.ts.map +1 -0
- package/dist/commands/canvas/validate.js +37 -0
- package/dist/commands/canvas/validate.js.map +1 -0
- package/dist/commands/images/find.d.ts +1 -1
- package/dist/commands/images/find.d.ts.map +1 -1
- package/dist/commands/images/find.js +12 -3
- package/dist/commands/images/find.js.map +1 -1
- package/dist/commands/images/google.d.ts +1 -1
- package/dist/commands/images/google.d.ts.map +1 -1
- package/dist/commands/images/google.js +12 -3
- package/dist/commands/images/google.js.map +1 -1
- package/dist/commands/images/stock.d.ts +1 -1
- package/dist/commands/images/stock.d.ts.map +1 -1
- package/dist/commands/images/stock.js +7 -3
- package/dist/commands/images/stock.js.map +1 -1
- package/dist/commands/testimonials/list.d.ts.map +1 -1
- package/dist/commands/testimonials/list.js +21 -6
- package/dist/commands/testimonials/list.js.map +1 -1
- package/dist/commands/testimonials/search.d.ts.map +1 -1
- package/dist/commands/testimonials/search.js +21 -6
- package/dist/commands/testimonials/search.js.map +1 -1
- package/dist/commands/testimonials/search.test.js +3 -3
- package/dist/commands/testimonials/search.test.js.map +1 -1
- package/dist/engine/client/backend-client.d.ts +50 -0
- package/dist/engine/client/backend-client.d.ts.map +1 -0
- package/dist/engine/client/backend-client.js +20 -0
- package/dist/engine/client/backend-client.js.map +1 -0
- package/dist/engine/client/env.d.ts +7 -0
- package/dist/engine/client/env.d.ts.map +1 -0
- package/dist/engine/client/env.js +18 -0
- package/dist/engine/client/env.js.map +1 -0
- package/dist/engine/client/http.d.ts +56 -0
- package/dist/engine/client/http.d.ts.map +1 -0
- package/dist/engine/client/http.js +168 -0
- package/dist/engine/client/http.js.map +1 -0
- package/dist/engine/engine/cache-key.d.ts +19 -0
- package/dist/engine/engine/cache-key.d.ts.map +1 -0
- package/dist/engine/engine/cache-key.js +33 -0
- package/dist/engine/engine/cache-key.js.map +1 -0
- package/dist/engine/engine/canonical.d.ts +14 -0
- package/dist/engine/engine/canonical.d.ts.map +1 -0
- package/dist/engine/engine/canonical.js +53 -0
- package/dist/engine/engine/canonical.js.map +1 -0
- package/dist/engine/engine/composition-hash.d.ts +14 -0
- package/dist/engine/engine/composition-hash.d.ts.map +1 -0
- package/dist/engine/engine/composition-hash.js +51 -0
- package/dist/engine/engine/composition-hash.js.map +1 -0
- package/dist/engine/engine/composition-meta.d.ts +77 -0
- package/dist/engine/engine/composition-meta.d.ts.map +1 -0
- package/dist/engine/engine/composition-meta.js +199 -0
- package/dist/engine/engine/composition-meta.js.map +1 -0
- package/dist/engine/engine/context.d.ts +16 -0
- package/dist/engine/engine/context.d.ts.map +1 -0
- package/dist/engine/engine/context.js +2 -0
- package/dist/engine/engine/context.js.map +1 -0
- package/dist/engine/engine/define.d.ts +69 -0
- package/dist/engine/engine/define.d.ts.map +1 -0
- package/dist/engine/engine/define.js +9 -0
- package/dist/engine/engine/define.js.map +1 -0
- package/dist/engine/engine/didyoumean.d.ts +2 -0
- package/dist/engine/engine/didyoumean.d.ts.map +1 -0
- package/dist/engine/engine/didyoumean.js +58 -0
- package/dist/engine/engine/didyoumean.js.map +1 -0
- package/dist/engine/engine/errors.d.ts +41 -0
- package/dist/engine/engine/errors.d.ts.map +1 -0
- package/dist/engine/engine/errors.js +45 -0
- package/dist/engine/engine/errors.js.map +1 -0
- package/dist/engine/engine/executor.d.ts +72 -0
- package/dist/engine/engine/executor.d.ts.map +1 -0
- package/dist/engine/engine/executor.js +445 -0
- package/dist/engine/engine/executor.js.map +1 -0
- package/dist/engine/engine/refs.d.ts +12 -0
- package/dist/engine/engine/refs.d.ts.map +1 -0
- package/dist/engine/engine/refs.js +48 -0
- package/dist/engine/engine/refs.js.map +1 -0
- package/dist/engine/engine/registry.d.ts +11 -0
- package/dist/engine/engine/registry.d.ts.map +1 -0
- package/dist/engine/engine/registry.js +26 -0
- package/dist/engine/engine/registry.js.map +1 -0
- package/dist/engine/engine/scheduler.d.ts +10 -0
- package/dist/engine/engine/scheduler.d.ts.map +1 -0
- package/dist/engine/engine/scheduler.js +89 -0
- package/dist/engine/engine/scheduler.js.map +1 -0
- package/dist/engine/engine/validator.d.ts +31 -0
- package/dist/engine/engine/validator.d.ts.map +1 -0
- package/dist/engine/engine/validator.js +501 -0
- package/dist/engine/engine/validator.js.map +1 -0
- package/dist/engine/index.d.ts +33 -0
- package/dist/engine/index.d.ts.map +1 -0
- package/dist/engine/index.js +104 -0
- package/dist/engine/index.js.map +1 -0
- package/dist/engine/lib/ulid.d.ts +3 -0
- package/dist/engine/lib/ulid.d.ts.map +1 -0
- package/dist/engine/lib/ulid.js +36 -0
- package/dist/engine/lib/ulid.js.map +1 -0
- package/dist/engine/models/canvas-ad-params.test.d.ts +2 -0
- package/dist/engine/models/canvas-ad-params.test.d.ts.map +1 -0
- package/dist/engine/models/canvas-ad-params.test.js +60 -0
- package/dist/engine/models/canvas-ad-params.test.js.map +1 -0
- package/dist/engine/models/registry.d.ts +63 -0
- package/dist/engine/models/registry.d.ts.map +1 -0
- package/dist/engine/models/registry.js +432 -0
- package/dist/engine/models/registry.js.map +1 -0
- package/dist/engine/models/validateParams.d.ts +38 -0
- package/dist/engine/models/validateParams.d.ts.map +1 -0
- package/dist/engine/models/validateParams.js +166 -0
- package/dist/engine/models/validateParams.js.map +1 -0
- package/dist/engine/nodes/ingest.d.ts +113 -0
- package/dist/engine/nodes/ingest.d.ts.map +1 -0
- package/dist/engine/nodes/ingest.js +555 -0
- package/dist/engine/nodes/ingest.js.map +1 -0
- package/dist/engine/nodes/ingest.svg.test.d.ts +2 -0
- package/dist/engine/nodes/ingest.svg.test.d.ts.map +1 -0
- package/dist/engine/nodes/ingest.svg.test.js +30 -0
- package/dist/engine/nodes/ingest.svg.test.js.map +1 -0
- package/dist/engine/nodes/local/audioTimeline.d.ts +82 -0
- package/dist/engine/nodes/local/audioTimeline.d.ts.map +1 -0
- package/dist/engine/nodes/local/audioTimeline.js +97 -0
- package/dist/engine/nodes/local/audioTimeline.js.map +1 -0
- package/dist/engine/nodes/local/ffmpeg.d.ts +56 -0
- package/dist/engine/nodes/local/ffmpeg.d.ts.map +1 -0
- package/dist/engine/nodes/local/ffmpeg.js +50 -0
- package/dist/engine/nodes/local/ffmpeg.js.map +1 -0
- package/dist/engine/nodes/local/fontSpecimen.d.ts +50 -0
- package/dist/engine/nodes/local/fontSpecimen.d.ts.map +1 -0
- package/dist/engine/nodes/local/fontSpecimen.js +198 -0
- package/dist/engine/nodes/local/fontSpecimen.js.map +1 -0
- package/dist/engine/nodes/local/hyperframe-snapshot.d.ts +116 -0
- package/dist/engine/nodes/local/hyperframe-snapshot.d.ts.map +1 -0
- package/dist/engine/nodes/local/hyperframe-snapshot.js +230 -0
- package/dist/engine/nodes/local/hyperframe-snapshot.js.map +1 -0
- package/dist/engine/nodes/local/hyperframe.d.ts +123 -0
- package/dist/engine/nodes/local/hyperframe.d.ts.map +1 -0
- package/dist/engine/nodes/local/hyperframe.js +367 -0
- package/dist/engine/nodes/local/hyperframe.js.map +1 -0
- package/dist/engine/nodes/local/imagemagick.d.ts +56 -0
- package/dist/engine/nodes/local/imagemagick.d.ts.map +1 -0
- package/dist/engine/nodes/local/imagemagick.js +71 -0
- package/dist/engine/nodes/local/imagemagick.js.map +1 -0
- package/dist/engine/nodes/local/lib/assets.d.ts +20 -0
- package/dist/engine/nodes/local/lib/assets.d.ts.map +1 -0
- package/dist/engine/nodes/local/lib/assets.js +40 -0
- package/dist/engine/nodes/local/lib/assets.js.map +1 -0
- package/dist/engine/nodes/local/lib/cli-runner.d.ts +78 -0
- package/dist/engine/nodes/local/lib/cli-runner.d.ts.map +1 -0
- package/dist/engine/nodes/local/lib/cli-runner.js +254 -0
- package/dist/engine/nodes/local/lib/cli-runner.js.map +1 -0
- package/dist/engine/nodes/local/lib/ffmpeg.d.ts +23 -0
- package/dist/engine/nodes/local/lib/ffmpeg.d.ts.map +1 -0
- package/dist/engine/nodes/local/lib/ffmpeg.js +75 -0
- package/dist/engine/nodes/local/lib/ffmpeg.js.map +1 -0
- package/dist/engine/nodes/local/lib/hyperframe-errors.d.ts +2 -0
- package/dist/engine/nodes/local/lib/hyperframe-errors.d.ts.map +1 -0
- package/dist/engine/nodes/local/lib/hyperframe-errors.js +47 -0
- package/dist/engine/nodes/local/lib/hyperframe-errors.js.map +1 -0
- package/dist/engine/nodes/local/lib/templating.d.ts +22 -0
- package/dist/engine/nodes/local/lib/templating.d.ts.map +1 -0
- package/dist/engine/nodes/local/lib/templating.js +85 -0
- package/dist/engine/nodes/local/lib/templating.js.map +1 -0
- package/dist/engine/nodes/local/text.d.ts +6 -0
- package/dist/engine/nodes/local/text.d.ts.map +1 -0
- package/dist/engine/nodes/local/text.js +15 -0
- package/dist/engine/nodes/local/text.js.map +1 -0
- package/dist/engine/nodes/remote/delegate.d.ts +25 -0
- package/dist/engine/nodes/remote/delegate.d.ts.map +1 -0
- package/dist/engine/nodes/remote/delegate.js +160 -0
- package/dist/engine/nodes/remote/delegate.js.map +1 -0
- package/dist/engine/nodes/remote/dialogue.d.ts +34 -0
- package/dist/engine/nodes/remote/dialogue.d.ts.map +1 -0
- package/dist/engine/nodes/remote/dialogue.js +54 -0
- package/dist/engine/nodes/remote/dialogue.js.map +1 -0
- package/dist/engine/nodes/remote/image.d.ts +42 -0
- package/dist/engine/nodes/remote/image.d.ts.map +1 -0
- package/dist/engine/nodes/remote/image.js +43 -0
- package/dist/engine/nodes/remote/image.js.map +1 -0
- package/dist/engine/nodes/remote/imageAspectAdapt.d.ts +30 -0
- package/dist/engine/nodes/remote/imageAspectAdapt.d.ts.map +1 -0
- package/dist/engine/nodes/remote/imageAspectAdapt.js +42 -0
- package/dist/engine/nodes/remote/imageAspectAdapt.js.map +1 -0
- package/dist/engine/nodes/remote/imageBackgroundRemove.d.ts +39 -0
- package/dist/engine/nodes/remote/imageBackgroundRemove.d.ts.map +1 -0
- package/dist/engine/nodes/remote/imageBackgroundRemove.js +37 -0
- package/dist/engine/nodes/remote/imageBackgroundRemove.js.map +1 -0
- package/dist/engine/nodes/remote/imageDescribe.d.ts +29 -0
- package/dist/engine/nodes/remote/imageDescribe.d.ts.map +1 -0
- package/dist/engine/nodes/remote/imageDescribe.js +25 -0
- package/dist/engine/nodes/remote/imageDescribe.js.map +1 -0
- package/dist/engine/nodes/remote/imageReferenceSheet.d.ts +34 -0
- package/dist/engine/nodes/remote/imageReferenceSheet.d.ts.map +1 -0
- package/dist/engine/nodes/remote/imageReferenceSheet.js +38 -0
- package/dist/engine/nodes/remote/imageReferenceSheet.js.map +1 -0
- package/dist/engine/nodes/remote/imageSearch.d.ts +18 -0
- package/dist/engine/nodes/remote/imageSearch.d.ts.map +1 -0
- package/dist/engine/nodes/remote/imageSearch.js +22 -0
- package/dist/engine/nodes/remote/imageSearch.js.map +1 -0
- package/dist/engine/nodes/remote/imageSelect.d.ts +39 -0
- package/dist/engine/nodes/remote/imageSelect.d.ts.map +1 -0
- package/dist/engine/nodes/remote/imageSelect.js +45 -0
- package/dist/engine/nodes/remote/imageSelect.js.map +1 -0
- package/dist/engine/nodes/remote/music.d.ts +45 -0
- package/dist/engine/nodes/remote/music.d.ts.map +1 -0
- package/dist/engine/nodes/remote/music.js +73 -0
- package/dist/engine/nodes/remote/music.js.map +1 -0
- package/dist/engine/nodes/remote/soundEffect.d.ts +21 -0
- package/dist/engine/nodes/remote/soundEffect.d.ts.map +1 -0
- package/dist/engine/nodes/remote/soundEffect.js +41 -0
- package/dist/engine/nodes/remote/soundEffect.js.map +1 -0
- package/dist/engine/nodes/remote/textGenerate.d.ts +21 -0
- package/dist/engine/nodes/remote/textGenerate.d.ts.map +1 -0
- package/dist/engine/nodes/remote/textGenerate.js +27 -0
- package/dist/engine/nodes/remote/textGenerate.js.map +1 -0
- package/dist/engine/nodes/remote/tts.d.ts +45 -0
- package/dist/engine/nodes/remote/tts.d.ts.map +1 -0
- package/dist/engine/nodes/remote/tts.js +66 -0
- package/dist/engine/nodes/remote/tts.js.map +1 -0
- package/dist/engine/nodes/remote/video.d.ts +58 -0
- package/dist/engine/nodes/remote/video.d.ts.map +1 -0
- package/dist/engine/nodes/remote/video.js +44 -0
- package/dist/engine/nodes/remote/video.js.map +1 -0
- package/dist/engine/nodes/remote/videoBackgroundRemove.d.ts +30 -0
- package/dist/engine/nodes/remote/videoBackgroundRemove.d.ts.map +1 -0
- package/dist/engine/nodes/remote/videoBackgroundRemove.js +29 -0
- package/dist/engine/nodes/remote/videoBackgroundRemove.js.map +1 -0
- package/dist/engine/nodes/remote/videoDeconstruct.d.ts +61 -0
- package/dist/engine/nodes/remote/videoDeconstruct.d.ts.map +1 -0
- package/dist/engine/nodes/remote/videoDeconstruct.js +40 -0
- package/dist/engine/nodes/remote/videoDeconstruct.js.map +1 -0
- package/dist/engine/nodes/remote/videoLipsync.d.ts +37 -0
- package/dist/engine/nodes/remote/videoLipsync.d.ts.map +1 -0
- package/dist/engine/nodes/remote/videoLipsync.js +26 -0
- package/dist/engine/nodes/remote/videoLipsync.js.map +1 -0
- package/dist/engine/nodes/remote/videoTranscribe.d.ts +116 -0
- package/dist/engine/nodes/remote/videoTranscribe.d.ts.map +1 -0
- package/dist/engine/nodes/remote/videoTranscribe.js +123 -0
- package/dist/engine/nodes/remote/videoTranscribe.js.map +1 -0
- package/dist/engine/nodes/remote/voiceSelect.d.ts +28 -0
- package/dist/engine/nodes/remote/voiceSelect.d.ts.map +1 -0
- package/dist/engine/nodes/remote/voiceSelect.js +25 -0
- package/dist/engine/nodes/remote/voiceSelect.js.map +1 -0
- package/dist/engine/scaffold/staticAd.d.ts +44 -0
- package/dist/engine/scaffold/staticAd.d.ts.map +1 -0
- package/dist/engine/scaffold/staticAd.js +243 -0
- package/dist/engine/scaffold/staticAd.js.map +1 -0
- package/dist/engine/scaffold/video.d.ts +56 -0
- package/dist/engine/scaffold/video.d.ts.map +1 -0
- package/dist/engine/scaffold/video.js +709 -0
- package/dist/engine/scaffold/video.js.map +1 -0
- package/dist/engine/schema/canvas.d.ts +41 -0
- package/dist/engine/schema/canvas.d.ts.map +1 -0
- package/dist/engine/schema/canvas.js +67 -0
- package/dist/engine/schema/canvas.js.map +1 -0
- package/dist/engine/schema/catalog.d.ts +25 -0
- package/dist/engine/schema/catalog.d.ts.map +1 -0
- package/dist/engine/schema/catalog.js +48 -0
- package/dist/engine/schema/catalog.js.map +1 -0
- package/dist/engine/schema/primitives.d.ts +6 -0
- package/dist/engine/schema/primitives.d.ts.map +1 -0
- package/dist/engine/schema/primitives.js +4 -0
- package/dist/engine/schema/primitives.js.map +1 -0
- package/dist/engine/schema/prompts.d.ts +4 -0
- package/dist/engine/schema/prompts.d.ts.map +1 -0
- package/dist/engine/schema/prompts.js +23 -0
- package/dist/engine/schema/prompts.js.map +1 -0
- package/dist/engine/schema/refs.d.ts +113 -0
- package/dist/engine/schema/refs.d.ts.map +1 -0
- package/dist/engine/schema/refs.js +35 -0
- package/dist/engine/schema/refs.js.map +1 -0
- package/dist/engine/storage/asset-store.d.ts +48 -0
- package/dist/engine/storage/asset-store.d.ts.map +1 -0
- package/dist/engine/storage/asset-store.js +166 -0
- package/dist/engine/storage/asset-store.js.map +1 -0
- package/dist/engine/storage/cache-store.d.ts +21 -0
- package/dist/engine/storage/cache-store.d.ts.map +1 -0
- package/dist/engine/storage/cache-store.js +31 -0
- package/dist/engine/storage/cache-store.js.map +1 -0
- package/dist/engine/storage/output-writer.d.ts +18 -0
- package/dist/engine/storage/output-writer.d.ts.map +1 -0
- package/dist/engine/storage/output-writer.js +52 -0
- package/dist/engine/storage/output-writer.js.map +1 -0
- package/dist/engine/storage/sha256.d.ts +2 -0
- package/dist/engine/storage/sha256.d.ts.map +1 -0
- package/dist/engine/storage/sha256.js +7 -0
- package/dist/engine/storage/sha256.js.map +1 -0
- package/package.json +15 -3
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"validateParams.js","sourceRoot":"","sources":["../../../src/engine/models/validateParams.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
import type { ExecCtx } from "../engine/context.ts";
|
|
3
|
+
import type { AssetKind, AssetRef } from "../schema/refs.ts";
|
|
4
|
+
declare const IngestParams: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
5
|
+
source: z.ZodLiteral<"url">;
|
|
6
|
+
url: z.ZodString;
|
|
7
|
+
expect: z.ZodEnum<{
|
|
8
|
+
json: "json";
|
|
9
|
+
video: "video";
|
|
10
|
+
image: "image";
|
|
11
|
+
audio: "audio";
|
|
12
|
+
text: "text";
|
|
13
|
+
font: "font";
|
|
14
|
+
}>;
|
|
15
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
16
|
+
source: z.ZodLiteral<"path">;
|
|
17
|
+
path: z.ZodString;
|
|
18
|
+
expect: z.ZodEnum<{
|
|
19
|
+
json: "json";
|
|
20
|
+
video: "video";
|
|
21
|
+
image: "image";
|
|
22
|
+
audio: "audio";
|
|
23
|
+
text: "text";
|
|
24
|
+
font: "font";
|
|
25
|
+
}>;
|
|
26
|
+
}, z.core.$strict>], "source">;
|
|
27
|
+
type IngestParamsT = z.infer<typeof IngestParams>;
|
|
28
|
+
type Strategy = "direct_fetch" | "handinger" | "yt_dlp" | "local_file";
|
|
29
|
+
declare function resolveStrategy(params: IngestParamsT): Strategy;
|
|
30
|
+
export declare const ingestNode: import("../engine/define.ts").NodeDefinition<Record<string, never>, {
|
|
31
|
+
source: "url";
|
|
32
|
+
url: string;
|
|
33
|
+
expect: "json" | "video" | "image" | "audio" | "text" | "font";
|
|
34
|
+
} | {
|
|
35
|
+
source: "path";
|
|
36
|
+
path: string;
|
|
37
|
+
expect: "json" | "video" | "image" | "audio" | "text" | "font";
|
|
38
|
+
}, {
|
|
39
|
+
asset: {
|
|
40
|
+
sha256: string;
|
|
41
|
+
mime: string;
|
|
42
|
+
kind: "image";
|
|
43
|
+
url?: string | undefined;
|
|
44
|
+
path?: string | undefined;
|
|
45
|
+
metadata?: Record<string, unknown> | undefined;
|
|
46
|
+
width?: number | undefined;
|
|
47
|
+
height?: number | undefined;
|
|
48
|
+
} | {
|
|
49
|
+
sha256: string;
|
|
50
|
+
mime: string;
|
|
51
|
+
kind: "video";
|
|
52
|
+
url?: string | undefined;
|
|
53
|
+
path?: string | undefined;
|
|
54
|
+
metadata?: Record<string, unknown> | undefined;
|
|
55
|
+
width?: number | undefined;
|
|
56
|
+
height?: number | undefined;
|
|
57
|
+
duration_ms?: number | undefined;
|
|
58
|
+
} | {
|
|
59
|
+
sha256: string;
|
|
60
|
+
mime: string;
|
|
61
|
+
kind: "audio";
|
|
62
|
+
url?: string | undefined;
|
|
63
|
+
path?: string | undefined;
|
|
64
|
+
metadata?: Record<string, unknown> | undefined;
|
|
65
|
+
duration_ms?: number | undefined;
|
|
66
|
+
} | {
|
|
67
|
+
sha256: string;
|
|
68
|
+
mime: string;
|
|
69
|
+
kind: "json";
|
|
70
|
+
url?: string | undefined;
|
|
71
|
+
path?: string | undefined;
|
|
72
|
+
metadata?: Record<string, unknown> | undefined;
|
|
73
|
+
} | {
|
|
74
|
+
sha256: string;
|
|
75
|
+
mime: string;
|
|
76
|
+
kind: "text";
|
|
77
|
+
url?: string | undefined;
|
|
78
|
+
path?: string | undefined;
|
|
79
|
+
metadata?: Record<string, unknown> | undefined;
|
|
80
|
+
} | {
|
|
81
|
+
sha256: string;
|
|
82
|
+
mime: string;
|
|
83
|
+
kind: "font";
|
|
84
|
+
url?: string | undefined;
|
|
85
|
+
path?: string | undefined;
|
|
86
|
+
metadata?: Record<string, unknown> | undefined;
|
|
87
|
+
};
|
|
88
|
+
}>;
|
|
89
|
+
declare function resolveLocalPath(input: string): string;
|
|
90
|
+
export declare function inferMimeFromPath(absPath: string, sniffBytes: Buffer): string;
|
|
91
|
+
export declare function rasterizeSvgToPng(bytes: Buffer): Promise<Buffer>;
|
|
92
|
+
declare function inferKindFromMime(mime: string): AssetKind | null;
|
|
93
|
+
type YtDlpInfo = Record<string, unknown>;
|
|
94
|
+
declare function buildYtDlpMetadata(sourceUrl: string, info: YtDlpInfo): Record<string, unknown>;
|
|
95
|
+
export declare function uploadAndIngest(args: {
|
|
96
|
+
bytes: Buffer;
|
|
97
|
+
kind: AssetKind;
|
|
98
|
+
mime: string;
|
|
99
|
+
metadata: Record<string, unknown>;
|
|
100
|
+
ctx: ExecCtx;
|
|
101
|
+
}): Promise<AssetRef>;
|
|
102
|
+
export declare const _internal: {
|
|
103
|
+
resolveStrategy: typeof resolveStrategy;
|
|
104
|
+
resolveLocalPath: typeof resolveLocalPath;
|
|
105
|
+
inferMimeFromPath: typeof inferMimeFromPath;
|
|
106
|
+
inferKindFromMime: typeof inferKindFromMime;
|
|
107
|
+
buildYtDlpMetadata: typeof buildYtDlpMetadata;
|
|
108
|
+
YT_DLP_EXT: Record<"json" | "video" | "image" | "audio" | "text" | "font", string>;
|
|
109
|
+
YT_DLP_MIME: Record<"json" | "video" | "image" | "audio" | "text" | "font", string>;
|
|
110
|
+
EXT_TO_MIME: Record<string, string>;
|
|
111
|
+
};
|
|
112
|
+
export {};
|
|
113
|
+
//# sourceMappingURL=ingest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ingest.d.ts","sourceRoot":"","sources":["../../../src/engine/nodes/ingest.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAGpD,OAAO,KAAK,EAAE,SAAS,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAc7D,QAAA,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;8BAehB,CAAC;AAKH,KAAK,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC;AAQlD,KAAK,QAAQ,GAAG,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,YAAY,CAAC;AAIvE,iBAAS,eAAe,CAAC,MAAM,EAAE,aAAa,GAAG,QAAQ,CAoBxD;AAcD,eAAO,MAAM,UAAU;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyBrB,CAAC;AAqEH,iBAAS,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAQ/C;AA+CD,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ7E;AAKD,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAatE;AAuBD,iBAAS,iBAAiB,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,CAQzD;AAiJD,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAuDzC,iBAAS,kBAAkB,CAAC,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAavF;AAMD,wBAAsB,eAAe,CAAC,IAAI,EAAE;IAC1C,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,EAAE,SAAS,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAClC,GAAG,EAAE,OAAO,CAAC;CACd,GAAG,OAAO,CAAC,QAAQ,CAAC,CAuBpB;AAqFD,eAAO,MAAM,SAAS;;;;;;;;;CASrB,CAAC"}
|
|
@@ -0,0 +1,555 @@
|
|
|
1
|
+
import { execFile as execFileCb, spawn } from "node:child_process";
|
|
2
|
+
import { mkdtemp, readdir, readFile, rm, stat } from "node:fs/promises";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { promisify } from "node:util";
|
|
6
|
+
import { z } from "zod";
|
|
7
|
+
import { defineNode } from "../engine/define.js";
|
|
8
|
+
import { NodeExecutionError } from "../engine/errors.js";
|
|
9
|
+
import { AssetRef as AssetRefSchema } from "../schema/refs.js";
|
|
10
|
+
import { MAX_ASSET_BYTES } from "../storage/asset-store.js";
|
|
11
|
+
import { sha256Hex } from "../storage/sha256.js";
|
|
12
|
+
import { callBackendExec } from "./remote/delegate.js";
|
|
13
|
+
const execFile = promisify(execFileCb);
|
|
14
|
+
// ---------------------------------------------------------------------------
|
|
15
|
+
// Schema — discriminated union on `source`
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
const ExpectEnum = z.enum(["image", "video", "audio", "text", "json", "font"]);
|
|
18
|
+
const IngestParams = z.discriminatedUnion("source", [
|
|
19
|
+
z
|
|
20
|
+
.object({
|
|
21
|
+
source: z.literal("url"),
|
|
22
|
+
url: z.string().url(),
|
|
23
|
+
expect: ExpectEnum,
|
|
24
|
+
})
|
|
25
|
+
.strict(),
|
|
26
|
+
z
|
|
27
|
+
.object({
|
|
28
|
+
source: z.literal("path"),
|
|
29
|
+
path: z.string().min(1),
|
|
30
|
+
expect: ExpectEnum,
|
|
31
|
+
})
|
|
32
|
+
.strict(),
|
|
33
|
+
]);
|
|
34
|
+
const IngestInputs = z.object({}).strict();
|
|
35
|
+
const IngestOutputs = z.object({ asset: AssetRefSchema }).strict();
|
|
36
|
+
const RAW_TEXT_EXTENSIONS = [".txt", ".md"];
|
|
37
|
+
function resolveStrategy(params) {
|
|
38
|
+
if (params.source === "path")
|
|
39
|
+
return "local_file";
|
|
40
|
+
switch (params.expect) {
|
|
41
|
+
case "video":
|
|
42
|
+
case "audio":
|
|
43
|
+
return "yt_dlp";
|
|
44
|
+
case "image":
|
|
45
|
+
case "json":
|
|
46
|
+
case "font":
|
|
47
|
+
return "direct_fetch";
|
|
48
|
+
case "text": {
|
|
49
|
+
const p = safePathname(params.url);
|
|
50
|
+
const isRawText = RAW_TEXT_EXTENSIONS.some((ext) => p.endsWith(ext));
|
|
51
|
+
return isRawText ? "direct_fetch" : "handinger";
|
|
52
|
+
}
|
|
53
|
+
default: {
|
|
54
|
+
params.expect;
|
|
55
|
+
throw new Error(`ingest: unhandled expect=${String(params.expect)}`);
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
function safePathname(rawUrl) {
|
|
60
|
+
try {
|
|
61
|
+
return new URL(rawUrl).pathname.toLowerCase();
|
|
62
|
+
}
|
|
63
|
+
catch {
|
|
64
|
+
return "";
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
// ---------------------------------------------------------------------------
|
|
68
|
+
// Node definition
|
|
69
|
+
// ---------------------------------------------------------------------------
|
|
70
|
+
export const ingestNode = defineNode({
|
|
71
|
+
id: "ingest",
|
|
72
|
+
version: "1.1.0",
|
|
73
|
+
category: "io",
|
|
74
|
+
location: "local",
|
|
75
|
+
summary: "Ingest an external URL or a local file into the asset store. Declare the kind you expect (image/video/audio/text/json/font); the node picks the strategy. For source=url: yt-dlp for video/audio (YouTube/TikTok/Vimeo/etc. and direct file URLs), Handinger for HTML/PDF pages → markdown, direct HTTP fetch for binary URLs (images, fonts) and raw .txt/.md. For source=path: read from the local filesystem and upload to R2.",
|
|
76
|
+
when_to_use: 'Use as the on-ramp for any external asset a canvas needs. `expect` makes the output port statically typed so downstream wiring is checked before any byte is fetched. Pick `source: "url"` for portable canvas; pick `source: "path"` for local files (canvas is not portable across machines without the file). Compose with other nodes for richer pipelines (e.g. `ingest expect=video` → `video_transcribe`).',
|
|
77
|
+
inputs: IngestInputs,
|
|
78
|
+
params: IngestParams,
|
|
79
|
+
outputs: IngestOutputs,
|
|
80
|
+
cost: () => ({ credits: 0, seconds_estimate: 5 }),
|
|
81
|
+
// Output kind is declared in params, so validators/UI can type the port
|
|
82
|
+
// statically without running the node.
|
|
83
|
+
outputKinds: (params) => ({ asset: params.expect }),
|
|
84
|
+
async execute({ params, ctx }) {
|
|
85
|
+
const strategy = resolveStrategy(params);
|
|
86
|
+
const source = params.source === "url" ? params.url : params.path;
|
|
87
|
+
ctx.log(`ingest: ${strategy} (${params.expect}) ${source}`);
|
|
88
|
+
const asset = await runStrategy(strategy, params, ctx);
|
|
89
|
+
return { asset };
|
|
90
|
+
},
|
|
91
|
+
});
|
|
92
|
+
function runStrategy(strategy, params, ctx) {
|
|
93
|
+
if (params.source === "path") {
|
|
94
|
+
if (strategy !== "local_file") {
|
|
95
|
+
throw new Error(`ingest: invariant — source=path but strategy=${strategy}`);
|
|
96
|
+
}
|
|
97
|
+
return execLocalFile(params, ctx);
|
|
98
|
+
}
|
|
99
|
+
// params.source === "url" — narrowed
|
|
100
|
+
switch (strategy) {
|
|
101
|
+
case "direct_fetch":
|
|
102
|
+
return execDirectFetch(params, ctx);
|
|
103
|
+
case "handinger":
|
|
104
|
+
return execHandinger(params, ctx);
|
|
105
|
+
case "yt_dlp":
|
|
106
|
+
return execYtDlp(params, ctx);
|
|
107
|
+
case "local_file":
|
|
108
|
+
throw new Error(`ingest: invariant — source=url but strategy=local_file`);
|
|
109
|
+
default: {
|
|
110
|
+
strategy;
|
|
111
|
+
throw new Error(`ingest: unhandled strategy=${String(strategy)}`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
// ---------------------------------------------------------------------------
|
|
116
|
+
// direct_fetch / handinger — both go through the backend
|
|
117
|
+
// ---------------------------------------------------------------------------
|
|
118
|
+
async function execDirectFetch(params, ctx) {
|
|
119
|
+
const result = await callBackendExec({
|
|
120
|
+
nodeType: "ingest",
|
|
121
|
+
nodeVersion: ingestNode.version,
|
|
122
|
+
params: { strategy: "direct_fetch", url: params.url, expect: params.expect },
|
|
123
|
+
inputs: {},
|
|
124
|
+
outputKinds: { asset: params.expect },
|
|
125
|
+
ctx,
|
|
126
|
+
});
|
|
127
|
+
return assertAssetOutput(result, params.expect);
|
|
128
|
+
}
|
|
129
|
+
async function execHandinger(params, ctx) {
|
|
130
|
+
const result = await callBackendExec({
|
|
131
|
+
nodeType: "ingest",
|
|
132
|
+
nodeVersion: ingestNode.version,
|
|
133
|
+
params: { strategy: "handinger", url: params.url },
|
|
134
|
+
inputs: {},
|
|
135
|
+
outputKinds: { asset: "text" },
|
|
136
|
+
ctx,
|
|
137
|
+
});
|
|
138
|
+
return assertAssetOutput(result, "text");
|
|
139
|
+
}
|
|
140
|
+
function assertAssetOutput(result, expected) {
|
|
141
|
+
const asset = result.asset;
|
|
142
|
+
if (!asset || typeof asset !== "object" || !("kind" in asset)) {
|
|
143
|
+
throw new Error(`ingest: backend returned no asset (got ${JSON.stringify(result)})`);
|
|
144
|
+
}
|
|
145
|
+
if (asset.kind !== expected) {
|
|
146
|
+
throw new Error(`ingest: backend returned kind=${asset.kind} but expected ${expected}`);
|
|
147
|
+
}
|
|
148
|
+
return asset;
|
|
149
|
+
}
|
|
150
|
+
// ---------------------------------------------------------------------------
|
|
151
|
+
// local_file — read from disk, sniff mime, upload to R2 via presign
|
|
152
|
+
// ---------------------------------------------------------------------------
|
|
153
|
+
function resolveLocalPath(input) {
|
|
154
|
+
if (input.startsWith("~")) {
|
|
155
|
+
throw new Error(`ingest: ~ path expansion is not supported (got "${input}"). Use an absolute path or a cwd-relative path.`);
|
|
156
|
+
}
|
|
157
|
+
if (path.isAbsolute(input))
|
|
158
|
+
return input;
|
|
159
|
+
return path.resolve(process.cwd(), input);
|
|
160
|
+
}
|
|
161
|
+
const EXT_TO_MIME = {
|
|
162
|
+
png: "image/png",
|
|
163
|
+
jpg: "image/jpeg",
|
|
164
|
+
jpeg: "image/jpeg",
|
|
165
|
+
webp: "image/webp",
|
|
166
|
+
gif: "image/gif",
|
|
167
|
+
avif: "image/avif",
|
|
168
|
+
mp4: "video/mp4",
|
|
169
|
+
webm: "video/webm",
|
|
170
|
+
mov: "video/quicktime",
|
|
171
|
+
m4v: "video/mp4",
|
|
172
|
+
mp3: "audio/mpeg",
|
|
173
|
+
wav: "audio/wav",
|
|
174
|
+
m4a: "audio/mp4",
|
|
175
|
+
ogg: "audio/ogg",
|
|
176
|
+
flac: "audio/flac",
|
|
177
|
+
json: "application/json",
|
|
178
|
+
txt: "text/plain",
|
|
179
|
+
md: "text/markdown",
|
|
180
|
+
markdown: "text/markdown",
|
|
181
|
+
html: "text/html",
|
|
182
|
+
htm: "text/html",
|
|
183
|
+
csv: "text/csv",
|
|
184
|
+
ttf: "font/ttf",
|
|
185
|
+
otf: "font/otf",
|
|
186
|
+
woff: "font/woff",
|
|
187
|
+
woff2: "font/woff2",
|
|
188
|
+
svg: "image/svg+xml",
|
|
189
|
+
};
|
|
190
|
+
const SVG_MIME = "image/svg+xml";
|
|
191
|
+
// Crispness target for rasterized SVGs. SVGs (brand logos especially) are often authored at small
|
|
192
|
+
// intrinsic sizes — sharp rasterizes at the intrinsic size unless we raise the render density, which
|
|
193
|
+
// would leave a tiny, blurry logo. We measure the intrinsic size and scale the density so the longest
|
|
194
|
+
// edge lands near SVG_TARGET_LONGEST_PX, clamped so a pathological viewBox can't blow up memory.
|
|
195
|
+
const SVG_TARGET_LONGEST_PX = 2048;
|
|
196
|
+
const SVG_BASE_DENSITY = 72;
|
|
197
|
+
const SVG_MAX_DENSITY = 2400;
|
|
198
|
+
function sniffSvg(buf) {
|
|
199
|
+
const head = buf.subarray(0, Math.min(buf.length, 512)).toString("utf8").trimStart();
|
|
200
|
+
return head.startsWith("<?xml") ? head.includes("<svg") : head.startsWith("<svg");
|
|
201
|
+
}
|
|
202
|
+
export function inferMimeFromPath(absPath, sniffBytes) {
|
|
203
|
+
const ext = path.extname(absPath).slice(1).toLowerCase();
|
|
204
|
+
const fromExt = EXT_TO_MIME[ext];
|
|
205
|
+
if (fromExt)
|
|
206
|
+
return fromExt;
|
|
207
|
+
const fromBytes = sniffImageMime(sniffBytes);
|
|
208
|
+
if (fromBytes)
|
|
209
|
+
return fromBytes;
|
|
210
|
+
if (sniffSvg(sniffBytes))
|
|
211
|
+
return SVG_MIME;
|
|
212
|
+
return "application/octet-stream";
|
|
213
|
+
}
|
|
214
|
+
// Image-generation models can't consume raw SVG markup, so an ingested SVG (e.g. a brand logo) is
|
|
215
|
+
// rasterized to a transparent PNG here. Transparency is kept so the wordmark composites cleanly onto
|
|
216
|
+
// any background downstream.
|
|
217
|
+
export async function rasterizeSvgToPng(bytes) {
|
|
218
|
+
// `sharp` is a native module that can't load in the Convex runtime. This function only runs in the
|
|
219
|
+
// local execLocalFile path (the ingest node is location: "local"), so import it lazily — a top-level
|
|
220
|
+
// import would drag the native binary into the Convex bundle and break `convex deploy`.
|
|
221
|
+
const { default: sharp } = await import("sharp");
|
|
222
|
+
const base = await sharp(bytes).metadata();
|
|
223
|
+
const longest = Math.max(base.width ?? 0, base.height ?? 0);
|
|
224
|
+
let density = SVG_BASE_DENSITY;
|
|
225
|
+
if (longest > 0) {
|
|
226
|
+
const scaled = Math.round((SVG_BASE_DENSITY * SVG_TARGET_LONGEST_PX) / longest);
|
|
227
|
+
density = Math.min(SVG_MAX_DENSITY, Math.max(SVG_BASE_DENSITY, scaled));
|
|
228
|
+
}
|
|
229
|
+
return await sharp(bytes, { density }).png({ force: true, palette: false }).toBuffer();
|
|
230
|
+
}
|
|
231
|
+
function sniffImageMime(buf) {
|
|
232
|
+
if (buf.length < 4)
|
|
233
|
+
return null;
|
|
234
|
+
if (buf[0] === 0x89 && buf[1] === 0x50 && buf[2] === 0x4e && buf[3] === 0x47)
|
|
235
|
+
return "image/png";
|
|
236
|
+
if (buf[0] === 0xff && buf[1] === 0xd8 && buf[2] === 0xff)
|
|
237
|
+
return "image/jpeg";
|
|
238
|
+
if (buf[0] === 0x47 && buf[1] === 0x49 && buf[2] === 0x46)
|
|
239
|
+
return "image/gif";
|
|
240
|
+
if (buf.length >= 12 &&
|
|
241
|
+
buf[0] === 0x52 &&
|
|
242
|
+
buf[1] === 0x49 &&
|
|
243
|
+
buf[2] === 0x46 &&
|
|
244
|
+
buf[3] === 0x46 &&
|
|
245
|
+
buf[8] === 0x57 &&
|
|
246
|
+
buf[9] === 0x45 &&
|
|
247
|
+
buf[10] === 0x42 &&
|
|
248
|
+
buf[11] === 0x50) {
|
|
249
|
+
return "image/webp";
|
|
250
|
+
}
|
|
251
|
+
return null;
|
|
252
|
+
}
|
|
253
|
+
function inferKindFromMime(mime) {
|
|
254
|
+
if (mime.startsWith("image/"))
|
|
255
|
+
return "image";
|
|
256
|
+
if (mime.startsWith("video/"))
|
|
257
|
+
return "video";
|
|
258
|
+
if (mime.startsWith("audio/"))
|
|
259
|
+
return "audio";
|
|
260
|
+
if (mime === "application/json")
|
|
261
|
+
return "json";
|
|
262
|
+
if (mime.startsWith("text/"))
|
|
263
|
+
return "text";
|
|
264
|
+
if (mime.startsWith("font/"))
|
|
265
|
+
return "font";
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
function localExecError(ctx, message) {
|
|
269
|
+
return new NodeExecutionError(ctx.nodeId, ctx.nodeType, {
|
|
270
|
+
kind: "local",
|
|
271
|
+
cause: new Error(`ingest: ${message}`),
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
async function execLocalFile(params, ctx) {
|
|
275
|
+
const absPath = resolveLocalPath(params.path);
|
|
276
|
+
let stats;
|
|
277
|
+
try {
|
|
278
|
+
stats = await stat(absPath);
|
|
279
|
+
}
|
|
280
|
+
catch (e) {
|
|
281
|
+
const code = e.code;
|
|
282
|
+
if (code === "ENOENT") {
|
|
283
|
+
throw localExecError(ctx, `file_not_found: ${absPath}`);
|
|
284
|
+
}
|
|
285
|
+
if (code === "EACCES") {
|
|
286
|
+
throw localExecError(ctx, `permission_denied: ${absPath}`);
|
|
287
|
+
}
|
|
288
|
+
throw localExecError(ctx, `stat failed for ${absPath}: ${e.message}`);
|
|
289
|
+
}
|
|
290
|
+
if (!stats.isFile()) {
|
|
291
|
+
throw localExecError(ctx, `not_a_file: ${absPath} (isDirectory=${stats.isDirectory()})`);
|
|
292
|
+
}
|
|
293
|
+
if (stats.size > MAX_ASSET_BYTES) {
|
|
294
|
+
throw localExecError(ctx, `file_too_large: ${absPath} is ${stats.size} bytes (limit ${MAX_ASSET_BYTES})`);
|
|
295
|
+
}
|
|
296
|
+
let bytes;
|
|
297
|
+
try {
|
|
298
|
+
bytes = await readFile(absPath);
|
|
299
|
+
}
|
|
300
|
+
catch (e) {
|
|
301
|
+
if (e.code === "EACCES") {
|
|
302
|
+
throw localExecError(ctx, `permission_denied: ${absPath}`);
|
|
303
|
+
}
|
|
304
|
+
throw localExecError(ctx, `read failed for ${absPath}: ${e.message}`);
|
|
305
|
+
}
|
|
306
|
+
const mime = inferMimeFromPath(absPath, bytes);
|
|
307
|
+
const kind = inferKindFromMime(mime);
|
|
308
|
+
if (kind === null) {
|
|
309
|
+
throw localExecError(ctx, `kind_mismatch: ${absPath} has mime=${mime} which doesn't map to any ingest kind (image/video/audio/text/json/font). If this is a PDF and you want text extraction, host it via URL and use source="url".`);
|
|
310
|
+
}
|
|
311
|
+
if (kind !== params.expect) {
|
|
312
|
+
throw localExecError(ctx, `kind_mismatch: expect=${params.expect} but ${absPath} has mime=${mime} (kind=${kind})`);
|
|
313
|
+
}
|
|
314
|
+
ctx.log(`ingest: local file ${stats.size}B mime=${mime}`);
|
|
315
|
+
// SVG resolves to the image kind, but downstream image-generation models can't read SVG markup, so
|
|
316
|
+
// rasterize to a transparent PNG before upload. The kind check above guarantees expect === "image".
|
|
317
|
+
let outBytes = bytes;
|
|
318
|
+
let outMime = mime;
|
|
319
|
+
if (mime === SVG_MIME) {
|
|
320
|
+
outBytes = await rasterizeSvgToPng(bytes);
|
|
321
|
+
outMime = "image/png";
|
|
322
|
+
ctx.log(`ingest: rasterized SVG -> PNG (${outBytes.length}B)`);
|
|
323
|
+
}
|
|
324
|
+
return await uploadAndIngest({
|
|
325
|
+
bytes: outBytes,
|
|
326
|
+
kind: params.expect,
|
|
327
|
+
mime: outMime,
|
|
328
|
+
metadata: {
|
|
329
|
+
source_path: absPath,
|
|
330
|
+
strategy: "local_file",
|
|
331
|
+
ingested_at: new Date().toISOString(),
|
|
332
|
+
file_size: stats.size,
|
|
333
|
+
original_filename: path.basename(absPath),
|
|
334
|
+
...(mime === SVG_MIME ? { rasterized_from: "svg" } : {}),
|
|
335
|
+
},
|
|
336
|
+
ctx,
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
// ---------------------------------------------------------------------------
|
|
340
|
+
// yt_dlp — runs locally, then uploads to R2 via presign so remote downstream
|
|
341
|
+
// nodes can fetch the asset by URL.
|
|
342
|
+
// ---------------------------------------------------------------------------
|
|
343
|
+
const YT_DLP_BIN = "yt-dlp";
|
|
344
|
+
const YT_DLP_TIMEOUT_MS = 10 * 60 * 1000;
|
|
345
|
+
const YT_DLP_MIME = {
|
|
346
|
+
image: "image/png",
|
|
347
|
+
video: "video/mp4",
|
|
348
|
+
audio: "audio/mp4",
|
|
349
|
+
text: "text/plain",
|
|
350
|
+
json: "application/json",
|
|
351
|
+
font: "font/ttf", // unreachable — resolveStrategy never routes font to yt_dlp
|
|
352
|
+
};
|
|
353
|
+
const YT_DLP_EXT = {
|
|
354
|
+
image: "png",
|
|
355
|
+
video: "mp4",
|
|
356
|
+
audio: "m4a",
|
|
357
|
+
text: "txt",
|
|
358
|
+
json: "json",
|
|
359
|
+
font: "ttf", // unreachable — resolveStrategy never routes font to yt_dlp
|
|
360
|
+
};
|
|
361
|
+
async function execYtDlp(params, ctx) {
|
|
362
|
+
if (params.expect !== "video" && params.expect !== "audio") {
|
|
363
|
+
// resolveStrategy guarantees this, but the runtime branch makes it explicit.
|
|
364
|
+
throw new Error(`ingest: yt_dlp only handles video/audio, got expect=${params.expect}`);
|
|
365
|
+
}
|
|
366
|
+
const workDir = await mkdtemp(path.join(tmpdir(), "ingest-yt-"));
|
|
367
|
+
try {
|
|
368
|
+
const { filePath, info } = await runYtDlp({
|
|
369
|
+
url: params.url,
|
|
370
|
+
audioOnly: params.expect === "audio",
|
|
371
|
+
workDir,
|
|
372
|
+
ctx,
|
|
373
|
+
});
|
|
374
|
+
const downloadedStats = await stat(filePath);
|
|
375
|
+
if (downloadedStats.size > MAX_ASSET_BYTES) {
|
|
376
|
+
throw localExecError(ctx, `file_too_large: yt-dlp output for ${params.url} is ${downloadedStats.size} bytes (limit ${MAX_ASSET_BYTES})`);
|
|
377
|
+
}
|
|
378
|
+
const bytes = await readFile(filePath);
|
|
379
|
+
const kind = params.expect;
|
|
380
|
+
const mime = YT_DLP_MIME[kind];
|
|
381
|
+
const metadata = buildYtDlpMetadata(params.url, info);
|
|
382
|
+
return await uploadAndIngest({ bytes, kind, mime, metadata, ctx });
|
|
383
|
+
}
|
|
384
|
+
finally {
|
|
385
|
+
await rm(workDir, { recursive: true, force: true }).catch((e) => {
|
|
386
|
+
ctx.log(`ingest: tmp cleanup failed (${e.message})`);
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
async function runYtDlp(args) {
|
|
391
|
+
const outTemplate = path.join(args.workDir, "out.%(ext)s");
|
|
392
|
+
const infoPath = path.join(args.workDir, "out.info.json");
|
|
393
|
+
const argv = [
|
|
394
|
+
args.url,
|
|
395
|
+
"--no-playlist",
|
|
396
|
+
"--no-progress",
|
|
397
|
+
"--no-warnings",
|
|
398
|
+
"--quiet",
|
|
399
|
+
"--write-info-json",
|
|
400
|
+
"-o",
|
|
401
|
+
outTemplate,
|
|
402
|
+
];
|
|
403
|
+
if (args.audioOnly) {
|
|
404
|
+
argv.push("-x", "--audio-format", "m4a");
|
|
405
|
+
}
|
|
406
|
+
else {
|
|
407
|
+
argv.push("--format", "bv*+ba/b", "--merge-output-format", "mp4");
|
|
408
|
+
}
|
|
409
|
+
args.ctx.log(`ingest: spawning yt-dlp (${args.audioOnly ? "audio" : "video"})`);
|
|
410
|
+
const { code, stderr } = await spawnAndWait(YT_DLP_BIN, argv, YT_DLP_TIMEOUT_MS);
|
|
411
|
+
if (code !== 0) {
|
|
412
|
+
throw new NodeExecutionError(args.ctx.nodeId, args.ctx.nodeType, {
|
|
413
|
+
kind: "local",
|
|
414
|
+
cause: new Error(`yt-dlp exited ${code}\n${tail(stderr, 40)}`),
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
const files = await readdir(args.workDir);
|
|
418
|
+
const wanted = args.audioOnly ? ".m4a" : ".mp4";
|
|
419
|
+
const downloaded = files.find((f) => f.startsWith("out.") && f.endsWith(wanted));
|
|
420
|
+
if (!downloaded) {
|
|
421
|
+
throw new Error(`ingest: yt-dlp finished but no ${wanted} file in ${args.workDir} (saw: ${files.join(", ")})`);
|
|
422
|
+
}
|
|
423
|
+
let info = {};
|
|
424
|
+
try {
|
|
425
|
+
const raw = await readFile(infoPath, "utf-8");
|
|
426
|
+
info = JSON.parse(raw);
|
|
427
|
+
}
|
|
428
|
+
catch {
|
|
429
|
+
// info.json is best-effort — yt-dlp sometimes omits it for direct file URLs.
|
|
430
|
+
}
|
|
431
|
+
return { filePath: path.join(args.workDir, downloaded), info };
|
|
432
|
+
}
|
|
433
|
+
function buildYtDlpMetadata(sourceUrl, info) {
|
|
434
|
+
const out = {
|
|
435
|
+
source_url: sourceUrl,
|
|
436
|
+
strategy: "yt_dlp",
|
|
437
|
+
ingested_at: new Date().toISOString(),
|
|
438
|
+
};
|
|
439
|
+
if (typeof info.title === "string")
|
|
440
|
+
out.original_title = info.title;
|
|
441
|
+
if (typeof info.uploader === "string")
|
|
442
|
+
out.original_uploader = info.uploader;
|
|
443
|
+
if (typeof info.duration === "number")
|
|
444
|
+
out.original_duration_s = info.duration;
|
|
445
|
+
if (typeof info.webpage_url === "string")
|
|
446
|
+
out.webpage_url = info.webpage_url;
|
|
447
|
+
if (typeof info.width === "number")
|
|
448
|
+
out.width = info.width;
|
|
449
|
+
if (typeof info.height === "number")
|
|
450
|
+
out.height = info.height;
|
|
451
|
+
return out;
|
|
452
|
+
}
|
|
453
|
+
// ---------------------------------------------------------------------------
|
|
454
|
+
// R2 upload + local ingest
|
|
455
|
+
// ---------------------------------------------------------------------------
|
|
456
|
+
export async function uploadAndIngest(args) {
|
|
457
|
+
const sha = sha256Hex(args.bytes);
|
|
458
|
+
const { putUrl, publicUrl } = await args.ctx.client.presignAssetUpload(sha, args.mime, args.ctx.signal);
|
|
459
|
+
const putRes = await fetch(putUrl, {
|
|
460
|
+
method: "PUT",
|
|
461
|
+
body: new Uint8Array(args.bytes),
|
|
462
|
+
headers: { "Content-Type": args.mime },
|
|
463
|
+
signal: args.ctx.signal,
|
|
464
|
+
});
|
|
465
|
+
if (!putRes.ok) {
|
|
466
|
+
throw new Error(`ingest: presigned PUT failed ${putRes.status} ${putRes.statusText}`);
|
|
467
|
+
}
|
|
468
|
+
const localRef = await args.ctx.assets.ingestBytes({
|
|
469
|
+
bytes: args.bytes,
|
|
470
|
+
kind: args.kind,
|
|
471
|
+
mime: args.mime,
|
|
472
|
+
metadata: args.metadata,
|
|
473
|
+
});
|
|
474
|
+
// ingestBytes leaves url undefined (LocalAssetStore is local-only). Overlay
|
|
475
|
+
// the public R2 URL so remote downstream nodes can pull this asset.
|
|
476
|
+
return { ...localRef, url: publicUrl };
|
|
477
|
+
}
|
|
478
|
+
function spawnAndWait(bin, argv, timeoutMs) {
|
|
479
|
+
return new Promise((resolve, reject) => {
|
|
480
|
+
const child = spawn(bin, argv, { stdio: ["ignore", "pipe", "pipe"] });
|
|
481
|
+
const stderrChunks = [];
|
|
482
|
+
let stderrLen = 0;
|
|
483
|
+
const MAX_STDERR = 256 * 1024;
|
|
484
|
+
child.stdout.on("data", () => {
|
|
485
|
+
// discard
|
|
486
|
+
});
|
|
487
|
+
child.stderr.on("data", (chunk) => {
|
|
488
|
+
if (stderrLen < MAX_STDERR) {
|
|
489
|
+
stderrChunks.push(chunk);
|
|
490
|
+
stderrLen += chunk.length;
|
|
491
|
+
}
|
|
492
|
+
});
|
|
493
|
+
const timer = setTimeout(() => {
|
|
494
|
+
child.kill("SIGKILL");
|
|
495
|
+
reject(new Error(`ingest: ${bin} timed out after ${timeoutMs}ms`));
|
|
496
|
+
}, timeoutMs);
|
|
497
|
+
child.on("error", (err) => {
|
|
498
|
+
clearTimeout(timer);
|
|
499
|
+
reject(new Error(`ingest: failed to spawn "${bin}" — ${err.message}`));
|
|
500
|
+
});
|
|
501
|
+
child.on("close", (code) => {
|
|
502
|
+
clearTimeout(timer);
|
|
503
|
+
resolve({ code: code ?? -1, stderr: Buffer.concat(stderrChunks).toString("utf8") });
|
|
504
|
+
});
|
|
505
|
+
});
|
|
506
|
+
}
|
|
507
|
+
function tail(text, maxLines) {
|
|
508
|
+
const lines = text.split("\n");
|
|
509
|
+
return lines.slice(-maxLines).join("\n");
|
|
510
|
+
}
|
|
511
|
+
// Fold yt-dlp version into the cache key so upgrades bust cache automatically.
|
|
512
|
+
async function ytDlpVersion() {
|
|
513
|
+
try {
|
|
514
|
+
const { stdout } = await execFile(YT_DLP_BIN, ["--version"], { encoding: "utf-8", maxBuffer: 64 * 1024 });
|
|
515
|
+
return stdout.trim();
|
|
516
|
+
}
|
|
517
|
+
catch {
|
|
518
|
+
return "yt-dlp:unknown";
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
// Attach cacheKeyExtras after definition so the helper can be referenced.
|
|
522
|
+
ingestNode.cacheKeyExtras = async ({ params }) => {
|
|
523
|
+
const p = params;
|
|
524
|
+
const extras = {};
|
|
525
|
+
// For source=path: fold mtime+size into the cache key so editing the file
|
|
526
|
+
// invalidates the cache automatically. Failures (missing file, perms) are
|
|
527
|
+
// silently swallowed — execute will surface them with full context.
|
|
528
|
+
if (p.source === "path" && typeof p.path === "string") {
|
|
529
|
+
try {
|
|
530
|
+
const abs = resolveLocalPath(p.path);
|
|
531
|
+
const s = await stat(abs);
|
|
532
|
+
extras.local_file_signature = `${s.mtimeMs}:${s.size}`;
|
|
533
|
+
}
|
|
534
|
+
catch {
|
|
535
|
+
// ignore — execute will fail clearly
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
// For source=url with yt_dlp strategy: fold tool version so upgrades bust cache.
|
|
539
|
+
if (p.source === "url" && (p.expect === "video" || p.expect === "audio")) {
|
|
540
|
+
extras.yt_dlp_version = await ytDlpVersion();
|
|
541
|
+
}
|
|
542
|
+
return extras;
|
|
543
|
+
};
|
|
544
|
+
// Exported for unit tests.
|
|
545
|
+
export const _internal = {
|
|
546
|
+
resolveStrategy,
|
|
547
|
+
resolveLocalPath,
|
|
548
|
+
inferMimeFromPath,
|
|
549
|
+
inferKindFromMime,
|
|
550
|
+
buildYtDlpMetadata,
|
|
551
|
+
YT_DLP_EXT,
|
|
552
|
+
YT_DLP_MIME,
|
|
553
|
+
EXT_TO_MIME,
|
|
554
|
+
};
|
|
555
|
+
//# sourceMappingURL=ingest.js.map
|