@adforge-adgeniq/providers 0.1.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.
@@ -0,0 +1,193 @@
1
+ interface AvatarProvider {
2
+ generate(params: {
3
+ avatarId: string;
4
+ audioUrl?: string;
5
+ text?: string;
6
+ aspect: "9:16" | "16:9" | "1:1";
7
+ }): Promise<{
8
+ jobId: string;
9
+ }>;
10
+ getStatus(jobId: string): Promise<{
11
+ status: "running" | "succeeded" | "failed";
12
+ resultUrl?: string;
13
+ error?: string;
14
+ }>;
15
+ }
16
+ interface VoiceProvider {
17
+ synthesize(params: {
18
+ voiceId: string;
19
+ text: string;
20
+ language: string;
21
+ }): Promise<{
22
+ audioUrl: string;
23
+ durationMs: number;
24
+ }>;
25
+ }
26
+ interface CloneProvider {
27
+ createAvatar(params: {
28
+ videoUrl: string;
29
+ workspaceId: string;
30
+ name: string;
31
+ }): Promise<{
32
+ providerAvatarId: string;
33
+ }>;
34
+ createVoice(params: {
35
+ audioUrl: string;
36
+ workspaceId: string;
37
+ name: string;
38
+ }): Promise<{
39
+ providerVoiceId: string;
40
+ }>;
41
+ }
42
+ interface LLMProvider {
43
+ complete<T>(params: {
44
+ prompt: string;
45
+ schema?: unknown;
46
+ maxTokens?: number;
47
+ }): Promise<{
48
+ content: T;
49
+ tokensUsed: number;
50
+ }>;
51
+ }
52
+ declare class FalAvatarProvider implements AvatarProvider {
53
+ private apiKey;
54
+ private baseUrl;
55
+ constructor(apiKey?: string);
56
+ generate(params: {
57
+ avatarId: string;
58
+ audioUrl?: string;
59
+ text?: string;
60
+ aspect: "9:16" | "16:9" | "1:1";
61
+ }): Promise<{
62
+ jobId: string;
63
+ }>;
64
+ getStatus(jobId: string): Promise<{
65
+ status: "running" | "succeeded" | "failed";
66
+ resultUrl?: string;
67
+ error?: string;
68
+ }>;
69
+ }
70
+ declare class ElevenLabsVoiceProvider implements VoiceProvider {
71
+ private apiKey;
72
+ private baseUrl;
73
+ constructor(apiKey?: string);
74
+ synthesize(params: {
75
+ voiceId: string;
76
+ text: string;
77
+ language: string;
78
+ }): Promise<{
79
+ audioUrl: string;
80
+ durationMs: number;
81
+ }>;
82
+ }
83
+ declare class HeyGenAvatarProvider implements AvatarProvider {
84
+ private apiKey;
85
+ private baseUrl;
86
+ constructor(apiKey?: string);
87
+ generate(params: {
88
+ avatarId: string;
89
+ audioUrl?: string;
90
+ text?: string;
91
+ aspect: "9:16" | "16:9" | "1:1";
92
+ }): Promise<{
93
+ jobId: string;
94
+ }>;
95
+ getStatus(jobId: string): Promise<{
96
+ status: "running" | "succeeded" | "failed";
97
+ resultUrl?: string;
98
+ error?: string;
99
+ }>;
100
+ }
101
+ declare class HeyGenCloneProvider implements CloneProvider {
102
+ private apiKey;
103
+ private baseUrl;
104
+ constructor(apiKey?: string);
105
+ createAvatar(params: {
106
+ videoUrl: string;
107
+ workspaceId: string;
108
+ name: string;
109
+ }): Promise<{
110
+ providerAvatarId: string;
111
+ }>;
112
+ createVoice(params: {
113
+ audioUrl: string;
114
+ workspaceId: string;
115
+ name: string;
116
+ }): Promise<{
117
+ providerVoiceId: string;
118
+ }>;
119
+ }
120
+ declare class ElevenLabsCloneProvider implements CloneProvider {
121
+ private apiKey;
122
+ private baseUrl;
123
+ constructor(apiKey?: string);
124
+ createAvatar(): Promise<{
125
+ providerAvatarId: string;
126
+ }>;
127
+ createVoice(params: {
128
+ audioUrl: string;
129
+ workspaceId: string;
130
+ name: string;
131
+ }): Promise<{
132
+ providerVoiceId: string;
133
+ }>;
134
+ }
135
+ type AvatarProviderName = "fal" | "heygen" | "stub";
136
+ type VoiceProviderName = "elevenlabs" | "stub";
137
+ type CloneProviderName = "heygen" | "elevenlabs";
138
+ interface ProviderKeys {
139
+ FAL_API_KEY?: string;
140
+ HEYGEN_API_KEY?: string;
141
+ ELEVENLABS_API_KEY?: string;
142
+ }
143
+ /** Get the configured avatar provider, falling back gracefully.
144
+ * Pass `keys` to override environment variables (e.g. from DB-stored settings). */
145
+ declare function getAvatarProvider(preferred?: AvatarProviderName, keys?: ProviderKeys): AvatarProvider;
146
+ declare function getVoiceProvider(preferred?: VoiceProviderName, keys?: ProviderKeys): VoiceProvider;
147
+ declare function getCloneProvider(preferred?: CloneProviderName, keys?: ProviderKeys): CloneProvider;
148
+ declare class StubAvatarProvider implements AvatarProvider {
149
+ generate(): Promise<{
150
+ jobId: string;
151
+ }>;
152
+ getStatus(_jobId: string): Promise<{
153
+ status: "succeeded";
154
+ resultUrl: string;
155
+ }>;
156
+ }
157
+ declare class StubVoiceProvider implements VoiceProvider {
158
+ synthesize(_params: {
159
+ voiceId: string;
160
+ text: string;
161
+ language: string;
162
+ }): Promise<{
163
+ audioUrl: string;
164
+ durationMs: number;
165
+ }>;
166
+ }
167
+ interface ProviderHealth {
168
+ /** Which avatar provider will be selected at runtime */
169
+ avatarProvider: AvatarProviderName;
170
+ /** Which voice provider will be selected at runtime */
171
+ voiceProvider: VoiceProviderName;
172
+ /** True when at least one real (non-stub) avatar provider is available */
173
+ avatarReady: boolean;
174
+ /** True when at least one real (non-stub) voice provider is available */
175
+ voiceReady: boolean;
176
+ /** Presence status for each API key (value is never the actual key string) */
177
+ keys: {
178
+ FAL_API_KEY: boolean;
179
+ HEYGEN_API_KEY: boolean;
180
+ ELEVENLABS_API_KEY: boolean;
181
+ };
182
+ }
183
+ /**
184
+ * Returns which providers are configured based on environment variable presence
185
+ * (or an explicit `keys` override — pass resolved DB values from the async wrapper).
186
+ * Does NOT instantiate any provider — safe to call from server components or API routes.
187
+ */
188
+ declare function getProviderHealth(keys?: ProviderKeys & {
189
+ ANTHROPIC_API_KEY?: string;
190
+ }): ProviderHealth;
191
+ declare function withRetry<T>(fn: () => Promise<T>, maxAttempts?: number, baseDelayMs?: number): Promise<T>;
192
+
193
+ export { type AvatarProvider, type AvatarProviderName, type CloneProvider, type CloneProviderName, ElevenLabsCloneProvider, ElevenLabsVoiceProvider, FalAvatarProvider, HeyGenAvatarProvider, HeyGenCloneProvider, type LLMProvider, type ProviderHealth, type ProviderKeys, StubAvatarProvider, StubVoiceProvider, type VoiceProvider, type VoiceProviderName, getAvatarProvider, getCloneProvider, getProviderHealth, getVoiceProvider, withRetry };
@@ -0,0 +1,193 @@
1
+ interface AvatarProvider {
2
+ generate(params: {
3
+ avatarId: string;
4
+ audioUrl?: string;
5
+ text?: string;
6
+ aspect: "9:16" | "16:9" | "1:1";
7
+ }): Promise<{
8
+ jobId: string;
9
+ }>;
10
+ getStatus(jobId: string): Promise<{
11
+ status: "running" | "succeeded" | "failed";
12
+ resultUrl?: string;
13
+ error?: string;
14
+ }>;
15
+ }
16
+ interface VoiceProvider {
17
+ synthesize(params: {
18
+ voiceId: string;
19
+ text: string;
20
+ language: string;
21
+ }): Promise<{
22
+ audioUrl: string;
23
+ durationMs: number;
24
+ }>;
25
+ }
26
+ interface CloneProvider {
27
+ createAvatar(params: {
28
+ videoUrl: string;
29
+ workspaceId: string;
30
+ name: string;
31
+ }): Promise<{
32
+ providerAvatarId: string;
33
+ }>;
34
+ createVoice(params: {
35
+ audioUrl: string;
36
+ workspaceId: string;
37
+ name: string;
38
+ }): Promise<{
39
+ providerVoiceId: string;
40
+ }>;
41
+ }
42
+ interface LLMProvider {
43
+ complete<T>(params: {
44
+ prompt: string;
45
+ schema?: unknown;
46
+ maxTokens?: number;
47
+ }): Promise<{
48
+ content: T;
49
+ tokensUsed: number;
50
+ }>;
51
+ }
52
+ declare class FalAvatarProvider implements AvatarProvider {
53
+ private apiKey;
54
+ private baseUrl;
55
+ constructor(apiKey?: string);
56
+ generate(params: {
57
+ avatarId: string;
58
+ audioUrl?: string;
59
+ text?: string;
60
+ aspect: "9:16" | "16:9" | "1:1";
61
+ }): Promise<{
62
+ jobId: string;
63
+ }>;
64
+ getStatus(jobId: string): Promise<{
65
+ status: "running" | "succeeded" | "failed";
66
+ resultUrl?: string;
67
+ error?: string;
68
+ }>;
69
+ }
70
+ declare class ElevenLabsVoiceProvider implements VoiceProvider {
71
+ private apiKey;
72
+ private baseUrl;
73
+ constructor(apiKey?: string);
74
+ synthesize(params: {
75
+ voiceId: string;
76
+ text: string;
77
+ language: string;
78
+ }): Promise<{
79
+ audioUrl: string;
80
+ durationMs: number;
81
+ }>;
82
+ }
83
+ declare class HeyGenAvatarProvider implements AvatarProvider {
84
+ private apiKey;
85
+ private baseUrl;
86
+ constructor(apiKey?: string);
87
+ generate(params: {
88
+ avatarId: string;
89
+ audioUrl?: string;
90
+ text?: string;
91
+ aspect: "9:16" | "16:9" | "1:1";
92
+ }): Promise<{
93
+ jobId: string;
94
+ }>;
95
+ getStatus(jobId: string): Promise<{
96
+ status: "running" | "succeeded" | "failed";
97
+ resultUrl?: string;
98
+ error?: string;
99
+ }>;
100
+ }
101
+ declare class HeyGenCloneProvider implements CloneProvider {
102
+ private apiKey;
103
+ private baseUrl;
104
+ constructor(apiKey?: string);
105
+ createAvatar(params: {
106
+ videoUrl: string;
107
+ workspaceId: string;
108
+ name: string;
109
+ }): Promise<{
110
+ providerAvatarId: string;
111
+ }>;
112
+ createVoice(params: {
113
+ audioUrl: string;
114
+ workspaceId: string;
115
+ name: string;
116
+ }): Promise<{
117
+ providerVoiceId: string;
118
+ }>;
119
+ }
120
+ declare class ElevenLabsCloneProvider implements CloneProvider {
121
+ private apiKey;
122
+ private baseUrl;
123
+ constructor(apiKey?: string);
124
+ createAvatar(): Promise<{
125
+ providerAvatarId: string;
126
+ }>;
127
+ createVoice(params: {
128
+ audioUrl: string;
129
+ workspaceId: string;
130
+ name: string;
131
+ }): Promise<{
132
+ providerVoiceId: string;
133
+ }>;
134
+ }
135
+ type AvatarProviderName = "fal" | "heygen" | "stub";
136
+ type VoiceProviderName = "elevenlabs" | "stub";
137
+ type CloneProviderName = "heygen" | "elevenlabs";
138
+ interface ProviderKeys {
139
+ FAL_API_KEY?: string;
140
+ HEYGEN_API_KEY?: string;
141
+ ELEVENLABS_API_KEY?: string;
142
+ }
143
+ /** Get the configured avatar provider, falling back gracefully.
144
+ * Pass `keys` to override environment variables (e.g. from DB-stored settings). */
145
+ declare function getAvatarProvider(preferred?: AvatarProviderName, keys?: ProviderKeys): AvatarProvider;
146
+ declare function getVoiceProvider(preferred?: VoiceProviderName, keys?: ProviderKeys): VoiceProvider;
147
+ declare function getCloneProvider(preferred?: CloneProviderName, keys?: ProviderKeys): CloneProvider;
148
+ declare class StubAvatarProvider implements AvatarProvider {
149
+ generate(): Promise<{
150
+ jobId: string;
151
+ }>;
152
+ getStatus(_jobId: string): Promise<{
153
+ status: "succeeded";
154
+ resultUrl: string;
155
+ }>;
156
+ }
157
+ declare class StubVoiceProvider implements VoiceProvider {
158
+ synthesize(_params: {
159
+ voiceId: string;
160
+ text: string;
161
+ language: string;
162
+ }): Promise<{
163
+ audioUrl: string;
164
+ durationMs: number;
165
+ }>;
166
+ }
167
+ interface ProviderHealth {
168
+ /** Which avatar provider will be selected at runtime */
169
+ avatarProvider: AvatarProviderName;
170
+ /** Which voice provider will be selected at runtime */
171
+ voiceProvider: VoiceProviderName;
172
+ /** True when at least one real (non-stub) avatar provider is available */
173
+ avatarReady: boolean;
174
+ /** True when at least one real (non-stub) voice provider is available */
175
+ voiceReady: boolean;
176
+ /** Presence status for each API key (value is never the actual key string) */
177
+ keys: {
178
+ FAL_API_KEY: boolean;
179
+ HEYGEN_API_KEY: boolean;
180
+ ELEVENLABS_API_KEY: boolean;
181
+ };
182
+ }
183
+ /**
184
+ * Returns which providers are configured based on environment variable presence
185
+ * (or an explicit `keys` override — pass resolved DB values from the async wrapper).
186
+ * Does NOT instantiate any provider — safe to call from server components or API routes.
187
+ */
188
+ declare function getProviderHealth(keys?: ProviderKeys & {
189
+ ANTHROPIC_API_KEY?: string;
190
+ }): ProviderHealth;
191
+ declare function withRetry<T>(fn: () => Promise<T>, maxAttempts?: number, baseDelayMs?: number): Promise<T>;
192
+
193
+ export { type AvatarProvider, type AvatarProviderName, type CloneProvider, type CloneProviderName, ElevenLabsCloneProvider, ElevenLabsVoiceProvider, FalAvatarProvider, HeyGenAvatarProvider, HeyGenCloneProvider, type LLMProvider, type ProviderHealth, type ProviderKeys, StubAvatarProvider, StubVoiceProvider, type VoiceProvider, type VoiceProviderName, getAvatarProvider, getCloneProvider, getProviderHealth, getVoiceProvider, withRetry };
package/dist/index.js ADDED
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ ElevenLabsCloneProvider: () => ElevenLabsCloneProvider,
24
+ ElevenLabsVoiceProvider: () => ElevenLabsVoiceProvider,
25
+ FalAvatarProvider: () => FalAvatarProvider,
26
+ HeyGenAvatarProvider: () => HeyGenAvatarProvider,
27
+ HeyGenCloneProvider: () => HeyGenCloneProvider,
28
+ StubAvatarProvider: () => StubAvatarProvider,
29
+ StubVoiceProvider: () => StubVoiceProvider,
30
+ getAvatarProvider: () => getAvatarProvider,
31
+ getCloneProvider: () => getCloneProvider,
32
+ getProviderHealth: () => getProviderHealth,
33
+ getVoiceProvider: () => getVoiceProvider,
34
+ withRetry: () => withRetry
35
+ });
36
+ module.exports = __toCommonJS(index_exports);
37
+ var FalAvatarProvider = class {
38
+ apiKey;
39
+ baseUrl = "https://fal.run";
40
+ constructor(apiKey) {
41
+ const key = apiKey ?? process.env.FAL_API_KEY;
42
+ if (!key) throw new Error("FAL_API_KEY is not set");
43
+ this.apiKey = key;
44
+ }
45
+ async generate(params) {
46
+ const aspectMap = { "9:16": "portrait", "16:9": "landscape", "1:1": "square" };
47
+ const res = await fetch(`${this.baseUrl}/fal-ai/talking-head`, {
48
+ method: "POST",
49
+ headers: {
50
+ Authorization: `Key ${this.apiKey}`,
51
+ "Content-Type": "application/json"
52
+ },
53
+ body: JSON.stringify({
54
+ avatar_id: params.avatarId,
55
+ ...params.audioUrl ? { audio_url: params.audioUrl } : { text: params.text },
56
+ aspect_ratio: aspectMap[params.aspect]
57
+ }),
58
+ signal: AbortSignal.timeout(3e4)
59
+ });
60
+ if (!res.ok) throw new Error(`fal.ai generate failed: ${res.status} ${await res.text()}`);
61
+ const data = await res.json();
62
+ const jobId = data.request_id ?? data.id;
63
+ if (!jobId) throw new Error("fal.ai returned no job ID");
64
+ return { jobId };
65
+ }
66
+ async getStatus(jobId) {
67
+ const res = await fetch(`${this.baseUrl}/fal-ai/talking-head/requests/${jobId}/status`, {
68
+ headers: { Authorization: `Key ${this.apiKey}` },
69
+ signal: AbortSignal.timeout(15e3)
70
+ });
71
+ if (!res.ok) throw new Error(`fal.ai status check failed: ${res.status}`);
72
+ const data = await res.json();
73
+ if (data.status === "COMPLETED" || data.status === "succeeded") {
74
+ return { status: "succeeded", resultUrl: data.output?.video?.url };
75
+ }
76
+ if (data.status === "FAILED" || data.status === "failed") {
77
+ return { status: "failed", error: data.error ?? "Unknown fal.ai error" };
78
+ }
79
+ return { status: "running" };
80
+ }
81
+ };
82
+ var ElevenLabsVoiceProvider = class {
83
+ apiKey;
84
+ baseUrl = "https://api.elevenlabs.io/v1";
85
+ constructor(apiKey) {
86
+ const key = apiKey ?? process.env.ELEVENLABS_API_KEY;
87
+ if (!key) throw new Error("ELEVENLABS_API_KEY is not set");
88
+ this.apiKey = key;
89
+ }
90
+ async synthesize(params) {
91
+ const res = await fetch(`${this.baseUrl}/text-to-speech/${params.voiceId}`, {
92
+ method: "POST",
93
+ headers: {
94
+ "xi-api-key": this.apiKey,
95
+ "Content-Type": "application/json",
96
+ Accept: "audio/mpeg"
97
+ },
98
+ body: JSON.stringify({
99
+ text: params.text,
100
+ model_id: "eleven_multilingual_v2",
101
+ voice_settings: { stability: 0.5, similarity_boost: 0.75 }
102
+ }),
103
+ signal: AbortSignal.timeout(6e4)
104
+ });
105
+ if (!res.ok) throw new Error(`ElevenLabs TTS failed: ${res.status} ${await res.text()}`);
106
+ const buffer = Buffer.from(await res.arrayBuffer());
107
+ const durationMs = Math.round(buffer.byteLength / 16e3 * 1e3);
108
+ const audioUrl = `data:audio/mpeg;base64,${buffer.toString("base64")}`;
109
+ return { audioUrl, durationMs };
110
+ }
111
+ };
112
+ var HeyGenAvatarProvider = class {
113
+ apiKey;
114
+ baseUrl = "https://api.heygen.com";
115
+ constructor(apiKey) {
116
+ const key = apiKey ?? process.env.HEYGEN_API_KEY;
117
+ if (!key) throw new Error("HEYGEN_API_KEY is not set");
118
+ this.apiKey = key;
119
+ }
120
+ async generate(params) {
121
+ const dimensionMap = {
122
+ "9:16": { width: 1080, height: 1920 },
123
+ "16:9": { width: 1920, height: 1080 },
124
+ "1:1": { width: 1080, height: 1080 }
125
+ };
126
+ const dim = dimensionMap[params.aspect];
127
+ const res = await fetch(`${this.baseUrl}/v2/video/generate`, {
128
+ method: "POST",
129
+ headers: {
130
+ "X-Api-Key": this.apiKey,
131
+ "Content-Type": "application/json"
132
+ },
133
+ body: JSON.stringify({
134
+ video_inputs: [{
135
+ character: { type: "avatar", avatar_id: params.avatarId, avatar_style: "normal" },
136
+ voice: params.audioUrl ? { type: "audio", audio_url: params.audioUrl } : { type: "text", input_text: params.text, voice_id: "en-US-SaraNeural" }
137
+ }],
138
+ dimension: dim
139
+ }),
140
+ signal: AbortSignal.timeout(3e4)
141
+ });
142
+ if (!res.ok) throw new Error(`HeyGen generate failed: ${res.status} ${await res.text()}`);
143
+ const data = await res.json();
144
+ if (data.error) throw new Error(`HeyGen error: ${data.error}`);
145
+ return { jobId: data.data.video_id };
146
+ }
147
+ async getStatus(jobId) {
148
+ const res = await fetch(`${this.baseUrl}/v1/video_status.get?video_id=${jobId}`, {
149
+ headers: { "X-Api-Key": this.apiKey },
150
+ signal: AbortSignal.timeout(15e3)
151
+ });
152
+ if (!res.ok) throw new Error(`HeyGen status failed: ${res.status}`);
153
+ const data = await res.json();
154
+ const s = data.data?.status;
155
+ if (s === "completed") return { status: "succeeded", resultUrl: data.data?.video_url };
156
+ if (s === "failed") return { status: "failed", error: data.data?.error };
157
+ return { status: "running" };
158
+ }
159
+ };
160
+ var HeyGenCloneProvider = class {
161
+ apiKey;
162
+ baseUrl = "https://api.heygen.com";
163
+ constructor(apiKey) {
164
+ const key = apiKey ?? process.env.HEYGEN_API_KEY;
165
+ if (!key) throw new Error("HEYGEN_API_KEY is not set");
166
+ this.apiKey = key;
167
+ }
168
+ async createAvatar(params) {
169
+ const res = await fetch(`${this.baseUrl}/v2/avatar/create`, {
170
+ method: "POST",
171
+ headers: { "X-Api-Key": this.apiKey, "Content-Type": "application/json" },
172
+ body: JSON.stringify({ name: params.name, video_url: params.videoUrl }),
173
+ signal: AbortSignal.timeout(3e4)
174
+ });
175
+ if (!res.ok) throw new Error(`HeyGen createAvatar failed: ${res.status} ${await res.text()}`);
176
+ const data = await res.json();
177
+ return { providerAvatarId: data.data.avatar_id };
178
+ }
179
+ async createVoice(params) {
180
+ const res = await fetch(`${this.baseUrl}/v2/voice/create`, {
181
+ method: "POST",
182
+ headers: { "X-Api-Key": this.apiKey, "Content-Type": "application/json" },
183
+ body: JSON.stringify({ name: params.name, audio_url: params.audioUrl }),
184
+ signal: AbortSignal.timeout(3e4)
185
+ });
186
+ if (!res.ok) throw new Error(`HeyGen createVoice failed: ${res.status} ${await res.text()}`);
187
+ const data = await res.json();
188
+ return { providerVoiceId: data.data.voice_id };
189
+ }
190
+ };
191
+ var ElevenLabsCloneProvider = class {
192
+ apiKey;
193
+ baseUrl = "https://api.elevenlabs.io/v1";
194
+ constructor(apiKey) {
195
+ const key = apiKey ?? process.env.ELEVENLABS_API_KEY;
196
+ if (!key) throw new Error("ELEVENLABS_API_KEY is not set");
197
+ this.apiKey = key;
198
+ }
199
+ async createAvatar() {
200
+ throw new Error("ElevenLabs does not support avatar cloning. Use HeyGen.");
201
+ }
202
+ async createVoice(params) {
203
+ const audioRes = await fetch(params.audioUrl, { signal: AbortSignal.timeout(6e4) });
204
+ if (!audioRes.ok) {
205
+ throw new Error(`Failed to download audio for voice cloning: ${audioRes.status}`);
206
+ }
207
+ const audioBuffer = await audioRes.arrayBuffer();
208
+ const contentType = audioRes.headers.get("content-type") ?? "audio/mpeg";
209
+ const audioBlob = new Blob([audioBuffer], { type: contentType });
210
+ const form = new FormData();
211
+ form.append("name", params.name);
212
+ form.append("files", audioBlob, "voice-sample.mp3");
213
+ form.append("description", `Cloned voice for workspace ${params.workspaceId}`);
214
+ const res = await fetch(`${this.baseUrl}/voices/add`, {
215
+ method: "POST",
216
+ headers: { "xi-api-key": this.apiKey },
217
+ body: form,
218
+ signal: AbortSignal.timeout(12e4)
219
+ });
220
+ if (!res.ok) throw new Error(`ElevenLabs createVoice failed: ${res.status} ${await res.text()}`);
221
+ const data = await res.json();
222
+ return { providerVoiceId: data.voice_id };
223
+ }
224
+ };
225
+ function getAvatarProvider(preferred = "fal", keys) {
226
+ const fal = keys?.FAL_API_KEY ?? process.env.FAL_API_KEY;
227
+ const heygen = keys?.HEYGEN_API_KEY ?? process.env.HEYGEN_API_KEY;
228
+ if (preferred === "fal" && fal) return new FalAvatarProvider(fal);
229
+ if (heygen) return new HeyGenAvatarProvider(heygen);
230
+ return new StubAvatarProvider();
231
+ }
232
+ function getVoiceProvider(preferred = "elevenlabs", keys) {
233
+ const el = keys?.ELEVENLABS_API_KEY ?? process.env.ELEVENLABS_API_KEY;
234
+ if (preferred === "elevenlabs" && el) return new ElevenLabsVoiceProvider(el);
235
+ return new StubVoiceProvider();
236
+ }
237
+ function getCloneProvider(preferred = "heygen", keys) {
238
+ const heygen = keys?.HEYGEN_API_KEY ?? process.env.HEYGEN_API_KEY;
239
+ const el = keys?.ELEVENLABS_API_KEY ?? process.env.ELEVENLABS_API_KEY;
240
+ if (preferred === "heygen" && heygen) return new HeyGenCloneProvider(heygen);
241
+ if (el) return new ElevenLabsCloneProvider(el);
242
+ throw new Error("No clone provider configured. Set HEYGEN_API_KEY or ELEVENLABS_API_KEY.");
243
+ }
244
+ var StubAvatarProvider = class {
245
+ async generate() {
246
+ return { jobId: `stub-avatar-${Date.now()}` };
247
+ }
248
+ async getStatus(_jobId) {
249
+ return { status: "succeeded", resultUrl: "https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" };
250
+ }
251
+ };
252
+ var StubVoiceProvider = class {
253
+ async synthesize(_params) {
254
+ return { audioUrl: "https://www.soundjay.com/misc/sounds/bell-ringing-05.mp3", durationMs: 15e3 };
255
+ }
256
+ };
257
+ function getProviderHealth(keys) {
258
+ const hasFal = Boolean(keys?.FAL_API_KEY ?? process.env.FAL_API_KEY);
259
+ const hasHeygen = Boolean(keys?.HEYGEN_API_KEY ?? process.env.HEYGEN_API_KEY);
260
+ const hasElevenLabs = Boolean(keys?.ELEVENLABS_API_KEY ?? process.env.ELEVENLABS_API_KEY);
261
+ const avatarProvider = hasFal ? "fal" : hasHeygen ? "heygen" : "stub";
262
+ const voiceProvider = hasElevenLabs ? "elevenlabs" : "stub";
263
+ return {
264
+ avatarProvider,
265
+ voiceProvider,
266
+ avatarReady: avatarProvider !== "stub",
267
+ voiceReady: voiceProvider !== "stub",
268
+ keys: {
269
+ FAL_API_KEY: hasFal,
270
+ HEYGEN_API_KEY: hasHeygen,
271
+ ELEVENLABS_API_KEY: hasElevenLabs
272
+ }
273
+ };
274
+ }
275
+ async function withRetry(fn, maxAttempts = 3, baseDelayMs = 1e3) {
276
+ let lastError = new Error("Unknown error");
277
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
278
+ try {
279
+ return await fn();
280
+ } catch (err) {
281
+ lastError = err instanceof Error ? err : new Error(String(err));
282
+ if (attempt < maxAttempts) {
283
+ const delay = baseDelayMs * Math.pow(2, attempt - 1);
284
+ await new Promise((r) => setTimeout(r, delay));
285
+ }
286
+ }
287
+ }
288
+ throw lastError;
289
+ }
290
+ // Annotate the CommonJS export names for ESM import in node:
291
+ 0 && (module.exports = {
292
+ ElevenLabsCloneProvider,
293
+ ElevenLabsVoiceProvider,
294
+ FalAvatarProvider,
295
+ HeyGenAvatarProvider,
296
+ HeyGenCloneProvider,
297
+ StubAvatarProvider,
298
+ StubVoiceProvider,
299
+ getAvatarProvider,
300
+ getCloneProvider,
301
+ getProviderHealth,
302
+ getVoiceProvider,
303
+ withRetry
304
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,268 @@
1
+ // src/index.ts
2
+ var FalAvatarProvider = class {
3
+ apiKey;
4
+ baseUrl = "https://fal.run";
5
+ constructor(apiKey) {
6
+ const key = apiKey ?? process.env.FAL_API_KEY;
7
+ if (!key) throw new Error("FAL_API_KEY is not set");
8
+ this.apiKey = key;
9
+ }
10
+ async generate(params) {
11
+ const aspectMap = { "9:16": "portrait", "16:9": "landscape", "1:1": "square" };
12
+ const res = await fetch(`${this.baseUrl}/fal-ai/talking-head`, {
13
+ method: "POST",
14
+ headers: {
15
+ Authorization: `Key ${this.apiKey}`,
16
+ "Content-Type": "application/json"
17
+ },
18
+ body: JSON.stringify({
19
+ avatar_id: params.avatarId,
20
+ ...params.audioUrl ? { audio_url: params.audioUrl } : { text: params.text },
21
+ aspect_ratio: aspectMap[params.aspect]
22
+ }),
23
+ signal: AbortSignal.timeout(3e4)
24
+ });
25
+ if (!res.ok) throw new Error(`fal.ai generate failed: ${res.status} ${await res.text()}`);
26
+ const data = await res.json();
27
+ const jobId = data.request_id ?? data.id;
28
+ if (!jobId) throw new Error("fal.ai returned no job ID");
29
+ return { jobId };
30
+ }
31
+ async getStatus(jobId) {
32
+ const res = await fetch(`${this.baseUrl}/fal-ai/talking-head/requests/${jobId}/status`, {
33
+ headers: { Authorization: `Key ${this.apiKey}` },
34
+ signal: AbortSignal.timeout(15e3)
35
+ });
36
+ if (!res.ok) throw new Error(`fal.ai status check failed: ${res.status}`);
37
+ const data = await res.json();
38
+ if (data.status === "COMPLETED" || data.status === "succeeded") {
39
+ return { status: "succeeded", resultUrl: data.output?.video?.url };
40
+ }
41
+ if (data.status === "FAILED" || data.status === "failed") {
42
+ return { status: "failed", error: data.error ?? "Unknown fal.ai error" };
43
+ }
44
+ return { status: "running" };
45
+ }
46
+ };
47
+ var ElevenLabsVoiceProvider = class {
48
+ apiKey;
49
+ baseUrl = "https://api.elevenlabs.io/v1";
50
+ constructor(apiKey) {
51
+ const key = apiKey ?? process.env.ELEVENLABS_API_KEY;
52
+ if (!key) throw new Error("ELEVENLABS_API_KEY is not set");
53
+ this.apiKey = key;
54
+ }
55
+ async synthesize(params) {
56
+ const res = await fetch(`${this.baseUrl}/text-to-speech/${params.voiceId}`, {
57
+ method: "POST",
58
+ headers: {
59
+ "xi-api-key": this.apiKey,
60
+ "Content-Type": "application/json",
61
+ Accept: "audio/mpeg"
62
+ },
63
+ body: JSON.stringify({
64
+ text: params.text,
65
+ model_id: "eleven_multilingual_v2",
66
+ voice_settings: { stability: 0.5, similarity_boost: 0.75 }
67
+ }),
68
+ signal: AbortSignal.timeout(6e4)
69
+ });
70
+ if (!res.ok) throw new Error(`ElevenLabs TTS failed: ${res.status} ${await res.text()}`);
71
+ const buffer = Buffer.from(await res.arrayBuffer());
72
+ const durationMs = Math.round(buffer.byteLength / 16e3 * 1e3);
73
+ const audioUrl = `data:audio/mpeg;base64,${buffer.toString("base64")}`;
74
+ return { audioUrl, durationMs };
75
+ }
76
+ };
77
+ var HeyGenAvatarProvider = class {
78
+ apiKey;
79
+ baseUrl = "https://api.heygen.com";
80
+ constructor(apiKey) {
81
+ const key = apiKey ?? process.env.HEYGEN_API_KEY;
82
+ if (!key) throw new Error("HEYGEN_API_KEY is not set");
83
+ this.apiKey = key;
84
+ }
85
+ async generate(params) {
86
+ const dimensionMap = {
87
+ "9:16": { width: 1080, height: 1920 },
88
+ "16:9": { width: 1920, height: 1080 },
89
+ "1:1": { width: 1080, height: 1080 }
90
+ };
91
+ const dim = dimensionMap[params.aspect];
92
+ const res = await fetch(`${this.baseUrl}/v2/video/generate`, {
93
+ method: "POST",
94
+ headers: {
95
+ "X-Api-Key": this.apiKey,
96
+ "Content-Type": "application/json"
97
+ },
98
+ body: JSON.stringify({
99
+ video_inputs: [{
100
+ character: { type: "avatar", avatar_id: params.avatarId, avatar_style: "normal" },
101
+ voice: params.audioUrl ? { type: "audio", audio_url: params.audioUrl } : { type: "text", input_text: params.text, voice_id: "en-US-SaraNeural" }
102
+ }],
103
+ dimension: dim
104
+ }),
105
+ signal: AbortSignal.timeout(3e4)
106
+ });
107
+ if (!res.ok) throw new Error(`HeyGen generate failed: ${res.status} ${await res.text()}`);
108
+ const data = await res.json();
109
+ if (data.error) throw new Error(`HeyGen error: ${data.error}`);
110
+ return { jobId: data.data.video_id };
111
+ }
112
+ async getStatus(jobId) {
113
+ const res = await fetch(`${this.baseUrl}/v1/video_status.get?video_id=${jobId}`, {
114
+ headers: { "X-Api-Key": this.apiKey },
115
+ signal: AbortSignal.timeout(15e3)
116
+ });
117
+ if (!res.ok) throw new Error(`HeyGen status failed: ${res.status}`);
118
+ const data = await res.json();
119
+ const s = data.data?.status;
120
+ if (s === "completed") return { status: "succeeded", resultUrl: data.data?.video_url };
121
+ if (s === "failed") return { status: "failed", error: data.data?.error };
122
+ return { status: "running" };
123
+ }
124
+ };
125
+ var HeyGenCloneProvider = class {
126
+ apiKey;
127
+ baseUrl = "https://api.heygen.com";
128
+ constructor(apiKey) {
129
+ const key = apiKey ?? process.env.HEYGEN_API_KEY;
130
+ if (!key) throw new Error("HEYGEN_API_KEY is not set");
131
+ this.apiKey = key;
132
+ }
133
+ async createAvatar(params) {
134
+ const res = await fetch(`${this.baseUrl}/v2/avatar/create`, {
135
+ method: "POST",
136
+ headers: { "X-Api-Key": this.apiKey, "Content-Type": "application/json" },
137
+ body: JSON.stringify({ name: params.name, video_url: params.videoUrl }),
138
+ signal: AbortSignal.timeout(3e4)
139
+ });
140
+ if (!res.ok) throw new Error(`HeyGen createAvatar failed: ${res.status} ${await res.text()}`);
141
+ const data = await res.json();
142
+ return { providerAvatarId: data.data.avatar_id };
143
+ }
144
+ async createVoice(params) {
145
+ const res = await fetch(`${this.baseUrl}/v2/voice/create`, {
146
+ method: "POST",
147
+ headers: { "X-Api-Key": this.apiKey, "Content-Type": "application/json" },
148
+ body: JSON.stringify({ name: params.name, audio_url: params.audioUrl }),
149
+ signal: AbortSignal.timeout(3e4)
150
+ });
151
+ if (!res.ok) throw new Error(`HeyGen createVoice failed: ${res.status} ${await res.text()}`);
152
+ const data = await res.json();
153
+ return { providerVoiceId: data.data.voice_id };
154
+ }
155
+ };
156
+ var ElevenLabsCloneProvider = class {
157
+ apiKey;
158
+ baseUrl = "https://api.elevenlabs.io/v1";
159
+ constructor(apiKey) {
160
+ const key = apiKey ?? process.env.ELEVENLABS_API_KEY;
161
+ if (!key) throw new Error("ELEVENLABS_API_KEY is not set");
162
+ this.apiKey = key;
163
+ }
164
+ async createAvatar() {
165
+ throw new Error("ElevenLabs does not support avatar cloning. Use HeyGen.");
166
+ }
167
+ async createVoice(params) {
168
+ const audioRes = await fetch(params.audioUrl, { signal: AbortSignal.timeout(6e4) });
169
+ if (!audioRes.ok) {
170
+ throw new Error(`Failed to download audio for voice cloning: ${audioRes.status}`);
171
+ }
172
+ const audioBuffer = await audioRes.arrayBuffer();
173
+ const contentType = audioRes.headers.get("content-type") ?? "audio/mpeg";
174
+ const audioBlob = new Blob([audioBuffer], { type: contentType });
175
+ const form = new FormData();
176
+ form.append("name", params.name);
177
+ form.append("files", audioBlob, "voice-sample.mp3");
178
+ form.append("description", `Cloned voice for workspace ${params.workspaceId}`);
179
+ const res = await fetch(`${this.baseUrl}/voices/add`, {
180
+ method: "POST",
181
+ headers: { "xi-api-key": this.apiKey },
182
+ body: form,
183
+ signal: AbortSignal.timeout(12e4)
184
+ });
185
+ if (!res.ok) throw new Error(`ElevenLabs createVoice failed: ${res.status} ${await res.text()}`);
186
+ const data = await res.json();
187
+ return { providerVoiceId: data.voice_id };
188
+ }
189
+ };
190
+ function getAvatarProvider(preferred = "fal", keys) {
191
+ const fal = keys?.FAL_API_KEY ?? process.env.FAL_API_KEY;
192
+ const heygen = keys?.HEYGEN_API_KEY ?? process.env.HEYGEN_API_KEY;
193
+ if (preferred === "fal" && fal) return new FalAvatarProvider(fal);
194
+ if (heygen) return new HeyGenAvatarProvider(heygen);
195
+ return new StubAvatarProvider();
196
+ }
197
+ function getVoiceProvider(preferred = "elevenlabs", keys) {
198
+ const el = keys?.ELEVENLABS_API_KEY ?? process.env.ELEVENLABS_API_KEY;
199
+ if (preferred === "elevenlabs" && el) return new ElevenLabsVoiceProvider(el);
200
+ return new StubVoiceProvider();
201
+ }
202
+ function getCloneProvider(preferred = "heygen", keys) {
203
+ const heygen = keys?.HEYGEN_API_KEY ?? process.env.HEYGEN_API_KEY;
204
+ const el = keys?.ELEVENLABS_API_KEY ?? process.env.ELEVENLABS_API_KEY;
205
+ if (preferred === "heygen" && heygen) return new HeyGenCloneProvider(heygen);
206
+ if (el) return new ElevenLabsCloneProvider(el);
207
+ throw new Error("No clone provider configured. Set HEYGEN_API_KEY or ELEVENLABS_API_KEY.");
208
+ }
209
+ var StubAvatarProvider = class {
210
+ async generate() {
211
+ return { jobId: `stub-avatar-${Date.now()}` };
212
+ }
213
+ async getStatus(_jobId) {
214
+ return { status: "succeeded", resultUrl: "https://storage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4" };
215
+ }
216
+ };
217
+ var StubVoiceProvider = class {
218
+ async synthesize(_params) {
219
+ return { audioUrl: "https://www.soundjay.com/misc/sounds/bell-ringing-05.mp3", durationMs: 15e3 };
220
+ }
221
+ };
222
+ function getProviderHealth(keys) {
223
+ const hasFal = Boolean(keys?.FAL_API_KEY ?? process.env.FAL_API_KEY);
224
+ const hasHeygen = Boolean(keys?.HEYGEN_API_KEY ?? process.env.HEYGEN_API_KEY);
225
+ const hasElevenLabs = Boolean(keys?.ELEVENLABS_API_KEY ?? process.env.ELEVENLABS_API_KEY);
226
+ const avatarProvider = hasFal ? "fal" : hasHeygen ? "heygen" : "stub";
227
+ const voiceProvider = hasElevenLabs ? "elevenlabs" : "stub";
228
+ return {
229
+ avatarProvider,
230
+ voiceProvider,
231
+ avatarReady: avatarProvider !== "stub",
232
+ voiceReady: voiceProvider !== "stub",
233
+ keys: {
234
+ FAL_API_KEY: hasFal,
235
+ HEYGEN_API_KEY: hasHeygen,
236
+ ELEVENLABS_API_KEY: hasElevenLabs
237
+ }
238
+ };
239
+ }
240
+ async function withRetry(fn, maxAttempts = 3, baseDelayMs = 1e3) {
241
+ let lastError = new Error("Unknown error");
242
+ for (let attempt = 1; attempt <= maxAttempts; attempt++) {
243
+ try {
244
+ return await fn();
245
+ } catch (err) {
246
+ lastError = err instanceof Error ? err : new Error(String(err));
247
+ if (attempt < maxAttempts) {
248
+ const delay = baseDelayMs * Math.pow(2, attempt - 1);
249
+ await new Promise((r) => setTimeout(r, delay));
250
+ }
251
+ }
252
+ }
253
+ throw lastError;
254
+ }
255
+ export {
256
+ ElevenLabsCloneProvider,
257
+ ElevenLabsVoiceProvider,
258
+ FalAvatarProvider,
259
+ HeyGenAvatarProvider,
260
+ HeyGenCloneProvider,
261
+ StubAvatarProvider,
262
+ StubVoiceProvider,
263
+ getAvatarProvider,
264
+ getCloneProvider,
265
+ getProviderHealth,
266
+ getVoiceProvider,
267
+ withRetry
268
+ };
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@adforge-adgeniq/providers",
3
+ "version": "0.1.0",
4
+ "description": "Provider adapters for avatar (fal.ai, HeyGen), voice (ElevenLabs), and clone operations",
5
+ "license": "MIT",
6
+ "main": "./dist/index.js",
7
+ "module": "./dist/index.mjs",
8
+ "types": "./dist/index.d.ts",
9
+ "files": [
10
+ "dist"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.js",
16
+ "types": "./dist/index.d.ts"
17
+ }
18
+ },
19
+ "publishConfig": {
20
+ "access": "public"
21
+ },
22
+ "dependencies": {
23
+ "zod": "^3.24.1"
24
+ },
25
+ "devDependencies": {
26
+ "@types/node": "^20.0.0",
27
+ "tsup": "^8.4.0",
28
+ "typescript": "^5.7.2",
29
+ "vitest": "^4.1.10"
30
+ },
31
+ "scripts": {
32
+ "build": "tsup",
33
+ "test": "vitest run"
34
+ }
35
+ }