vargai 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/.claude/settings.local.json +7 -0
- package/.env.example +27 -0
- package/.github/workflows/ci.yml +23 -0
- package/.husky/README.md +102 -0
- package/.husky/commit-msg +6 -0
- package/.husky/pre-commit +9 -0
- package/.husky/pre-push +6 -0
- package/.size-limit.json +8 -0
- package/.test-hooks.ts +5 -0
- package/CLAUDE.md +125 -0
- package/CONTRIBUTING.md +150 -0
- package/LICENSE.md +53 -0
- package/README.md +78 -0
- package/SKILLS.md +173 -0
- package/STRUCTURE.md +92 -0
- package/biome.json +34 -0
- package/bun.lock +1254 -0
- package/commitlint.config.js +22 -0
- package/docs/plan.md +66 -0
- package/docs/todo.md +14 -0
- package/docs/varg-sdk.md +812 -0
- package/ffmpeg/CLAUDE.md +68 -0
- package/package.json +69 -0
- package/pipeline/cookbooks/SKILL.md +285 -0
- package/pipeline/cookbooks/remotion-video.md +585 -0
- package/pipeline/cookbooks/round-video-character.md +337 -0
- package/pipeline/cookbooks/scripts/animate-frames-parallel.ts +84 -0
- package/pipeline/cookbooks/scripts/combine-scenes.sh +53 -0
- package/pipeline/cookbooks/scripts/generate-frames-parallel.ts +99 -0
- package/pipeline/cookbooks/scripts/still-to-video.sh +37 -0
- package/pipeline/cookbooks/talking-character.md +59 -0
- package/pipeline/cookbooks/text-to-tiktok.md +669 -0
- package/pipeline/cookbooks/trendwatching.md +156 -0
- package/plan.md +281 -0
- package/scripts/.gitkeep +0 -0
- package/src/ai-sdk/cache.ts +142 -0
- package/src/ai-sdk/examples/cached-generation.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-4.ts +53 -0
- package/src/ai-sdk/examples/duet-scene-5-audio.ts +32 -0
- package/src/ai-sdk/examples/duet-video.ts +56 -0
- package/src/ai-sdk/examples/editly-composition.ts +63 -0
- package/src/ai-sdk/examples/editly-test.ts +57 -0
- package/src/ai-sdk/examples/editly-video-test.ts +52 -0
- package/src/ai-sdk/examples/fal-lipsync.ts +43 -0
- package/src/ai-sdk/examples/higgsfield-image.ts +61 -0
- package/src/ai-sdk/examples/music-generation.ts +19 -0
- package/src/ai-sdk/examples/openai-sora.ts +34 -0
- package/src/ai-sdk/examples/replicate-bg-removal.ts +52 -0
- package/src/ai-sdk/examples/simpsons-scene.ts +61 -0
- package/src/ai-sdk/examples/talking-lion.ts +55 -0
- package/src/ai-sdk/examples/video-generation.ts +39 -0
- package/src/ai-sdk/examples/workflow-animated-girl.ts +104 -0
- package/src/ai-sdk/examples/workflow-before-after.ts +114 -0
- package/src/ai-sdk/examples/workflow-character-grid.ts +112 -0
- package/src/ai-sdk/examples/workflow-slideshow.ts +161 -0
- package/src/ai-sdk/file-cache.ts +112 -0
- package/src/ai-sdk/file.ts +238 -0
- package/src/ai-sdk/generate-element.ts +92 -0
- package/src/ai-sdk/generate-music.ts +46 -0
- package/src/ai-sdk/generate-video.ts +165 -0
- package/src/ai-sdk/index.ts +72 -0
- package/src/ai-sdk/music-model.ts +110 -0
- package/src/ai-sdk/providers/editly/editly.test.ts +1108 -0
- package/src/ai-sdk/providers/editly/ffmpeg.ts +60 -0
- package/src/ai-sdk/providers/editly/index.ts +817 -0
- package/src/ai-sdk/providers/editly/layers.ts +772 -0
- package/src/ai-sdk/providers/editly/plan.md +144 -0
- package/src/ai-sdk/providers/editly/types.ts +328 -0
- package/src/ai-sdk/providers/elevenlabs-provider.ts +255 -0
- package/src/ai-sdk/providers/fal-provider.ts +512 -0
- package/src/ai-sdk/providers/higgsfield.ts +379 -0
- package/src/ai-sdk/providers/openai.ts +251 -0
- package/src/ai-sdk/providers/replicate.ts +16 -0
- package/src/ai-sdk/video-model.ts +185 -0
- package/src/cli/commands/find.tsx +137 -0
- package/src/cli/commands/help.tsx +85 -0
- package/src/cli/commands/index.ts +9 -0
- package/src/cli/commands/list.tsx +238 -0
- package/src/cli/commands/run.tsx +511 -0
- package/src/cli/commands/which.tsx +253 -0
- package/src/cli/index.ts +112 -0
- package/src/cli/quiet.ts +44 -0
- package/src/cli/types.ts +32 -0
- package/src/cli/ui/components/Badge.tsx +29 -0
- package/src/cli/ui/components/DataTable.tsx +51 -0
- package/src/cli/ui/components/Header.tsx +23 -0
- package/src/cli/ui/components/HelpBlock.tsx +44 -0
- package/src/cli/ui/components/KeyValue.tsx +33 -0
- package/src/cli/ui/components/OptionRow.tsx +81 -0
- package/src/cli/ui/components/Separator.tsx +23 -0
- package/src/cli/ui/components/StatusBox.tsx +108 -0
- package/src/cli/ui/components/VargBox.tsx +51 -0
- package/src/cli/ui/components/VargProgress.tsx +36 -0
- package/src/cli/ui/components/VargSpinner.tsx +34 -0
- package/src/cli/ui/components/VargText.tsx +56 -0
- package/src/cli/ui/components/index.ts +19 -0
- package/src/cli/ui/index.ts +12 -0
- package/src/cli/ui/render.ts +35 -0
- package/src/cli/ui/theme.ts +63 -0
- package/src/cli/utils.ts +78 -0
- package/src/core/executor/executor.ts +201 -0
- package/src/core/executor/index.ts +13 -0
- package/src/core/executor/job.ts +214 -0
- package/src/core/executor/pipeline.ts +222 -0
- package/src/core/index.ts +11 -0
- package/src/core/registry/index.ts +9 -0
- package/src/core/registry/loader.ts +149 -0
- package/src/core/registry/registry.ts +221 -0
- package/src/core/registry/resolver.ts +206 -0
- package/src/core/schema/helpers.ts +134 -0
- package/src/core/schema/index.ts +8 -0
- package/src/core/schema/shared.ts +102 -0
- package/src/core/schema/types.ts +279 -0
- package/src/core/schema/validator.ts +92 -0
- package/src/definitions/actions/captions.ts +261 -0
- package/src/definitions/actions/edit.ts +298 -0
- package/src/definitions/actions/image.ts +125 -0
- package/src/definitions/actions/index.ts +114 -0
- package/src/definitions/actions/music.ts +205 -0
- package/src/definitions/actions/sync.ts +128 -0
- package/src/definitions/actions/transcribe.ts +200 -0
- package/src/definitions/actions/upload.ts +111 -0
- package/src/definitions/actions/video.ts +163 -0
- package/src/definitions/actions/voice.ts +119 -0
- package/src/definitions/index.ts +23 -0
- package/src/definitions/models/elevenlabs.ts +50 -0
- package/src/definitions/models/flux.ts +56 -0
- package/src/definitions/models/index.ts +36 -0
- package/src/definitions/models/kling.ts +56 -0
- package/src/definitions/models/llama.ts +54 -0
- package/src/definitions/models/nano-banana-pro.ts +102 -0
- package/src/definitions/models/sonauto.ts +68 -0
- package/src/definitions/models/soul.ts +65 -0
- package/src/definitions/models/wan.ts +54 -0
- package/src/definitions/models/whisper.ts +44 -0
- package/src/definitions/skills/index.ts +12 -0
- package/src/definitions/skills/talking-character.ts +87 -0
- package/src/definitions/skills/text-to-tiktok.ts +97 -0
- package/src/index.ts +118 -0
- package/src/providers/apify.ts +269 -0
- package/src/providers/base.ts +264 -0
- package/src/providers/elevenlabs.ts +217 -0
- package/src/providers/fal.ts +392 -0
- package/src/providers/ffmpeg.ts +544 -0
- package/src/providers/fireworks.ts +193 -0
- package/src/providers/groq.ts +149 -0
- package/src/providers/higgsfield.ts +145 -0
- package/src/providers/index.ts +143 -0
- package/src/providers/replicate.ts +147 -0
- package/src/providers/storage.ts +206 -0
- package/src/tests/all.test.ts +509 -0
- package/src/tests/index.ts +33 -0
- package/src/tests/unit.test.ts +403 -0
- package/tsconfig.json +45 -0
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Replicate provider for video and image generation
|
|
3
|
+
* Supports Minimax, Kling, Luma, Flux, and other models
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import Replicate from "replicate";
|
|
7
|
+
import type { JobStatusUpdate, ProviderConfig } from "../core/schema/types";
|
|
8
|
+
import { BaseProvider } from "./base";
|
|
9
|
+
|
|
10
|
+
export class ReplicateProvider extends BaseProvider {
|
|
11
|
+
readonly name = "replicate";
|
|
12
|
+
private client: Replicate;
|
|
13
|
+
|
|
14
|
+
constructor(config?: ProviderConfig) {
|
|
15
|
+
super(config);
|
|
16
|
+
this.client = new Replicate({
|
|
17
|
+
auth: config?.apiKey || process.env.REPLICATE_API_TOKEN || "",
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async submit(
|
|
22
|
+
model: string,
|
|
23
|
+
inputs: Record<string, unknown>,
|
|
24
|
+
_config?: ProviderConfig,
|
|
25
|
+
): Promise<string> {
|
|
26
|
+
// Transform inputs for provider-specific field names
|
|
27
|
+
const transformedInputs = this.transformInputs(model, inputs);
|
|
28
|
+
|
|
29
|
+
const prediction = await this.client.predictions.create({
|
|
30
|
+
model: model as `${string}/${string}`,
|
|
31
|
+
input: transformedInputs,
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
console.log(`[replicate] job submitted: ${prediction.id}`);
|
|
35
|
+
return prediction.id;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Transform inputs for provider-specific field names
|
|
40
|
+
*/
|
|
41
|
+
private transformInputs(
|
|
42
|
+
model: string,
|
|
43
|
+
inputs: Record<string, unknown>,
|
|
44
|
+
): Record<string, unknown> {
|
|
45
|
+
// Nano Banana Pro: Replicate uses 'image_input' instead of 'image_urls'
|
|
46
|
+
if (model === "google/nano-banana-pro" && inputs.image_urls) {
|
|
47
|
+
const { image_urls, ...rest } = inputs;
|
|
48
|
+
return {
|
|
49
|
+
...rest,
|
|
50
|
+
image_input: image_urls,
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
return inputs;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
async getStatus(jobId: string): Promise<JobStatusUpdate> {
|
|
57
|
+
const prediction = await this.client.predictions.get(jobId);
|
|
58
|
+
|
|
59
|
+
const statusMap: Record<string, JobStatusUpdate["status"]> = {
|
|
60
|
+
starting: "queued",
|
|
61
|
+
processing: "processing",
|
|
62
|
+
succeeded: "completed",
|
|
63
|
+
failed: "failed",
|
|
64
|
+
canceled: "cancelled",
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
return {
|
|
68
|
+
status: statusMap[prediction.status] ?? "processing",
|
|
69
|
+
output: prediction.output,
|
|
70
|
+
error:
|
|
71
|
+
typeof prediction.error === "string" ? prediction.error : undefined,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async getResult(jobId: string): Promise<unknown> {
|
|
76
|
+
const prediction = await this.client.predictions.get(jobId);
|
|
77
|
+
return prediction.output;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
override async cancel(jobId: string): Promise<void> {
|
|
81
|
+
await this.client.predictions.cancel(jobId);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// ============================================================================
|
|
85
|
+
// High-level convenience methods
|
|
86
|
+
// ============================================================================
|
|
87
|
+
|
|
88
|
+
async runModel(model: string, input: Record<string, unknown>) {
|
|
89
|
+
console.log(`[replicate] running ${model}...`);
|
|
90
|
+
|
|
91
|
+
const output = await this.client.run(model as `${string}/${string}`, {
|
|
92
|
+
input,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
console.log(`[replicate] completed`);
|
|
96
|
+
return output;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
async runVideo(options: { model: string; input: Record<string, unknown> }) {
|
|
100
|
+
return this.runModel(options.model, options.input);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
async runImage(options: { model: string; input: Record<string, unknown> }) {
|
|
104
|
+
return this.runModel(options.model, options.input);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async listPredictions() {
|
|
108
|
+
return this.client.predictions.list();
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
async getPrediction(id: string) {
|
|
112
|
+
return this.client.predictions.get(id);
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
// Popular models registry
|
|
117
|
+
export const MODELS = {
|
|
118
|
+
VIDEO: {
|
|
119
|
+
MINIMAX: "minimax/video-01",
|
|
120
|
+
KLING: "fofr/kling-v1.5",
|
|
121
|
+
LUMA: "fofr/ltx-video",
|
|
122
|
+
RUNWAY_GEN3: "replicate/runway-gen3-turbo",
|
|
123
|
+
WAN_2_5: "wan-video/wan-2.5-i2v",
|
|
124
|
+
},
|
|
125
|
+
IMAGE: {
|
|
126
|
+
FLUX_PRO: "black-forest-labs/flux-1.1-pro",
|
|
127
|
+
FLUX_DEV: "black-forest-labs/flux-dev",
|
|
128
|
+
FLUX_SCHNELL: "black-forest-labs/flux-schnell",
|
|
129
|
+
STABLE_DIFFUSION: "stability-ai/sdxl",
|
|
130
|
+
NANO_BANANA_PRO: "google/nano-banana-pro",
|
|
131
|
+
},
|
|
132
|
+
};
|
|
133
|
+
|
|
134
|
+
// Export singleton instance
|
|
135
|
+
export const replicateProvider = new ReplicateProvider();
|
|
136
|
+
|
|
137
|
+
// Re-export convenience functions for backward compatibility
|
|
138
|
+
export const runModel = (model: string, input: Record<string, unknown>) =>
|
|
139
|
+
replicateProvider.runModel(model, input);
|
|
140
|
+
export const runVideo = (options: {
|
|
141
|
+
model: string;
|
|
142
|
+
input: Record<string, unknown>;
|
|
143
|
+
}) => replicateProvider.runVideo(options);
|
|
144
|
+
export const runImage = (options: {
|
|
145
|
+
model: string;
|
|
146
|
+
input: Record<string, unknown>;
|
|
147
|
+
}) => replicateProvider.runImage(options);
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Storage provider for Cloudflare R2 / S3 compatible storage
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
GetObjectCommand,
|
|
7
|
+
PutObjectCommand,
|
|
8
|
+
S3Client,
|
|
9
|
+
} from "@aws-sdk/client-s3";
|
|
10
|
+
import { getSignedUrl } from "@aws-sdk/s3-request-presigner";
|
|
11
|
+
import type { JobStatusUpdate, ProviderConfig } from "../core/schema/types";
|
|
12
|
+
import { BaseProvider } from "./base";
|
|
13
|
+
|
|
14
|
+
export interface StorageConfig extends ProviderConfig {
|
|
15
|
+
endpoint?: string;
|
|
16
|
+
accessKeyId?: string;
|
|
17
|
+
secretAccessKey?: string;
|
|
18
|
+
bucket?: string;
|
|
19
|
+
publicUrl?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export class StorageProvider extends BaseProvider {
|
|
23
|
+
readonly name = "storage";
|
|
24
|
+
private client: S3Client;
|
|
25
|
+
private bucket: string;
|
|
26
|
+
private publicUrl: string;
|
|
27
|
+
|
|
28
|
+
constructor(config?: StorageConfig) {
|
|
29
|
+
super(config);
|
|
30
|
+
|
|
31
|
+
this.client = new S3Client({
|
|
32
|
+
region: "auto",
|
|
33
|
+
endpoint: config?.endpoint || process.env.CLOUDFLARE_R2_API_URL,
|
|
34
|
+
credentials: {
|
|
35
|
+
accessKeyId:
|
|
36
|
+
config?.accessKeyId || process.env.CLOUDFLARE_ACCESS_KEY_ID || "",
|
|
37
|
+
secretAccessKey:
|
|
38
|
+
config?.secretAccessKey || process.env.CLOUDFLARE_ACCESS_SECRET || "",
|
|
39
|
+
},
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
this.bucket = config?.bucket || process.env.CLOUDFLARE_R2_BUCKET || "m";
|
|
43
|
+
this.publicUrl = config?.publicUrl || "https://s3.varg.ai";
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async submit(
|
|
47
|
+
_model: string,
|
|
48
|
+
_inputs: Record<string, unknown>,
|
|
49
|
+
_config?: ProviderConfig,
|
|
50
|
+
): Promise<string> {
|
|
51
|
+
const jobId = `storage_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
52
|
+
return jobId;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async getStatus(_jobId: string): Promise<JobStatusUpdate> {
|
|
56
|
+
return { status: "completed" };
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async getResult(_jobId: string): Promise<unknown> {
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// ============================================================================
|
|
64
|
+
// Storage Operations
|
|
65
|
+
// ============================================================================
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Upload a file from local path to storage
|
|
69
|
+
*/
|
|
70
|
+
async uploadLocalFile(filePath: string, objectKey: string): Promise<string> {
|
|
71
|
+
console.log(`[storage] uploading ${filePath} to ${objectKey}`);
|
|
72
|
+
|
|
73
|
+
const file = Bun.file(filePath);
|
|
74
|
+
const buffer = await file.arrayBuffer();
|
|
75
|
+
|
|
76
|
+
await this.client.send(
|
|
77
|
+
new PutObjectCommand({
|
|
78
|
+
Bucket: this.bucket,
|
|
79
|
+
Key: objectKey,
|
|
80
|
+
Body: new Uint8Array(buffer),
|
|
81
|
+
}),
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
return this.getPublicUrl(objectKey);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* Upload content from a URL to storage
|
|
89
|
+
*/
|
|
90
|
+
async uploadFromUrl(url: string, objectKey: string): Promise<string> {
|
|
91
|
+
console.log(`[storage] uploading from URL to ${objectKey}`);
|
|
92
|
+
|
|
93
|
+
const response = await fetch(url);
|
|
94
|
+
const buffer = await response.arrayBuffer();
|
|
95
|
+
|
|
96
|
+
await this.client.send(
|
|
97
|
+
new PutObjectCommand({
|
|
98
|
+
Bucket: this.bucket,
|
|
99
|
+
Key: objectKey,
|
|
100
|
+
Body: new Uint8Array(buffer),
|
|
101
|
+
}),
|
|
102
|
+
);
|
|
103
|
+
|
|
104
|
+
return this.getPublicUrl(objectKey);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Upload raw buffer to storage
|
|
109
|
+
*/
|
|
110
|
+
async uploadBuffer(
|
|
111
|
+
buffer: ArrayBuffer | Uint8Array,
|
|
112
|
+
objectKey: string,
|
|
113
|
+
contentType?: string,
|
|
114
|
+
): Promise<string> {
|
|
115
|
+
console.log(`[storage] uploading buffer to ${objectKey}`);
|
|
116
|
+
|
|
117
|
+
await this.client.send(
|
|
118
|
+
new PutObjectCommand({
|
|
119
|
+
Bucket: this.bucket,
|
|
120
|
+
Key: objectKey,
|
|
121
|
+
Body: buffer instanceof Uint8Array ? buffer : new Uint8Array(buffer),
|
|
122
|
+
ContentType: contentType,
|
|
123
|
+
}),
|
|
124
|
+
);
|
|
125
|
+
|
|
126
|
+
return this.getPublicUrl(objectKey);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Generate a presigned URL for temporary access
|
|
131
|
+
*/
|
|
132
|
+
async generatePresignedUrl(
|
|
133
|
+
objectKey: string,
|
|
134
|
+
expiresIn = 3600,
|
|
135
|
+
): Promise<string> {
|
|
136
|
+
const command = new GetObjectCommand({
|
|
137
|
+
Bucket: this.bucket,
|
|
138
|
+
Key: objectKey,
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
return getSignedUrl(this.client, command, { expiresIn });
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get the public URL for an object
|
|
146
|
+
*/
|
|
147
|
+
getPublicUrl(objectKey: string): string {
|
|
148
|
+
const endpoint = process.env.CLOUDFLARE_R2_API_URL || "";
|
|
149
|
+
|
|
150
|
+
if (endpoint.includes("localhost")) {
|
|
151
|
+
return `${endpoint}/${objectKey}`;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return `${this.publicUrl}/${objectKey}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Download a file from storage
|
|
159
|
+
*/
|
|
160
|
+
async downloadToFile(objectKey: string, outputPath: string): Promise<string> {
|
|
161
|
+
console.log(`[storage] downloading ${objectKey} to ${outputPath}`);
|
|
162
|
+
|
|
163
|
+
const command = new GetObjectCommand({
|
|
164
|
+
Bucket: this.bucket,
|
|
165
|
+
Key: objectKey,
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
const response = await this.client.send(command);
|
|
169
|
+
const body = response.Body;
|
|
170
|
+
|
|
171
|
+
if (!body) {
|
|
172
|
+
throw new Error("Empty response body");
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Convert stream to buffer
|
|
176
|
+
const chunks: Uint8Array[] = [];
|
|
177
|
+
// @ts-expect-error - body is a readable stream
|
|
178
|
+
for await (const chunk of body) {
|
|
179
|
+
chunks.push(chunk);
|
|
180
|
+
}
|
|
181
|
+
const buffer = Buffer.concat(chunks);
|
|
182
|
+
|
|
183
|
+
await Bun.write(outputPath, buffer);
|
|
184
|
+
console.log(`[storage] saved to ${outputPath}`);
|
|
185
|
+
|
|
186
|
+
return outputPath;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
// Export singleton instance
|
|
191
|
+
export const storageProvider = new StorageProvider();
|
|
192
|
+
|
|
193
|
+
// Re-export convenience functions for backward compatibility
|
|
194
|
+
export const uploadFile = (filePath: string, objectKey: string) =>
|
|
195
|
+
storageProvider.uploadLocalFile(filePath, objectKey);
|
|
196
|
+
export const uploadFromUrl = (url: string, objectKey: string) =>
|
|
197
|
+
storageProvider.uploadFromUrl(url, objectKey);
|
|
198
|
+
export const uploadBuffer = (
|
|
199
|
+
buffer: ArrayBuffer | Uint8Array,
|
|
200
|
+
objectKey: string,
|
|
201
|
+
contentType?: string,
|
|
202
|
+
) => storageProvider.uploadBuffer(buffer, objectKey, contentType);
|
|
203
|
+
export const generatePresignedUrl = (objectKey: string, expiresIn?: number) =>
|
|
204
|
+
storageProvider.generatePresignedUrl(objectKey, expiresIn);
|
|
205
|
+
export const getPublicUrl = (objectKey: string) =>
|
|
206
|
+
storageProvider.getPublicUrl(objectKey);
|