whale-code 6.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +95 -0
- package/bin/swag-agent.js +9 -0
- package/bin/swagmanager-mcp.js +321 -0
- package/dist/cli/app.d.ts +26 -0
- package/dist/cli/app.js +64 -0
- package/dist/cli/chat/AgentSelector.d.ts +14 -0
- package/dist/cli/chat/AgentSelector.js +14 -0
- package/dist/cli/chat/ChatApp.d.ts +9 -0
- package/dist/cli/chat/ChatApp.js +267 -0
- package/dist/cli/chat/ChatInput.d.ts +39 -0
- package/dist/cli/chat/ChatInput.js +509 -0
- package/dist/cli/chat/MarkdownText.d.ts +10 -0
- package/dist/cli/chat/MarkdownText.js +20 -0
- package/dist/cli/chat/MessageList.d.ts +37 -0
- package/dist/cli/chat/MessageList.js +80 -0
- package/dist/cli/chat/ModelSelector.d.ts +20 -0
- package/dist/cli/chat/ModelSelector.js +73 -0
- package/dist/cli/chat/RewindViewer.d.ts +26 -0
- package/dist/cli/chat/RewindViewer.js +185 -0
- package/dist/cli/chat/StoreSelector.d.ts +14 -0
- package/dist/cli/chat/StoreSelector.js +24 -0
- package/dist/cli/chat/StreamingText.d.ts +12 -0
- package/dist/cli/chat/StreamingText.js +12 -0
- package/dist/cli/chat/SubagentPanel.d.ts +45 -0
- package/dist/cli/chat/SubagentPanel.js +110 -0
- package/dist/cli/chat/TeamPanel.d.ts +21 -0
- package/dist/cli/chat/TeamPanel.js +42 -0
- package/dist/cli/chat/ToolIndicator.d.ts +25 -0
- package/dist/cli/chat/ToolIndicator.js +436 -0
- package/dist/cli/chat/hooks/useAgentLoop.d.ts +39 -0
- package/dist/cli/chat/hooks/useAgentLoop.js +382 -0
- package/dist/cli/chat/hooks/useSlashCommands.d.ts +37 -0
- package/dist/cli/chat/hooks/useSlashCommands.js +387 -0
- package/dist/cli/commands/config-cmd.d.ts +10 -0
- package/dist/cli/commands/config-cmd.js +99 -0
- package/dist/cli/commands/doctor.d.ts +14 -0
- package/dist/cli/commands/doctor.js +172 -0
- package/dist/cli/commands/init.d.ts +16 -0
- package/dist/cli/commands/init.js +278 -0
- package/dist/cli/commands/mcp.d.ts +12 -0
- package/dist/cli/commands/mcp.js +162 -0
- package/dist/cli/login/LoginApp.d.ts +7 -0
- package/dist/cli/login/LoginApp.js +157 -0
- package/dist/cli/print-mode.d.ts +31 -0
- package/dist/cli/print-mode.js +202 -0
- package/dist/cli/serve-mode.d.ts +37 -0
- package/dist/cli/serve-mode.js +636 -0
- package/dist/cli/services/agent-definitions.d.ts +25 -0
- package/dist/cli/services/agent-definitions.js +91 -0
- package/dist/cli/services/agent-events.d.ts +178 -0
- package/dist/cli/services/agent-events.js +175 -0
- package/dist/cli/services/agent-loop.d.ts +90 -0
- package/dist/cli/services/agent-loop.js +762 -0
- package/dist/cli/services/agent-worker-base.d.ts +97 -0
- package/dist/cli/services/agent-worker-base.js +220 -0
- package/dist/cli/services/auth-service.d.ts +30 -0
- package/dist/cli/services/auth-service.js +160 -0
- package/dist/cli/services/background-processes.d.ts +126 -0
- package/dist/cli/services/background-processes.js +318 -0
- package/dist/cli/services/browser-auth.d.ts +24 -0
- package/dist/cli/services/browser-auth.js +180 -0
- package/dist/cli/services/claude-md-loader.d.ts +16 -0
- package/dist/cli/services/claude-md-loader.js +58 -0
- package/dist/cli/services/config-store.d.ts +47 -0
- package/dist/cli/services/config-store.js +79 -0
- package/dist/cli/services/debug-log.d.ts +10 -0
- package/dist/cli/services/debug-log.js +52 -0
- package/dist/cli/services/error-logger.d.ts +58 -0
- package/dist/cli/services/error-logger.js +269 -0
- package/dist/cli/services/file-history.d.ts +21 -0
- package/dist/cli/services/file-history.js +83 -0
- package/dist/cli/services/format-server-response.d.ts +16 -0
- package/dist/cli/services/format-server-response.js +440 -0
- package/dist/cli/services/git-context.d.ts +11 -0
- package/dist/cli/services/git-context.js +66 -0
- package/dist/cli/services/hooks.d.ts +85 -0
- package/dist/cli/services/hooks.js +258 -0
- package/dist/cli/services/interactive-tools.d.ts +125 -0
- package/dist/cli/services/interactive-tools.js +260 -0
- package/dist/cli/services/keybinding-manager.d.ts +52 -0
- package/dist/cli/services/keybinding-manager.js +115 -0
- package/dist/cli/services/local-tools.d.ts +22 -0
- package/dist/cli/services/local-tools.js +697 -0
- package/dist/cli/services/lsp-manager.d.ts +18 -0
- package/dist/cli/services/lsp-manager.js +717 -0
- package/dist/cli/services/mcp-client.d.ts +48 -0
- package/dist/cli/services/mcp-client.js +157 -0
- package/dist/cli/services/memory-manager.d.ts +16 -0
- package/dist/cli/services/memory-manager.js +57 -0
- package/dist/cli/services/model-manager.d.ts +18 -0
- package/dist/cli/services/model-manager.js +71 -0
- package/dist/cli/services/model-router.d.ts +26 -0
- package/dist/cli/services/model-router.js +149 -0
- package/dist/cli/services/permission-modes.d.ts +13 -0
- package/dist/cli/services/permission-modes.js +43 -0
- package/dist/cli/services/rewind.d.ts +84 -0
- package/dist/cli/services/rewind.js +194 -0
- package/dist/cli/services/ripgrep.d.ts +28 -0
- package/dist/cli/services/ripgrep.js +138 -0
- package/dist/cli/services/sandbox.d.ts +29 -0
- package/dist/cli/services/sandbox.js +97 -0
- package/dist/cli/services/server-tools.d.ts +61 -0
- package/dist/cli/services/server-tools.js +543 -0
- package/dist/cli/services/session-persistence.d.ts +23 -0
- package/dist/cli/services/session-persistence.js +99 -0
- package/dist/cli/services/subagent-worker.d.ts +19 -0
- package/dist/cli/services/subagent-worker.js +41 -0
- package/dist/cli/services/subagent.d.ts +47 -0
- package/dist/cli/services/subagent.js +647 -0
- package/dist/cli/services/system-prompt.d.ts +7 -0
- package/dist/cli/services/system-prompt.js +198 -0
- package/dist/cli/services/team-lead.d.ts +73 -0
- package/dist/cli/services/team-lead.js +512 -0
- package/dist/cli/services/team-state.d.ts +77 -0
- package/dist/cli/services/team-state.js +398 -0
- package/dist/cli/services/teammate.d.ts +31 -0
- package/dist/cli/services/teammate.js +689 -0
- package/dist/cli/services/telemetry.d.ts +61 -0
- package/dist/cli/services/telemetry.js +209 -0
- package/dist/cli/services/tools/agent-tools.d.ts +14 -0
- package/dist/cli/services/tools/agent-tools.js +347 -0
- package/dist/cli/services/tools/file-ops.d.ts +15 -0
- package/dist/cli/services/tools/file-ops.js +487 -0
- package/dist/cli/services/tools/search-tools.d.ts +8 -0
- package/dist/cli/services/tools/search-tools.js +186 -0
- package/dist/cli/services/tools/shell-exec.d.ts +10 -0
- package/dist/cli/services/tools/shell-exec.js +168 -0
- package/dist/cli/services/tools/task-manager.d.ts +28 -0
- package/dist/cli/services/tools/task-manager.js +209 -0
- package/dist/cli/services/tools/web-tools.d.ts +11 -0
- package/dist/cli/services/tools/web-tools.js +395 -0
- package/dist/cli/setup/SetupApp.d.ts +9 -0
- package/dist/cli/setup/SetupApp.js +191 -0
- package/dist/cli/shared/MatrixIntro.d.ts +4 -0
- package/dist/cli/shared/MatrixIntro.js +83 -0
- package/dist/cli/shared/Theme.d.ts +74 -0
- package/dist/cli/shared/Theme.js +127 -0
- package/dist/cli/shared/WhaleBanner.d.ts +10 -0
- package/dist/cli/shared/WhaleBanner.js +12 -0
- package/dist/cli/shared/markdown.d.ts +21 -0
- package/dist/cli/shared/markdown.js +756 -0
- package/dist/cli/status/StatusApp.d.ts +4 -0
- package/dist/cli/status/StatusApp.js +105 -0
- package/dist/cli/stores/StoreApp.d.ts +7 -0
- package/dist/cli/stores/StoreApp.js +81 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.js +538 -0
- package/dist/local-agent/connection.d.ts +48 -0
- package/dist/local-agent/connection.js +332 -0
- package/dist/local-agent/discovery.d.ts +18 -0
- package/dist/local-agent/discovery.js +146 -0
- package/dist/local-agent/executor.d.ts +34 -0
- package/dist/local-agent/executor.js +241 -0
- package/dist/local-agent/index.d.ts +14 -0
- package/dist/local-agent/index.js +198 -0
- package/dist/node/adapters/base.d.ts +35 -0
- package/dist/node/adapters/base.js +10 -0
- package/dist/node/adapters/discord.d.ts +29 -0
- package/dist/node/adapters/discord.js +299 -0
- package/dist/node/adapters/email.d.ts +23 -0
- package/dist/node/adapters/email.js +218 -0
- package/dist/node/adapters/imessage.d.ts +17 -0
- package/dist/node/adapters/imessage.js +118 -0
- package/dist/node/adapters/slack.d.ts +26 -0
- package/dist/node/adapters/slack.js +259 -0
- package/dist/node/adapters/sms.d.ts +23 -0
- package/dist/node/adapters/sms.js +161 -0
- package/dist/node/adapters/telegram.d.ts +17 -0
- package/dist/node/adapters/telegram.js +101 -0
- package/dist/node/adapters/webchat.d.ts +27 -0
- package/dist/node/adapters/webchat.js +160 -0
- package/dist/node/adapters/whatsapp.d.ts +28 -0
- package/dist/node/adapters/whatsapp.js +230 -0
- package/dist/node/cli.d.ts +2 -0
- package/dist/node/cli.js +325 -0
- package/dist/node/config.d.ts +17 -0
- package/dist/node/config.js +31 -0
- package/dist/node/runtime.d.ts +50 -0
- package/dist/node/runtime.js +351 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.d.ts +11 -0
- package/dist/server/handlers/__test-utils__/mock-supabase.js +393 -0
- package/dist/server/handlers/analytics.d.ts +17 -0
- package/dist/server/handlers/analytics.js +266 -0
- package/dist/server/handlers/api-keys.d.ts +6 -0
- package/dist/server/handlers/api-keys.js +221 -0
- package/dist/server/handlers/billing.d.ts +33 -0
- package/dist/server/handlers/billing.js +272 -0
- package/dist/server/handlers/browser.d.ts +10 -0
- package/dist/server/handlers/browser.js +517 -0
- package/dist/server/handlers/catalog.d.ts +99 -0
- package/dist/server/handlers/catalog.js +976 -0
- package/dist/server/handlers/comms.d.ts +254 -0
- package/dist/server/handlers/comms.js +588 -0
- package/dist/server/handlers/creations.d.ts +6 -0
- package/dist/server/handlers/creations.js +479 -0
- package/dist/server/handlers/crm.d.ts +89 -0
- package/dist/server/handlers/crm.js +538 -0
- package/dist/server/handlers/discovery.d.ts +6 -0
- package/dist/server/handlers/discovery.js +288 -0
- package/dist/server/handlers/embeddings.d.ts +92 -0
- package/dist/server/handlers/embeddings.js +197 -0
- package/dist/server/handlers/enrichment.d.ts +8 -0
- package/dist/server/handlers/enrichment.js +768 -0
- package/dist/server/handlers/image-gen.d.ts +6 -0
- package/dist/server/handlers/image-gen.js +409 -0
- package/dist/server/handlers/inventory.d.ts +319 -0
- package/dist/server/handlers/inventory.js +447 -0
- package/dist/server/handlers/kali.d.ts +10 -0
- package/dist/server/handlers/kali.js +210 -0
- package/dist/server/handlers/llm-providers.d.ts +6 -0
- package/dist/server/handlers/llm-providers.js +673 -0
- package/dist/server/handlers/local-agent.d.ts +6 -0
- package/dist/server/handlers/local-agent.js +118 -0
- package/dist/server/handlers/meta-ads.d.ts +111 -0
- package/dist/server/handlers/meta-ads.js +2279 -0
- package/dist/server/handlers/nodes.d.ts +33 -0
- package/dist/server/handlers/nodes.js +699 -0
- package/dist/server/handlers/operations.d.ts +138 -0
- package/dist/server/handlers/operations.js +131 -0
- package/dist/server/handlers/platform.d.ts +23 -0
- package/dist/server/handlers/platform.js +227 -0
- package/dist/server/handlers/supply-chain.d.ts +19 -0
- package/dist/server/handlers/supply-chain.js +327 -0
- package/dist/server/handlers/transcription.d.ts +17 -0
- package/dist/server/handlers/transcription.js +121 -0
- package/dist/server/handlers/video-gen.d.ts +6 -0
- package/dist/server/handlers/video-gen.js +466 -0
- package/dist/server/handlers/voice.d.ts +8 -0
- package/dist/server/handlers/voice.js +1146 -0
- package/dist/server/handlers/workflow-steps.d.ts +86 -0
- package/dist/server/handlers/workflow-steps.js +2349 -0
- package/dist/server/handlers/workflows.d.ts +7 -0
- package/dist/server/handlers/workflows.js +989 -0
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.js +2427 -0
- package/dist/server/lib/batch-client.d.ts +80 -0
- package/dist/server/lib/batch-client.js +467 -0
- package/dist/server/lib/code-worker-pool.d.ts +31 -0
- package/dist/server/lib/code-worker-pool.js +224 -0
- package/dist/server/lib/code-worker.d.ts +1 -0
- package/dist/server/lib/code-worker.js +188 -0
- package/dist/server/lib/compaction-service.d.ts +32 -0
- package/dist/server/lib/compaction-service.js +162 -0
- package/dist/server/lib/logger.d.ts +19 -0
- package/dist/server/lib/logger.js +46 -0
- package/dist/server/lib/otel.d.ts +38 -0
- package/dist/server/lib/otel.js +126 -0
- package/dist/server/lib/pg-rate-limiter.d.ts +21 -0
- package/dist/server/lib/pg-rate-limiter.js +86 -0
- package/dist/server/lib/prompt-sanitizer.d.ts +37 -0
- package/dist/server/lib/prompt-sanitizer.js +177 -0
- package/dist/server/lib/provider-capabilities.d.ts +85 -0
- package/dist/server/lib/provider-capabilities.js +190 -0
- package/dist/server/lib/provider-failover.d.ts +74 -0
- package/dist/server/lib/provider-failover.js +210 -0
- package/dist/server/lib/rate-limiter.d.ts +39 -0
- package/dist/server/lib/rate-limiter.js +147 -0
- package/dist/server/lib/server-agent-loop.d.ts +107 -0
- package/dist/server/lib/server-agent-loop.js +667 -0
- package/dist/server/lib/server-subagent.d.ts +78 -0
- package/dist/server/lib/server-subagent.js +203 -0
- package/dist/server/lib/session-checkpoint.d.ts +51 -0
- package/dist/server/lib/session-checkpoint.js +145 -0
- package/dist/server/lib/ssrf-guard.d.ts +13 -0
- package/dist/server/lib/ssrf-guard.js +240 -0
- package/dist/server/lib/supabase-client.d.ts +7 -0
- package/dist/server/lib/supabase-client.js +78 -0
- package/dist/server/lib/template-resolver.d.ts +31 -0
- package/dist/server/lib/template-resolver.js +215 -0
- package/dist/server/lib/utils.d.ts +16 -0
- package/dist/server/lib/utils.js +147 -0
- package/dist/server/local-agent-gateway.d.ts +82 -0
- package/dist/server/local-agent-gateway.js +426 -0
- package/dist/server/providers/anthropic.d.ts +20 -0
- package/dist/server/providers/anthropic.js +199 -0
- package/dist/server/providers/bedrock.d.ts +20 -0
- package/dist/server/providers/bedrock.js +194 -0
- package/dist/server/providers/gemini.d.ts +24 -0
- package/dist/server/providers/gemini.js +486 -0
- package/dist/server/providers/openai.d.ts +24 -0
- package/dist/server/providers/openai.js +522 -0
- package/dist/server/providers/registry.d.ts +32 -0
- package/dist/server/providers/registry.js +58 -0
- package/dist/server/providers/shared.d.ts +32 -0
- package/dist/server/providers/shared.js +124 -0
- package/dist/server/providers/types.d.ts +92 -0
- package/dist/server/providers/types.js +12 -0
- package/dist/server/proxy-handlers.d.ts +6 -0
- package/dist/server/proxy-handlers.js +89 -0
- package/dist/server/tool-router.d.ts +149 -0
- package/dist/server/tool-router.js +803 -0
- package/dist/server/validation.d.ts +24 -0
- package/dist/server/validation.js +301 -0
- package/dist/server/worker.d.ts +19 -0
- package/dist/server/worker.js +201 -0
- package/dist/setup.d.ts +8 -0
- package/dist/setup.js +181 -0
- package/dist/shared/agent-core.d.ts +157 -0
- package/dist/shared/agent-core.js +534 -0
- package/dist/shared/anthropic-types.d.ts +105 -0
- package/dist/shared/anthropic-types.js +7 -0
- package/dist/shared/api-client.d.ts +90 -0
- package/dist/shared/api-client.js +379 -0
- package/dist/shared/constants.d.ts +33 -0
- package/dist/shared/constants.js +80 -0
- package/dist/shared/sse-parser.d.ts +26 -0
- package/dist/shared/sse-parser.js +259 -0
- package/dist/shared/tool-dispatch.d.ts +52 -0
- package/dist/shared/tool-dispatch.js +191 -0
- package/dist/shared/types.d.ts +72 -0
- package/dist/shared/types.js +7 -0
- package/dist/updater.d.ts +25 -0
- package/dist/updater.js +140 -0
- package/dist/webchat/widget.d.ts +0 -0
- package/dist/webchat/widget.js +397 -0
- package/package.json +95 -0
- package/src/cli/services/builtin-skills/commit.md +19 -0
- package/src/cli/services/builtin-skills/review-pr.md +21 -0
- package/src/cli/services/builtin-skills/review.md +18 -0
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
// server/handlers/video-gen.ts — Multi-provider video generation
|
|
2
|
+
// Providers: Google Veo (3.1, 3.0, 2.0), OpenAI Sora (sora-2, sora-2-pro)
|
|
3
|
+
// Async polling pattern — video gen takes 1-5 minutes
|
|
4
|
+
// Auto-uploads to Supabase storage, records in store_media
|
|
5
|
+
import { GoogleGenAI } from "@google/genai";
|
|
6
|
+
// In-memory job tracker (server is single-instance on Fly.io)
|
|
7
|
+
const activeJobs = new Map();
|
|
8
|
+
async function getVideoCredentials(sb, storeId) {
|
|
9
|
+
const creds = {};
|
|
10
|
+
const results = await Promise.all([
|
|
11
|
+
(async () => { try {
|
|
12
|
+
const r = await sb.rpc("decrypt_secret", { p_name: "OPENAI_API_KEY", p_store_id: storeId });
|
|
13
|
+
return r.data;
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return null;
|
|
17
|
+
} })(),
|
|
18
|
+
(async () => { try {
|
|
19
|
+
const r = await sb.rpc("decrypt_secret", { p_name: "GOOGLE_AI_API_KEY", p_store_id: storeId });
|
|
20
|
+
return r.data;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return null;
|
|
24
|
+
} })(),
|
|
25
|
+
]);
|
|
26
|
+
if (results[0])
|
|
27
|
+
creds.openai = results[0];
|
|
28
|
+
if (results[1])
|
|
29
|
+
creds.google = results[1];
|
|
30
|
+
return creds;
|
|
31
|
+
}
|
|
32
|
+
// ============================================================================
|
|
33
|
+
// STORAGE HELPER
|
|
34
|
+
// ============================================================================
|
|
35
|
+
async function uploadVideoAndRecord(sb, storeId, videoData, prompt, provider, model, mimeType = "video/mp4") {
|
|
36
|
+
const id = crypto.randomUUID();
|
|
37
|
+
const ext = mimeType.includes("webm") ? "webm" : "mp4";
|
|
38
|
+
const fileName = `${id}.${ext}`;
|
|
39
|
+
const storeIdUpper = storeId.toUpperCase();
|
|
40
|
+
const storagePath = `ai-generated/${storeIdUpper}/videos/${fileName}`;
|
|
41
|
+
const buffer = Buffer.from(videoData);
|
|
42
|
+
const { error: uploadErr } = await sb.storage
|
|
43
|
+
.from("store-media")
|
|
44
|
+
.upload(storagePath, buffer, { contentType: mimeType, upsert: true });
|
|
45
|
+
if (uploadErr)
|
|
46
|
+
throw new Error(`Storage upload failed: ${uploadErr.message}`);
|
|
47
|
+
const { data: urlData } = sb.storage.from("store-media").getPublicUrl(storagePath);
|
|
48
|
+
const fileUrl = urlData.publicUrl;
|
|
49
|
+
const aiTags = [
|
|
50
|
+
provider === "veo" ? `Veo (${model})` : `Sora (${model})`,
|
|
51
|
+
"AI Generated",
|
|
52
|
+
"Video",
|
|
53
|
+
];
|
|
54
|
+
const { data: mediaRow, error: mediaErr } = await sb
|
|
55
|
+
.from("store_media")
|
|
56
|
+
.insert({
|
|
57
|
+
store_id: storeId,
|
|
58
|
+
file_name: fileName,
|
|
59
|
+
file_path: storagePath,
|
|
60
|
+
file_url: fileUrl,
|
|
61
|
+
file_size: buffer.length,
|
|
62
|
+
file_type: mimeType,
|
|
63
|
+
category: "ai_generated",
|
|
64
|
+
ai_tags: aiTags,
|
|
65
|
+
ai_description: prompt.substring(0, 500),
|
|
66
|
+
source: `mcp-${provider}`,
|
|
67
|
+
folder: "ai-generated",
|
|
68
|
+
})
|
|
69
|
+
.select("id")
|
|
70
|
+
.single();
|
|
71
|
+
if (mediaErr)
|
|
72
|
+
console.error("[video-gen] store_media insert error:", mediaErr.message);
|
|
73
|
+
return { file_url: fileUrl, media_id: mediaRow?.id || id };
|
|
74
|
+
}
|
|
75
|
+
// ============================================================================
|
|
76
|
+
// VEO GENERATION (async polling)
|
|
77
|
+
// ============================================================================
|
|
78
|
+
async function generateWithVeo(apiKey, prompt, model, config, sb, storeId, jobId) {
|
|
79
|
+
const job = activeJobs.get(jobId);
|
|
80
|
+
if (!job)
|
|
81
|
+
return;
|
|
82
|
+
try {
|
|
83
|
+
job.status = "generating";
|
|
84
|
+
const client = new GoogleGenAI({ apiKey });
|
|
85
|
+
let operation = await client.models.generateVideos({
|
|
86
|
+
model,
|
|
87
|
+
prompt,
|
|
88
|
+
config: {
|
|
89
|
+
...(config.aspectRatio ? { aspectRatio: config.aspectRatio } : {}),
|
|
90
|
+
...(config.resolution ? { resolution: config.resolution } : {}),
|
|
91
|
+
...(config.negativePrompt ? { negativePrompt: config.negativePrompt } : {}),
|
|
92
|
+
},
|
|
93
|
+
});
|
|
94
|
+
// Poll until complete (max 10 minutes)
|
|
95
|
+
const maxWait = 600_000;
|
|
96
|
+
const start = Date.now();
|
|
97
|
+
while (!operation.done && Date.now() - start < maxWait) {
|
|
98
|
+
await new Promise((r) => setTimeout(r, 10_000));
|
|
99
|
+
operation = await client.operations.getVideosOperation({ operation });
|
|
100
|
+
}
|
|
101
|
+
if (!operation.done) {
|
|
102
|
+
job.status = "failed";
|
|
103
|
+
job.error = "Video generation timed out after 10 minutes";
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
const video = operation.response?.generatedVideos?.[0]?.video;
|
|
107
|
+
if (!video) {
|
|
108
|
+
job.status = "failed";
|
|
109
|
+
job.error = "Veo returned no video data";
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
// Wait for file processing to complete (Veo files need post-processing)
|
|
113
|
+
job.status = "uploading";
|
|
114
|
+
const videoUri = video.uri;
|
|
115
|
+
if (!videoUri) {
|
|
116
|
+
job.status = "failed";
|
|
117
|
+
job.error = "Veo returned video object without URI";
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
// Extract file ID from URI — format: .../files/FILEID or .../files/FILEID:download
|
|
121
|
+
const fileIdMatch = videoUri.match(/files\/([^/:?]+)/);
|
|
122
|
+
if (!fileIdMatch) {
|
|
123
|
+
job.status = "failed";
|
|
124
|
+
job.error = `Cannot parse file ID from URI: ${videoUri}`;
|
|
125
|
+
return;
|
|
126
|
+
}
|
|
127
|
+
const fileId = fileIdMatch[1];
|
|
128
|
+
const fileInfoUrl = `https://generativelanguage.googleapis.com/v1beta/files/${fileId}?key=${apiKey}`;
|
|
129
|
+
// Poll until file state is ACTIVE (max 10 minutes — Veo processing can be slow)
|
|
130
|
+
const fileStart = Date.now();
|
|
131
|
+
let fileState = "PROCESSING";
|
|
132
|
+
while (fileState === "PROCESSING" && Date.now() - fileStart < 600_000) {
|
|
133
|
+
await new Promise((r) => setTimeout(r, 5_000));
|
|
134
|
+
const infoResp = await fetch(fileInfoUrl);
|
|
135
|
+
if (infoResp.ok) {
|
|
136
|
+
const info = await infoResp.json();
|
|
137
|
+
fileState = info.state;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
if (fileState !== "ACTIVE") {
|
|
141
|
+
job.status = "failed";
|
|
142
|
+
job.error = `File processing did not complete. State: ${fileState}`;
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Download via SDK (writes to temp file, then read)
|
|
146
|
+
const os = await import("node:os");
|
|
147
|
+
const path = await import("node:path");
|
|
148
|
+
const fs = await import("node:fs");
|
|
149
|
+
const tmpPath = path.join(os.tmpdir(), `veo_${jobId}.mp4`);
|
|
150
|
+
await client.files.download({
|
|
151
|
+
file: video,
|
|
152
|
+
downloadPath: tmpPath,
|
|
153
|
+
});
|
|
154
|
+
const videoBytes = fs.readFileSync(tmpPath);
|
|
155
|
+
fs.unlinkSync(tmpPath);
|
|
156
|
+
// Upload to storage
|
|
157
|
+
const { file_url, media_id } = await uploadVideoAndRecord(sb, storeId, videoBytes, prompt, "veo", model);
|
|
158
|
+
job.status = "completed";
|
|
159
|
+
job.file_url = file_url;
|
|
160
|
+
job.media_id = media_id;
|
|
161
|
+
}
|
|
162
|
+
catch (err) {
|
|
163
|
+
job.status = "failed";
|
|
164
|
+
job.error = err instanceof Error ? err.message : String(err);
|
|
165
|
+
console.error(`[video-gen] Veo job ${jobId} failed:`, job.error);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
// ============================================================================
|
|
169
|
+
// SORA GENERATION (async polling)
|
|
170
|
+
// ============================================================================
|
|
171
|
+
async function generateWithSora(apiKey, prompt, model, config, sb, storeId, jobId) {
|
|
172
|
+
const job = activeJobs.get(jobId);
|
|
173
|
+
if (!job)
|
|
174
|
+
return;
|
|
175
|
+
try {
|
|
176
|
+
job.status = "generating";
|
|
177
|
+
const baseUrl = "https://api.openai.com/v1";
|
|
178
|
+
const headers = { Authorization: `Bearer ${apiKey}`, "Content-Type": "application/json" };
|
|
179
|
+
// Create video job
|
|
180
|
+
const createResp = await fetch(`${baseUrl}/videos`, {
|
|
181
|
+
method: "POST",
|
|
182
|
+
headers,
|
|
183
|
+
body: JSON.stringify({
|
|
184
|
+
model,
|
|
185
|
+
prompt,
|
|
186
|
+
...(config.duration ? { duration: config.duration } : {}),
|
|
187
|
+
...(config.resolution ? { resolution: config.resolution } : {}),
|
|
188
|
+
}),
|
|
189
|
+
});
|
|
190
|
+
if (!createResp.ok) {
|
|
191
|
+
const errText = await createResp.text();
|
|
192
|
+
throw new Error(`Sora create failed: ${createResp.status} ${errText.substring(0, 300)}`);
|
|
193
|
+
}
|
|
194
|
+
const createData = await createResp.json();
|
|
195
|
+
const videoId = createData.id;
|
|
196
|
+
// Poll until complete (max 10 minutes)
|
|
197
|
+
const maxWait = 600_000;
|
|
198
|
+
const start = Date.now();
|
|
199
|
+
let status = createData.status;
|
|
200
|
+
while (status !== "completed" && status !== "failed" && Date.now() - start < maxWait) {
|
|
201
|
+
await new Promise((r) => setTimeout(r, 10_000));
|
|
202
|
+
const pollResp = await fetch(`${baseUrl}/videos/${videoId}`, { headers });
|
|
203
|
+
const pollData = await pollResp.json();
|
|
204
|
+
status = pollData.status;
|
|
205
|
+
if (status === "failed") {
|
|
206
|
+
job.status = "failed";
|
|
207
|
+
job.error = pollData.error?.message || "Sora generation failed";
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
if (status !== "completed") {
|
|
212
|
+
job.status = "failed";
|
|
213
|
+
job.error = "Video generation timed out after 10 minutes";
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
216
|
+
// Download the video
|
|
217
|
+
job.status = "uploading";
|
|
218
|
+
const downloadResp = await fetch(`${baseUrl}/videos/${videoId}/content`, {
|
|
219
|
+
headers: { Authorization: `Bearer ${apiKey}`, Accept: "video/mp4" },
|
|
220
|
+
});
|
|
221
|
+
if (!downloadResp.ok)
|
|
222
|
+
throw new Error(`Sora download failed: ${downloadResp.status}`);
|
|
223
|
+
const videoBuffer = Buffer.from(await downloadResp.arrayBuffer());
|
|
224
|
+
const { file_url, media_id } = await uploadVideoAndRecord(sb, storeId, videoBuffer, prompt, "sora", model);
|
|
225
|
+
job.status = "completed";
|
|
226
|
+
job.file_url = file_url;
|
|
227
|
+
job.media_id = media_id;
|
|
228
|
+
}
|
|
229
|
+
catch (err) {
|
|
230
|
+
job.status = "failed";
|
|
231
|
+
job.error = err instanceof Error ? err.message : String(err);
|
|
232
|
+
console.error(`[video-gen] Sora job ${jobId} failed:`, job.error);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// ============================================================================
|
|
236
|
+
// HANDLER
|
|
237
|
+
// ============================================================================
|
|
238
|
+
export async function handleVideoGen(sb, args, storeId) {
|
|
239
|
+
const action = args.action;
|
|
240
|
+
if (!storeId)
|
|
241
|
+
return { success: false, error: "store_id is required" };
|
|
242
|
+
switch (action) {
|
|
243
|
+
// ================================================================
|
|
244
|
+
// GENERATE — start a video generation job (returns immediately with job_id)
|
|
245
|
+
// ================================================================
|
|
246
|
+
case "generate": {
|
|
247
|
+
const prompt = args.prompt;
|
|
248
|
+
if (!prompt)
|
|
249
|
+
return { success: false, error: "prompt is required" };
|
|
250
|
+
const provider = args.provider || "veo";
|
|
251
|
+
const creds = await getVideoCredentials(sb, storeId);
|
|
252
|
+
const jobId = crypto.randomUUID();
|
|
253
|
+
const job = {
|
|
254
|
+
job_id: jobId,
|
|
255
|
+
provider,
|
|
256
|
+
model: "",
|
|
257
|
+
prompt,
|
|
258
|
+
status: "queued",
|
|
259
|
+
};
|
|
260
|
+
switch (provider) {
|
|
261
|
+
case "veo": {
|
|
262
|
+
if (!creds.google)
|
|
263
|
+
return { success: false, error: "Google AI API key not configured" };
|
|
264
|
+
const model = args.model || "veo-3.1-generate-preview";
|
|
265
|
+
const validModels = [
|
|
266
|
+
"veo-3.1-generate-preview", "veo-3.1-fast-generate-preview",
|
|
267
|
+
"veo-3.0-generate-001", "veo-3.0-fast-generate-001",
|
|
268
|
+
"veo-2.0-generate-001",
|
|
269
|
+
];
|
|
270
|
+
if (!validModels.includes(model)) {
|
|
271
|
+
return { success: false, error: `Invalid Veo model: ${model}. Valid: ${validModels.join(", ")}` };
|
|
272
|
+
}
|
|
273
|
+
job.model = model;
|
|
274
|
+
activeJobs.set(jobId, job);
|
|
275
|
+
// Fire and forget — runs in background
|
|
276
|
+
generateWithVeo(creds.google, prompt, model, {
|
|
277
|
+
aspectRatio: args.aspect_ratio,
|
|
278
|
+
resolution: args.resolution,
|
|
279
|
+
negativePrompt: args.negative_prompt,
|
|
280
|
+
}, sb, storeId, jobId).catch((err) => console.error("[video-gen] Veo background error:", err));
|
|
281
|
+
return {
|
|
282
|
+
success: true,
|
|
283
|
+
data: {
|
|
284
|
+
job_id: jobId,
|
|
285
|
+
provider: "veo",
|
|
286
|
+
model,
|
|
287
|
+
status: "queued",
|
|
288
|
+
note: "Video generation started. Use action=status with this job_id to check progress. Typically takes 1-5 minutes.",
|
|
289
|
+
},
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
case "sora": {
|
|
293
|
+
if (!creds.openai)
|
|
294
|
+
return { success: false, error: "OpenAI API key not configured" };
|
|
295
|
+
const model = args.model || "sora-2";
|
|
296
|
+
const validModels = ["sora-2", "sora-2-pro"];
|
|
297
|
+
if (!validModels.includes(model)) {
|
|
298
|
+
return { success: false, error: `Invalid Sora model: ${model}. Valid: ${validModels.join(", ")}` };
|
|
299
|
+
}
|
|
300
|
+
job.model = model;
|
|
301
|
+
activeJobs.set(jobId, job);
|
|
302
|
+
generateWithSora(creds.openai, prompt, model, {
|
|
303
|
+
duration: args.duration,
|
|
304
|
+
resolution: args.resolution,
|
|
305
|
+
}, sb, storeId, jobId).catch((err) => console.error("[video-gen] Sora background error:", err));
|
|
306
|
+
return {
|
|
307
|
+
success: true,
|
|
308
|
+
data: {
|
|
309
|
+
job_id: jobId,
|
|
310
|
+
provider: "sora",
|
|
311
|
+
model,
|
|
312
|
+
status: "queued",
|
|
313
|
+
note: "Video generation started. Use action=status with this job_id to check progress. Typically takes 1-5 minutes.",
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
}
|
|
317
|
+
default:
|
|
318
|
+
return { success: false, error: `Unknown provider: ${provider}. Use veo or sora` };
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// ================================================================
|
|
322
|
+
// BATCH — start multiple video jobs in parallel
|
|
323
|
+
// ================================================================
|
|
324
|
+
case "batch": {
|
|
325
|
+
const items = args.items;
|
|
326
|
+
if (!items?.length)
|
|
327
|
+
return { success: false, error: "items array is required" };
|
|
328
|
+
if (items.length > 5)
|
|
329
|
+
return { success: false, error: "Maximum 5 videos per batch" };
|
|
330
|
+
const creds = await getVideoCredentials(sb, storeId);
|
|
331
|
+
const jobs = [];
|
|
332
|
+
for (const item of items) {
|
|
333
|
+
const provider = item.provider || "veo";
|
|
334
|
+
const jobId = crypto.randomUUID();
|
|
335
|
+
if (provider === "veo") {
|
|
336
|
+
if (!creds.google) {
|
|
337
|
+
jobs.push({ job_id: jobId, provider, model: "", prompt: item.prompt, status: "failed: no Google API key" });
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
const model = item.model || "veo-3.1-generate-preview";
|
|
341
|
+
const job = { job_id: jobId, provider, model, prompt: item.prompt, status: "queued" };
|
|
342
|
+
activeJobs.set(jobId, job);
|
|
343
|
+
generateWithVeo(creds.google, item.prompt, model, {
|
|
344
|
+
aspectRatio: item.aspect_ratio, resolution: item.resolution, negativePrompt: item.negative_prompt,
|
|
345
|
+
}, sb, storeId, jobId).catch(e => console.warn('[video-gen] veo batch job failed:', e.message));
|
|
346
|
+
jobs.push({ job_id: jobId, provider, model, prompt: item.prompt, status: "queued" });
|
|
347
|
+
}
|
|
348
|
+
else if (provider === "sora") {
|
|
349
|
+
if (!creds.openai) {
|
|
350
|
+
jobs.push({ job_id: jobId, provider, model: "", prompt: item.prompt, status: "failed: no OpenAI API key" });
|
|
351
|
+
continue;
|
|
352
|
+
}
|
|
353
|
+
const model = item.model || "sora-2";
|
|
354
|
+
const job = { job_id: jobId, provider, model, prompt: item.prompt, status: "queued" };
|
|
355
|
+
activeJobs.set(jobId, job);
|
|
356
|
+
generateWithSora(creds.openai, item.prompt, model, {
|
|
357
|
+
duration: item.duration, resolution: item.resolution,
|
|
358
|
+
}, sb, storeId, jobId).catch(e => console.warn('[video-gen] sora batch job failed:', e.message));
|
|
359
|
+
jobs.push({ job_id: jobId, provider, model, prompt: item.prompt, status: "queued" });
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return { success: true, data: { total: jobs.length, jobs } };
|
|
363
|
+
}
|
|
364
|
+
// ================================================================
|
|
365
|
+
// STATUS — check status of a video generation job
|
|
366
|
+
// ================================================================
|
|
367
|
+
case "status": {
|
|
368
|
+
const jobId = args.job_id;
|
|
369
|
+
if (!jobId)
|
|
370
|
+
return { success: false, error: "job_id is required" };
|
|
371
|
+
const job = activeJobs.get(jobId);
|
|
372
|
+
if (!job)
|
|
373
|
+
return { success: false, error: `Job ${jobId} not found. Jobs are only tracked in memory on this server instance.` };
|
|
374
|
+
const result = {
|
|
375
|
+
job_id: job.job_id,
|
|
376
|
+
provider: job.provider,
|
|
377
|
+
model: job.model,
|
|
378
|
+
prompt: job.prompt,
|
|
379
|
+
status: job.status,
|
|
380
|
+
};
|
|
381
|
+
if (job.file_url)
|
|
382
|
+
result.file_url = job.file_url;
|
|
383
|
+
if (job.media_id)
|
|
384
|
+
result.media_id = job.media_id;
|
|
385
|
+
if (job.error)
|
|
386
|
+
result.error = job.error;
|
|
387
|
+
return { success: true, data: result };
|
|
388
|
+
}
|
|
389
|
+
// ================================================================
|
|
390
|
+
// JOBS — list all active/recent jobs
|
|
391
|
+
// ================================================================
|
|
392
|
+
case "jobs": {
|
|
393
|
+
const jobs = Array.from(activeJobs.values()).map((j) => ({
|
|
394
|
+
job_id: j.job_id,
|
|
395
|
+
provider: j.provider,
|
|
396
|
+
model: j.model,
|
|
397
|
+
prompt: j.prompt.substring(0, 80),
|
|
398
|
+
status: j.status,
|
|
399
|
+
...(j.file_url ? { file_url: j.file_url } : {}),
|
|
400
|
+
...(j.error ? { error: j.error } : {}),
|
|
401
|
+
}));
|
|
402
|
+
return { success: true, data: { total: jobs.length, jobs } };
|
|
403
|
+
}
|
|
404
|
+
// ================================================================
|
|
405
|
+
// LIST — list generated videos from store_media
|
|
406
|
+
// ================================================================
|
|
407
|
+
case "list": {
|
|
408
|
+
const limit = Math.min(args.limit || 25, 100);
|
|
409
|
+
const { data, error } = await sb
|
|
410
|
+
.from("store_media")
|
|
411
|
+
.select("id, file_name, file_url, file_size, file_type, ai_tags, ai_description, source, created_at")
|
|
412
|
+
.eq("store_id", storeId)
|
|
413
|
+
.eq("status", "active")
|
|
414
|
+
.eq("category", "ai_generated")
|
|
415
|
+
.or("file_type.eq.video/mp4,file_type.eq.video/webm")
|
|
416
|
+
.order("created_at", { ascending: false })
|
|
417
|
+
.limit(limit);
|
|
418
|
+
if (error)
|
|
419
|
+
return { success: false, error: error.message };
|
|
420
|
+
return { success: true, data: { videos: data, count: data?.length || 0 } };
|
|
421
|
+
}
|
|
422
|
+
// ================================================================
|
|
423
|
+
// PROVIDERS — show available video generation providers
|
|
424
|
+
// ================================================================
|
|
425
|
+
case "providers": {
|
|
426
|
+
const creds = await getVideoCredentials(sb, storeId);
|
|
427
|
+
return {
|
|
428
|
+
success: true,
|
|
429
|
+
data: {
|
|
430
|
+
providers: [
|
|
431
|
+
{
|
|
432
|
+
provider: "veo",
|
|
433
|
+
configured: !!creds.google,
|
|
434
|
+
models: [
|
|
435
|
+
{ id: "veo-3.1-generate-preview", name: "Veo 3.1", note: "Latest, highest quality, native audio" },
|
|
436
|
+
{ id: "veo-3.1-fast-generate-preview", name: "Veo 3.1 Fast", note: "Faster, slightly lower quality" },
|
|
437
|
+
{ id: "veo-3.0-generate-001", name: "Veo 3.0", note: "Stable release" },
|
|
438
|
+
{ id: "veo-3.0-fast-generate-001", name: "Veo 3.0 Fast", note: "Fast variant" },
|
|
439
|
+
{ id: "veo-2.0-generate-001", name: "Veo 2.0", note: "Previous generation" },
|
|
440
|
+
],
|
|
441
|
+
config: {
|
|
442
|
+
aspect_ratios: ["16:9", "9:16"],
|
|
443
|
+
resolutions: ["720p", "1080p", "4k"],
|
|
444
|
+
negative_prompt: true,
|
|
445
|
+
},
|
|
446
|
+
},
|
|
447
|
+
{
|
|
448
|
+
provider: "sora",
|
|
449
|
+
configured: !!creds.openai,
|
|
450
|
+
models: [
|
|
451
|
+
{ id: "sora-2", name: "Sora 2", note: "Fast, good for exploration" },
|
|
452
|
+
{ id: "sora-2-pro", name: "Sora 2 Pro", note: "Production quality, slower" },
|
|
453
|
+
],
|
|
454
|
+
config: {
|
|
455
|
+
durations: [4, 8, 12],
|
|
456
|
+
resolutions: ["720x1280", "1280x720", "1024x1792", "1792x1024"],
|
|
457
|
+
},
|
|
458
|
+
},
|
|
459
|
+
],
|
|
460
|
+
},
|
|
461
|
+
};
|
|
462
|
+
}
|
|
463
|
+
default:
|
|
464
|
+
return { success: false, error: `Unknown action: ${action}. Valid: generate, batch, status, jobs, list, providers` };
|
|
465
|
+
}
|
|
466
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { SupabaseClient } from "@supabase/supabase-js";
|
|
2
|
+
type Result = {
|
|
3
|
+
success: boolean;
|
|
4
|
+
data?: unknown;
|
|
5
|
+
error?: string;
|
|
6
|
+
};
|
|
7
|
+
export declare function handleVoice(sb: SupabaseClient, args: Record<string, unknown>, storeId?: string): Promise<Result>;
|
|
8
|
+
export {};
|