@wingman-ai/gateway 0.2.5 → 0.3.1

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.
Files changed (184) hide show
  1. package/README.md +9 -0
  2. package/dist/agent/config/agentConfig.cjs +12 -0
  3. package/dist/agent/config/agentConfig.d.ts +26 -0
  4. package/dist/agent/config/agentConfig.js +10 -1
  5. package/dist/agent/config/agentLoader.cjs +9 -0
  6. package/dist/agent/config/agentLoader.js +9 -0
  7. package/dist/agent/config/mcpClientManager.cjs +44 -10
  8. package/dist/agent/config/mcpClientManager.d.ts +6 -2
  9. package/dist/agent/config/mcpClientManager.js +44 -10
  10. package/dist/agent/config/toolRegistry.cjs +20 -1
  11. package/dist/agent/config/toolRegistry.d.ts +15 -0
  12. package/dist/agent/config/toolRegistry.js +20 -1
  13. package/dist/agent/tests/agentConfig.test.cjs +6 -1
  14. package/dist/agent/tests/agentConfig.test.js +6 -1
  15. package/dist/agent/tests/browserControlHelpers.test.cjs +35 -0
  16. package/dist/agent/tests/browserControlHelpers.test.d.ts +1 -0
  17. package/dist/agent/tests/browserControlHelpers.test.js +29 -0
  18. package/dist/agent/tests/browserControlTool.test.cjs +2117 -0
  19. package/dist/agent/tests/browserControlTool.test.d.ts +1 -0
  20. package/dist/agent/tests/browserControlTool.test.js +2111 -0
  21. package/dist/agent/tests/mcpClientManager.test.cjs +124 -0
  22. package/dist/agent/tests/mcpClientManager.test.d.ts +1 -0
  23. package/dist/agent/tests/mcpClientManager.test.js +118 -0
  24. package/dist/agent/tests/toolRegistry.test.cjs +6 -0
  25. package/dist/agent/tests/toolRegistry.test.js +6 -0
  26. package/dist/agent/tools/browser_control.cjs +1282 -0
  27. package/dist/agent/tools/browser_control.d.ts +478 -0
  28. package/dist/agent/tools/browser_control.js +1242 -0
  29. package/dist/agent/tools/command_execute.cjs +1 -1
  30. package/dist/agent/tools/command_execute.js +1 -1
  31. package/dist/cli/commands/agent.cjs +16 -2
  32. package/dist/cli/commands/agent.js +16 -2
  33. package/dist/cli/commands/browser.cjs +603 -0
  34. package/dist/cli/commands/browser.d.ts +13 -0
  35. package/dist/cli/commands/browser.js +566 -0
  36. package/dist/cli/commands/gateway.cjs +18 -7
  37. package/dist/cli/commands/gateway.d.ts +5 -1
  38. package/dist/cli/commands/gateway.js +18 -7
  39. package/dist/cli/commands/init.cjs +134 -45
  40. package/dist/cli/commands/init.js +134 -45
  41. package/dist/cli/commands/skill.cjs +3 -2
  42. package/dist/cli/commands/skill.js +3 -2
  43. package/dist/cli/config/loader.cjs +15 -0
  44. package/dist/cli/config/loader.js +15 -0
  45. package/dist/cli/config/schema.cjs +51 -2
  46. package/dist/cli/config/schema.d.ts +51 -0
  47. package/dist/cli/config/schema.js +44 -1
  48. package/dist/cli/core/agentInvoker.cjs +55 -66
  49. package/dist/cli/core/agentInvoker.d.ts +10 -13
  50. package/dist/cli/core/agentInvoker.js +42 -62
  51. package/dist/cli/core/imagePersistence.cjs +125 -0
  52. package/dist/cli/core/imagePersistence.d.ts +24 -0
  53. package/dist/cli/core/imagePersistence.js +85 -0
  54. package/dist/cli/core/sessionManager.cjs +297 -40
  55. package/dist/cli/core/sessionManager.d.ts +9 -0
  56. package/dist/cli/core/sessionManager.js +297 -40
  57. package/dist/cli/core/workspace.cjs +89 -0
  58. package/dist/cli/core/workspace.d.ts +1 -0
  59. package/dist/cli/core/workspace.js +55 -0
  60. package/dist/cli/index.cjs +53 -5
  61. package/dist/cli/index.js +53 -5
  62. package/dist/cli/types/browser.cjs +18 -0
  63. package/dist/cli/types/browser.d.ts +9 -0
  64. package/dist/cli/types/browser.js +0 -0
  65. package/dist/debug/terminalProbe.cjs +57 -0
  66. package/dist/debug/terminalProbe.d.ts +10 -0
  67. package/dist/debug/terminalProbe.js +20 -0
  68. package/dist/debug/terminalProbeAuth.cjs +140 -0
  69. package/dist/debug/terminalProbeAuth.d.ts +20 -0
  70. package/dist/debug/terminalProbeAuth.js +97 -0
  71. package/dist/gateway/browserRelayServer.cjs +338 -0
  72. package/dist/gateway/browserRelayServer.d.ts +38 -0
  73. package/dist/gateway/browserRelayServer.js +301 -0
  74. package/dist/gateway/http/agents.cjs +22 -0
  75. package/dist/gateway/http/agents.js +22 -0
  76. package/dist/gateway/http/fs.cjs +76 -0
  77. package/dist/gateway/http/fs.js +77 -1
  78. package/dist/gateway/http/sessions.cjs +25 -5
  79. package/dist/gateway/http/sessions.js +25 -5
  80. package/dist/gateway/server.cjs +155 -17
  81. package/dist/gateway/server.d.ts +6 -1
  82. package/dist/gateway/server.js +148 -16
  83. package/dist/gateway/transport/websocket.cjs +45 -10
  84. package/dist/gateway/transport/websocket.d.ts +1 -0
  85. package/dist/gateway/transport/websocket.js +41 -9
  86. package/dist/gateway/types.d.ts +4 -0
  87. package/dist/tests/agentInvokerSummarization.test.cjs +56 -37
  88. package/dist/tests/agentInvokerSummarization.test.js +58 -39
  89. package/dist/tests/agentInvokerWorkdir.test.cjs +50 -0
  90. package/dist/tests/agentInvokerWorkdir.test.js +52 -2
  91. package/dist/tests/agents-api.test.cjs +52 -0
  92. package/dist/tests/agents-api.test.js +53 -1
  93. package/dist/tests/browser-command.test.cjs +264 -0
  94. package/dist/tests/browser-command.test.d.ts +1 -0
  95. package/dist/tests/browser-command.test.js +258 -0
  96. package/dist/tests/browser-relay-server.test.cjs +20 -0
  97. package/dist/tests/browser-relay-server.test.d.ts +1 -0
  98. package/dist/tests/browser-relay-server.test.js +14 -0
  99. package/dist/tests/cli-config-loader.test.cjs +43 -0
  100. package/dist/tests/cli-config-loader.test.js +43 -0
  101. package/dist/tests/cli-init.test.cjs +61 -2
  102. package/dist/tests/cli-init.test.js +61 -2
  103. package/dist/tests/cli-workspace-root.test.cjs +114 -0
  104. package/dist/tests/cli-workspace-root.test.d.ts +1 -0
  105. package/dist/tests/cli-workspace-root.test.js +108 -0
  106. package/dist/tests/falRuntime.test.cjs +78 -0
  107. package/dist/tests/falRuntime.test.d.ts +1 -0
  108. package/dist/tests/falRuntime.test.js +72 -0
  109. package/dist/tests/falSummary.test.cjs +51 -0
  110. package/dist/tests/falSummary.test.d.ts +1 -0
  111. package/dist/tests/falSummary.test.js +45 -0
  112. package/dist/tests/fs-api.test.cjs +138 -0
  113. package/dist/tests/fs-api.test.d.ts +1 -0
  114. package/dist/tests/fs-api.test.js +132 -0
  115. package/dist/tests/gateway-command-workspace.test.cjs +150 -0
  116. package/dist/tests/gateway-command-workspace.test.d.ts +1 -0
  117. package/dist/tests/gateway-command-workspace.test.js +144 -0
  118. package/dist/tests/gateway-request-execution-overrides.test.cjs +42 -0
  119. package/dist/tests/gateway-request-execution-overrides.test.d.ts +1 -0
  120. package/dist/tests/gateway-request-execution-overrides.test.js +36 -0
  121. package/dist/tests/gateway.test.cjs +140 -1
  122. package/dist/tests/gateway.test.js +140 -1
  123. package/dist/tests/imagePersistence.test.cjs +143 -0
  124. package/dist/tests/imagePersistence.test.d.ts +1 -0
  125. package/dist/tests/imagePersistence.test.js +137 -0
  126. package/dist/tests/sessionMessageAttachments.test.cjs +30 -0
  127. package/dist/tests/sessionMessageAttachments.test.js +30 -0
  128. package/dist/tests/sessionStateMessages.test.cjs +126 -0
  129. package/dist/tests/sessionStateMessages.test.js +126 -0
  130. package/dist/tests/sessions-api.test.cjs +117 -3
  131. package/dist/tests/sessions-api.test.js +118 -4
  132. package/dist/tests/terminalProbe.test.cjs +45 -0
  133. package/dist/tests/terminalProbe.test.d.ts +1 -0
  134. package/dist/tests/terminalProbe.test.js +39 -0
  135. package/dist/tests/terminalProbeAuth.test.cjs +85 -0
  136. package/dist/tests/terminalProbeAuth.test.d.ts +1 -0
  137. package/dist/tests/terminalProbeAuth.test.js +79 -0
  138. package/dist/tests/websocket-transport.test.cjs +31 -0
  139. package/dist/tests/websocket-transport.test.d.ts +1 -0
  140. package/dist/tests/websocket-transport.test.js +25 -0
  141. package/dist/tools/fal/runtime.cjs +103 -0
  142. package/dist/tools/fal/runtime.d.ts +10 -0
  143. package/dist/tools/fal/runtime.js +60 -0
  144. package/dist/tools/fal/summary.cjs +78 -0
  145. package/dist/tools/fal/summary.d.ts +22 -0
  146. package/dist/tools/fal/summary.js +41 -0
  147. package/dist/tools/mcp-fal-ai.cjs +1041 -0
  148. package/dist/tools/mcp-fal-ai.d.ts +1 -0
  149. package/dist/tools/mcp-fal-ai.js +1025 -0
  150. package/dist/types/mcp.cjs +2 -0
  151. package/dist/types/mcp.d.ts +8 -0
  152. package/dist/types/mcp.js +3 -1
  153. package/dist/webui/assets/index-BW9nM0J2.css +11 -0
  154. package/dist/webui/assets/index-C8-oboEC.js +278 -0
  155. package/dist/webui/index.html +2 -2
  156. package/extensions/wingman-browser-extension/README.md +27 -0
  157. package/extensions/wingman-browser-extension/background.js +416 -0
  158. package/extensions/wingman-browser-extension/manifest.json +19 -0
  159. package/extensions/wingman-browser-extension/options.html +156 -0
  160. package/extensions/wingman-browser-extension/options.js +106 -0
  161. package/package.json +18 -13
  162. package/{.wingman → templates}/agents/README.md +2 -1
  163. package/{.wingman → templates}/agents/coding/agent.md +5 -1
  164. package/{.wingman → templates}/agents/coding-v2/agent.md +58 -1
  165. package/templates/agents/game-dev/agent.md +101 -0
  166. package/templates/agents/game-dev/art-generation.md +38 -0
  167. package/templates/agents/game-dev/asset-refinement.md +17 -0
  168. package/templates/agents/game-dev/planning-idea.md +17 -0
  169. package/templates/agents/game-dev/ui-specialist.md +17 -0
  170. package/templates/agents/main/agent.md +29 -0
  171. package/{.wingman → templates}/agents/researcher/agent.md +9 -0
  172. package/{.wingman → templates}/agents/stock-trader/agent.md +1 -0
  173. package/.wingman/agents/main/agent.md +0 -22
  174. package/dist/webui/assets/index-C7EuTbnE.js +0 -270
  175. package/dist/webui/assets/index-DVWQluit.css +0 -11
  176. /package/{.wingman → templates}/agents/coding-v2/implementor.md +0 -0
  177. /package/{.wingman → templates}/agents/stock-trader/chain-curator.md +0 -0
  178. /package/{.wingman → templates}/agents/stock-trader/goal-translator.md +0 -0
  179. /package/{.wingman → templates}/agents/stock-trader/guardrails-veto.md +0 -0
  180. /package/{.wingman → templates}/agents/stock-trader/path-planner.md +0 -0
  181. /package/{.wingman → templates}/agents/stock-trader/regime-analyst.md +0 -0
  182. /package/{.wingman → templates}/agents/stock-trader/risk.md +0 -0
  183. /package/{.wingman → templates}/agents/stock-trader/selection.md +0 -0
  184. /package/{.wingman → templates}/agents/stock-trader/strategy-composer.md +0 -0
@@ -0,0 +1,1041 @@
1
+ "use strict";
2
+ var __webpack_modules__ = {
3
+ "./src/tools/mcp-fal-ai.ts" (module, __webpack_exports__, __webpack_require__) {
4
+ __webpack_require__.a(module, async function(__rspack_load_async_deps, __rspack_async_done) {
5
+ try {
6
+ __webpack_require__.r(__webpack_exports__);
7
+ var node_crypto__rspack_import_0 = __webpack_require__("node:crypto");
8
+ var node_fs__rspack_import_1 = __webpack_require__("node:fs");
9
+ var node_path__rspack_import_2 = __webpack_require__("node:path");
10
+ var _fal_ai_client__rspack_import_3 = __webpack_require__("@fal-ai/client");
11
+ var _modelcontextprotocol_sdk_server_mcp_js__rspack_import_4 = __webpack_require__("@modelcontextprotocol/sdk/server/mcp.js");
12
+ var _modelcontextprotocol_sdk_server_stdio_js__rspack_import_5 = __webpack_require__("@modelcontextprotocol/sdk/server/stdio.js");
13
+ var zod__rspack_import_6 = __webpack_require__("zod");
14
+ var _fal_runtime_js__rspack_import_7 = __webpack_require__("./fal/runtime.js");
15
+ var _fal_summary_js__rspack_import_8 = __webpack_require__("./fal/summary.js");
16
+ const FAL_DEFAULT_POLL_INTERVAL_MS = parsePositiveInt(process.env.FAL_MCP_POLL_INTERVAL_MS) || 2500;
17
+ const FAL_DEFAULT_STATUS_TIMEOUT_MS = parsePositiveInt(process.env.FAL_MCP_STATUS_TIMEOUT_MS) || 60000;
18
+ const FAL_HTTP_TIMEOUT_MS = parsePositiveInt(process.env.FAL_MCP_HTTP_TIMEOUT_MS) || 120000;
19
+ const FAL_DEFAULT_LOG_LIMIT = 40;
20
+ const FAL_API_KEY_ENV = process.env.FAL_API_KEY?.trim() || process.env.FAL_KEY?.trim() || "";
21
+ const FAL_REVIEW_MODE = normalizeReviewMode(process.env.FAL_MCP_REVIEW_MODE);
22
+ const FAL_MODELS = {
23
+ imageOrTexture: process.env.FAL_MODEL_IMAGE_OR_TEXTURE?.trim() || "fal-ai/nano-banana-pro",
24
+ imageEdit: process.env.FAL_MODEL_IMAGE_EDIT?.trim() || "fal-ai/kling-image/v3/image-to-image",
25
+ audioMusic: process.env.FAL_MODEL_AUDIO_OR_MUSIC?.trim() || "fal-ai/elevenlabs/music",
26
+ audioSoundEffect: process.env.FAL_MODEL_SOUND_EFFECT?.trim() || "beatoven/sound-effect-generation",
27
+ videoFromImage: process.env.FAL_MODEL_VIDEO_FROM_IMAGE?.trim() || "fal-ai/kling-video/o3/standard/image-to-video"
28
+ };
29
+ const FAL_WORKDIR = (0, _fal_runtime_js__rspack_import_7.resolveFalWorkdir)();
30
+ const FAL_STATE_DIR = (0, _fal_runtime_js__rspack_import_7.resolveFalStateDir)();
31
+ const FAL_OUTPUT_DIR = (0, _fal_runtime_js__rspack_import_7.resolveFalOutputDir)();
32
+ const JOBS_DIR = (0, node_path__rspack_import_2.join)(FAL_STATE_DIR, "jobs");
33
+ const PENDING_DIR = (0, node_path__rspack_import_2.join)(FAL_STATE_DIR, "pending");
34
+ const inMemoryJobs = new Map();
35
+ const server = new _modelcontextprotocol_sdk_server_mcp_js__rspack_import_4.McpServer({
36
+ name: "wingman-fal-ai",
37
+ version: "0.1.0"
38
+ });
39
+ const sharedInputSchema = zod__rspack_import_6.object({
40
+ apiKey: zod__rspack_import_6.string().min(1).optional().describe("Optional FAL API key override for this tool call."),
41
+ modelInput: zod__rspack_import_6.record(zod__rspack_import_6.string(), zod__rspack_import_6.any()).optional().describe("Optional raw model input overrides merged into the default payload."),
42
+ waitForCompletion: zod__rspack_import_6.boolean().optional().default(false).describe("If true, block in-tool and poll status until complete or timeout."),
43
+ timeoutSeconds: zod__rspack_import_6.number().int().min(1).max(3600).optional().describe("Optional timeout in seconds for waitForCompletion mode."),
44
+ pollIntervalMs: zod__rspack_import_6.number().int().min(250).max(30000).optional().describe("Optional polling interval in ms for waitForCompletion mode.")
45
+ });
46
+ const generateImageOrTextureSchema = sharedInputSchema.extend({
47
+ prompt: zod__rspack_import_6.string().min(1)
48
+ });
49
+ const generateImageEditSchema = sharedInputSchema.extend({
50
+ prompt: zod__rspack_import_6.string().min(1),
51
+ sourceImage: zod__rspack_import_6.string().min(1).describe("Source image as data URL, local file path, or http(s) URL.")
52
+ });
53
+ const generateAudioOrMusicSchema = sharedInputSchema.extend({
54
+ prompt: zod__rspack_import_6.string().min(1),
55
+ audioMode: zod__rspack_import_6["enum"]([
56
+ "music",
57
+ "sound_effect"
58
+ ]).optional().default("music").describe("Use 'music' for songs/tracks or 'sound_effect' for effects.")
59
+ });
60
+ const generateVideoFromImageSchema = sharedInputSchema.extend({
61
+ prompt: zod__rspack_import_6.string().min(1),
62
+ sourceImage: zod__rspack_import_6.string().min(1).describe("Source image as data URL, local file path, or http(s) URL.")
63
+ });
64
+ const falGenerationStatusSchema = zod__rspack_import_6.object({
65
+ jobId: zod__rspack_import_6.string().min(1).describe("Job id returned from a generate_* tool."),
66
+ action: zod__rspack_import_6["enum"]([
67
+ "check",
68
+ "wait",
69
+ "cancel",
70
+ "accept",
71
+ "deny"
72
+ ]).optional().default("check").describe("Status action. Use wait for polling, accept/deny for review flow."),
73
+ includeLogs: zod__rspack_import_6.boolean().optional().default(true).describe("Include recent queue logs in the result payload."),
74
+ timeoutSeconds: zod__rspack_import_6.number().int().min(1).max(3600).optional().describe("Timeout in seconds when action=wait."),
75
+ pollIntervalMs: zod__rspack_import_6.number().int().min(250).max(30000).optional().describe("Polling interval in ms when action=wait."),
76
+ apiKey: zod__rspack_import_6.string().min(1).optional().describe("Optional FAL API key override for queue status calls.")
77
+ });
78
+ server.registerTool("generate_image_or_texture", {
79
+ title: "FAL Generate Image Or Texture",
80
+ description: "Generate an image or texture with FAL using a queue-backed async job. Returns a job id. Use fal_generation_status for updates and completion.",
81
+ inputSchema: generateImageOrTextureSchema
82
+ }, async ({ prompt, modelInput, apiKey, waitForCompletion, timeoutSeconds, pollIntervalMs })=>{
83
+ const resolvedApiKey = resolveFalApiKey(apiKey);
84
+ const client = (0, _fal_ai_client__rspack_import_3.createFalClient)({
85
+ credentials: resolvedApiKey
86
+ });
87
+ const payload = mergeModelInput({
88
+ prompt
89
+ }, modelInput);
90
+ const job = await submitFalJob({
91
+ client,
92
+ modelId: FAL_MODELS.imageOrTexture,
93
+ modality: "image",
94
+ toolName: "generate_image_or_texture",
95
+ prompt,
96
+ input: payload
97
+ });
98
+ if (waitForCompletion) return runStatusAction({
99
+ job,
100
+ action: "wait",
101
+ includeLogs: true,
102
+ client,
103
+ timeoutMs: normalizeTimeoutMs(timeoutSeconds),
104
+ pollIntervalMs: normalizePollIntervalMs(pollIntervalMs)
105
+ });
106
+ return toToolResult(job);
107
+ });
108
+ server.registerTool("generate_image_edit", {
109
+ title: "FAL Generate Image Edit",
110
+ description: "Edit an image using FAL image-to-image queue jobs. Returns a job id. Use fal_generation_status for updates and completion.",
111
+ inputSchema: generateImageEditSchema
112
+ }, async ({ prompt, sourceImage, modelInput, apiKey, waitForCompletion, timeoutSeconds, pollIntervalMs })=>{
113
+ const resolvedApiKey = resolveFalApiKey(apiKey);
114
+ const client = (0, _fal_ai_client__rspack_import_3.createFalClient)({
115
+ credentials: resolvedApiKey
116
+ });
117
+ const sourceImageUrl = await resolveImageSourceToFalUrl(sourceImage, client);
118
+ const payload = mergeModelInput({
119
+ prompt,
120
+ image_url: sourceImageUrl
121
+ }, modelInput);
122
+ const job = await submitFalJob({
123
+ client,
124
+ modelId: FAL_MODELS.imageEdit,
125
+ modality: "image",
126
+ toolName: "generate_image_edit",
127
+ prompt,
128
+ input: payload
129
+ });
130
+ if (waitForCompletion) return runStatusAction({
131
+ job,
132
+ action: "wait",
133
+ includeLogs: true,
134
+ client,
135
+ timeoutMs: normalizeTimeoutMs(timeoutSeconds),
136
+ pollIntervalMs: normalizePollIntervalMs(pollIntervalMs)
137
+ });
138
+ return toToolResult(job);
139
+ });
140
+ server.registerTool("generate_audio_or_music", {
141
+ title: "FAL Generate Audio Or Music",
142
+ description: "Generate audio or music through FAL queue jobs. Returns a job id. Use fal_generation_status for updates and completion.",
143
+ inputSchema: generateAudioOrMusicSchema
144
+ }, async ({ prompt, audioMode, modelInput, apiKey, waitForCompletion, timeoutSeconds, pollIntervalMs })=>{
145
+ const resolvedApiKey = resolveFalApiKey(apiKey);
146
+ const client = (0, _fal_ai_client__rspack_import_3.createFalClient)({
147
+ credentials: resolvedApiKey
148
+ });
149
+ const modelId = "sound_effect" === audioMode ? FAL_MODELS.audioSoundEffect : FAL_MODELS.audioMusic;
150
+ const payload = mergeModelInput({
151
+ prompt
152
+ }, modelInput);
153
+ const job = await submitFalJob({
154
+ client,
155
+ modelId,
156
+ modality: "audio",
157
+ toolName: "generate_audio_or_music",
158
+ prompt,
159
+ input: payload
160
+ });
161
+ if (waitForCompletion) return runStatusAction({
162
+ job,
163
+ action: "wait",
164
+ includeLogs: true,
165
+ client,
166
+ timeoutMs: normalizeTimeoutMs(timeoutSeconds),
167
+ pollIntervalMs: normalizePollIntervalMs(pollIntervalMs)
168
+ });
169
+ return toToolResult(job);
170
+ });
171
+ server.registerTool("generate_video_from_image", {
172
+ title: "FAL Generate Video From Image",
173
+ description: "Generate a video from an image using FAL queue jobs. Returns a job id. Use fal_generation_status for updates and completion.",
174
+ inputSchema: generateVideoFromImageSchema
175
+ }, async ({ prompt, sourceImage, modelInput, apiKey, waitForCompletion, timeoutSeconds, pollIntervalMs })=>{
176
+ const resolvedApiKey = resolveFalApiKey(apiKey);
177
+ const client = (0, _fal_ai_client__rspack_import_3.createFalClient)({
178
+ credentials: resolvedApiKey
179
+ });
180
+ const sourceImageUrl = await resolveImageSourceToFalUrl(sourceImage, client);
181
+ const payload = mergeModelInput({
182
+ prompt,
183
+ image_url: sourceImageUrl
184
+ }, modelInput);
185
+ const job = await submitFalJob({
186
+ client,
187
+ modelId: FAL_MODELS.videoFromImage,
188
+ modality: "video",
189
+ toolName: "generate_video_from_image",
190
+ prompt,
191
+ input: payload
192
+ });
193
+ if (waitForCompletion) return runStatusAction({
194
+ job,
195
+ action: "wait",
196
+ includeLogs: true,
197
+ client,
198
+ timeoutMs: normalizeTimeoutMs(timeoutSeconds),
199
+ pollIntervalMs: normalizePollIntervalMs(pollIntervalMs)
200
+ });
201
+ return toToolResult(job);
202
+ });
203
+ server.registerTool("fal_generation_status", {
204
+ title: "FAL Generation Status",
205
+ description: "Check, wait, cancel, or review FAL generation jobs. Use action=wait to poll until completion. Use action=accept/deny when review mode is enabled.",
206
+ inputSchema: falGenerationStatusSchema
207
+ }, async ({ jobId, action, includeLogs, timeoutSeconds, pollIntervalMs, apiKey })=>{
208
+ const existing = loadJobState(jobId);
209
+ if (!existing) throw new Error(`Unknown FAL job "${jobId}". Submit a new generation first or check the job id.`);
210
+ if ("accept" === action || "deny" === action) return runReviewDecision(existing, action);
211
+ if ("check" === action && isTerminal(existing.status)) return toToolResult(existing);
212
+ const resolvedApiKey = resolveFalApiKey(apiKey);
213
+ const client = (0, _fal_ai_client__rspack_import_3.createFalClient)({
214
+ credentials: resolvedApiKey
215
+ });
216
+ return runStatusAction({
217
+ job: existing,
218
+ action,
219
+ includeLogs,
220
+ client,
221
+ timeoutMs: normalizeTimeoutMs(timeoutSeconds),
222
+ pollIntervalMs: normalizePollIntervalMs(pollIntervalMs)
223
+ });
224
+ });
225
+ function normalizeReviewMode(raw) {
226
+ return raw?.trim().toLowerCase() === "hil" ? "hil" : "auto";
227
+ }
228
+ function parsePositiveInt(raw) {
229
+ if (!raw) return null;
230
+ const parsed = Number.parseInt(raw, 10);
231
+ return Number.isFinite(parsed) && parsed > 0 ? parsed : null;
232
+ }
233
+ function normalizeTimeoutMs(timeoutSeconds) {
234
+ if (timeoutSeconds && timeoutSeconds > 0) return 1000 * timeoutSeconds;
235
+ return FAL_DEFAULT_STATUS_TIMEOUT_MS;
236
+ }
237
+ function normalizePollIntervalMs(interval) {
238
+ if (interval && interval >= 250) return interval;
239
+ return FAL_DEFAULT_POLL_INTERVAL_MS;
240
+ }
241
+ function resolveFalApiKey(override) {
242
+ const candidate = override?.trim() || FAL_API_KEY_ENV;
243
+ if (!candidate) throw new Error("FAL API key missing. Set FAL_API_KEY (or FAL_KEY) in MCP env, or pass apiKey in tool input.");
244
+ return candidate;
245
+ }
246
+ function sanitizeJobId(value) {
247
+ const normalized = (value || "").trim();
248
+ if (!normalized) return (0, node_crypto__rspack_import_0.randomUUID)();
249
+ const sanitized = normalized.replace(/[^a-zA-Z0-9._-]/g, "_");
250
+ return sanitized.slice(0, 120) || (0, node_crypto__rspack_import_0.randomUUID)();
251
+ }
252
+ function buildJobPath(jobId) {
253
+ return (0, node_path__rspack_import_2.join)(JOBS_DIR, `${sanitizeJobId(jobId)}.json`);
254
+ }
255
+ function loadJobState(jobId) {
256
+ const normalized = sanitizeJobId(jobId);
257
+ const cached = inMemoryJobs.get(normalized);
258
+ if (cached) return cached;
259
+ const path = buildJobPath(normalized);
260
+ if (!(0, node_fs__rspack_import_1.existsSync)(path)) return null;
261
+ try {
262
+ const parsed = JSON.parse((0, node_fs__rspack_import_1.readFileSync)(path, "utf-8"));
263
+ if (!parsed || "object" != typeof parsed) return null;
264
+ inMemoryJobs.set(normalized, parsed);
265
+ return parsed;
266
+ } catch {
267
+ return null;
268
+ }
269
+ }
270
+ function saveJobState(job) {
271
+ const normalizedId = sanitizeJobId(job.jobId);
272
+ const normalized = {
273
+ ...job,
274
+ jobId: normalizedId,
275
+ updatedAt: new Date().toISOString()
276
+ };
277
+ (0, node_fs__rspack_import_1.mkdirSync)(JOBS_DIR, {
278
+ recursive: true
279
+ });
280
+ (0, node_fs__rspack_import_1.writeFileSync)(buildJobPath(normalizedId), JSON.stringify(normalized, null, 2));
281
+ inMemoryJobs.set(normalizedId, normalized);
282
+ return normalized;
283
+ }
284
+ function mapQueueStatus(status) {
285
+ switch(status){
286
+ case "IN_QUEUE":
287
+ return "in_queue";
288
+ case "IN_PROGRESS":
289
+ return "in_progress";
290
+ case "COMPLETED":
291
+ return "completed";
292
+ default:
293
+ return "failed";
294
+ }
295
+ }
296
+ function isTerminal(status) {
297
+ return "completed" === status || "failed" === status || "cancelled" === status || "awaiting_review" === status || "denied" === status;
298
+ }
299
+ function mergeModelInput(base, overrides) {
300
+ if (!overrides || "object" != typeof overrides) return base;
301
+ return {
302
+ ...base,
303
+ ...overrides
304
+ };
305
+ }
306
+ async function resolveImageSourceToFalUrl(source, client) {
307
+ const trimmed = source.trim();
308
+ if (!trimmed) throw new Error("sourceImage cannot be empty.");
309
+ if (/^https?:\/\//i.test(trimmed)) return trimmed;
310
+ const inline = parseBase64DataUrl(trimmed);
311
+ if (inline) {
312
+ if (!inline.mimeType.startsWith("image/")) throw new Error(`Expected image data URL, got ${inline.mimeType}.`);
313
+ const bytes = Buffer.from(inline.data, "base64");
314
+ if (0 === bytes.length) throw new Error("sourceImage data URL is empty.");
315
+ return uploadToFalStorage(client, bytes, inline.mimeType, "source-image");
316
+ }
317
+ const localPath = (0, _fal_runtime_js__rspack_import_7.resolveFalLocalMediaPath)(trimmed, FAL_WORKDIR);
318
+ if (!(0, node_fs__rspack_import_1.existsSync)(localPath) || !(0, node_fs__rspack_import_1.statSync)(localPath).isFile()) throw new Error(`sourceImage must be a data URL, http(s) URL, or local file path: ${source}`);
319
+ const bytes = (0, node_fs__rspack_import_1.readFileSync)(localPath);
320
+ if (0 === bytes.length) throw new Error(`sourceImage file is empty: ${localPath}`);
321
+ const mimeType = mimeTypeFromPath(localPath) || "image/png";
322
+ if (!mimeType.startsWith("image/")) throw new Error(`sourceImage must be an image file, got ${mimeType}.`);
323
+ return uploadToFalStorage(client, bytes, mimeType, (0, node_path__rspack_import_2.basename)(localPath));
324
+ }
325
+ async function uploadToFalStorage(client, bytes, mimeType, name) {
326
+ const ext = extensionFromMimeType(mimeType);
327
+ const blob = new Blob([
328
+ bytes
329
+ ], {
330
+ type: mimeType
331
+ });
332
+ const file = new File([
333
+ blob
334
+ ], `${sanitizeName(name)}.${ext}`, {
335
+ type: mimeType
336
+ });
337
+ return client.storage.upload(file, {
338
+ lifecycle: {
339
+ expiresIn: "1d"
340
+ }
341
+ });
342
+ }
343
+ async function submitFalJob(input) {
344
+ const queued = await input.client.queue.submit(input.modelId, {
345
+ input: input.input
346
+ });
347
+ const now = new Date().toISOString();
348
+ const job = {
349
+ jobId: sanitizeJobId(queued.request_id || (0, node_crypto__rspack_import_0.randomUUID)()),
350
+ requestId: queued.request_id,
351
+ toolName: input.toolName,
352
+ modelId: input.modelId,
353
+ modality: input.modality,
354
+ prompt: input.prompt,
355
+ status: mapQueueStatus(queued.status),
356
+ reviewRequired: "hil" === FAL_REVIEW_MODE,
357
+ reviewState: null,
358
+ error: null,
359
+ submittedAt: now,
360
+ updatedAt: now,
361
+ queueStatus: queued.status,
362
+ queueStatusUrl: "string" == typeof queued.status_url ? queued.status_url : null,
363
+ queueResponseUrl: "string" == typeof queued.response_url ? queued.response_url : null,
364
+ queueCancelUrl: "string" == typeof queued.cancel_url ? queued.cancel_url : null,
365
+ logs: [],
366
+ media: [],
367
+ rawResult: null
368
+ };
369
+ return saveJobState(job);
370
+ }
371
+ async function runStatusAction(input) {
372
+ let job = input.job;
373
+ if ("cancel" === input.action) {
374
+ if (isTerminal(job.status)) return toToolResult(job);
375
+ await input.client.queue.cancel(job.modelId, {
376
+ requestId: job.requestId
377
+ });
378
+ job = saveJobState({
379
+ ...job,
380
+ status: "cancelled",
381
+ error: null
382
+ });
383
+ return toToolResult(job);
384
+ }
385
+ const deadline = "wait" === input.action ? Date.now() + Math.max(1000, input.timeoutMs) : 0;
386
+ for(;;){
387
+ job = await refreshQueueStatus(job, input.client, input.includeLogs);
388
+ if ("completed" === job.status) job = await materializeCompletedJob(job, input.client);
389
+ if (isTerminal(job.status)) return toToolResult(job);
390
+ if ("wait" !== input.action) return toToolResult(job);
391
+ if (Date.now() >= deadline) return toToolResult(job);
392
+ await sleep(input.pollIntervalMs);
393
+ }
394
+ }
395
+ async function refreshQueueStatus(job, client, includeLogs) {
396
+ const status = await client.queue.status(job.modelId, {
397
+ requestId: job.requestId,
398
+ logs: includeLogs
399
+ });
400
+ const statusWithLogs = status;
401
+ const logs = includeLogs && Array.isArray(statusWithLogs.logs) ? statusWithLogs.logs.map((entry)=>"string" == typeof entry?.message ? entry.message : "").filter((entry)=>entry.trim().length > 0).slice(-FAL_DEFAULT_LOG_LIMIT) : job.logs;
402
+ return saveJobState({
403
+ ...job,
404
+ status: mapQueueStatus(status.status),
405
+ queueStatus: status.status,
406
+ queueStatusUrl: "string" == typeof status.status_url ? status.status_url : null,
407
+ queueResponseUrl: "string" == typeof status.response_url ? status.response_url : null,
408
+ queueCancelUrl: "string" == typeof status.cancel_url ? status.cancel_url : null,
409
+ logs
410
+ });
411
+ }
412
+ async function materializeCompletedJob(job, client) {
413
+ if (job.rawResult && job.media.length > 0) {
414
+ if (job.reviewRequired && "pending" === job.reviewState) return saveJobState({
415
+ ...job,
416
+ status: "awaiting_review"
417
+ });
418
+ return saveJobState({
419
+ ...job,
420
+ status: "completed"
421
+ });
422
+ }
423
+ try {
424
+ const result = await client.queue.result(job.modelId, {
425
+ requestId: job.requestId
426
+ });
427
+ const rawData = result?.data ?? null;
428
+ const candidates = extractMediaCandidates(rawData, job.modality);
429
+ const media = candidates.length > 0 ? await materializeMediaCandidates(job, candidates) : [];
430
+ const nextStatus = job.reviewRequired && media.length > 0 ? "awaiting_review" : "completed";
431
+ const reviewState = job.reviewRequired && media.length > 0 ? "pending" : media.length > 0 ? "accepted" : null;
432
+ return saveJobState({
433
+ ...job,
434
+ status: nextStatus,
435
+ reviewState,
436
+ error: null,
437
+ media,
438
+ rawResult: rawData
439
+ });
440
+ } catch (error) {
441
+ return saveJobState({
442
+ ...job,
443
+ status: "failed",
444
+ error: errorToString(error)
445
+ });
446
+ }
447
+ }
448
+ async function materializeMediaCandidates(job, candidates) {
449
+ const destinationRoot = job.reviewRequired ? (0, node_path__rspack_import_2.join)(PENDING_DIR, job.jobId) : resolveFinalOutputDir(job.modality);
450
+ (0, node_fs__rspack_import_1.mkdirSync)(destinationRoot, {
451
+ recursive: true
452
+ });
453
+ const records = [];
454
+ for (const [index, candidate] of candidates.entries()){
455
+ let bytes;
456
+ let mimeType;
457
+ try {
458
+ const downloaded = await downloadRemoteFile(candidate.url);
459
+ bytes = downloaded.bytes;
460
+ mimeType = normalizeMimeType(downloaded.mimeType) || candidate.mimeType || "application/octet-stream";
461
+ } catch {
462
+ continue;
463
+ }
464
+ const extension = extensionFromMimeType(mimeType) || extensionFromUrl(candidate.url);
465
+ const hash = (0, node_crypto__rspack_import_0.createHash)("sha256").update(bytes).digest("hex").slice(0, 12);
466
+ const filename = `${Date.now()}-${index + 1}-${hash}.${extension || "bin"}`;
467
+ const outputPath = (0, node_path__rspack_import_2.join)(destinationRoot, filename);
468
+ (0, node_fs__rspack_import_1.writeFileSync)(outputPath, bytes);
469
+ records.push({
470
+ id: `${job.jobId}-${index + 1}`,
471
+ modality: classifyModality(candidate, mimeType),
472
+ mimeType,
473
+ remoteUrl: candidate.url,
474
+ path: outputPath,
475
+ name: candidate.label || filename,
476
+ sizeBytes: bytes.length,
477
+ createdAt: new Date().toISOString()
478
+ });
479
+ }
480
+ return records;
481
+ }
482
+ function resolveFinalOutputDir(modality) {
483
+ const folder = "image" === modality ? "images" : "audio" === modality ? "audio" : "video" === modality ? "video" : "files";
484
+ const dir = (0, node_path__rspack_import_2.join)(FAL_OUTPUT_DIR, folder);
485
+ (0, node_fs__rspack_import_1.mkdirSync)(dir, {
486
+ recursive: true
487
+ });
488
+ return dir;
489
+ }
490
+ function runReviewDecision(job, decision) {
491
+ if (true !== job.reviewRequired || "pending" !== job.reviewState) throw new Error(`Job ${job.jobId} is not waiting for review. Current reviewState=${job.reviewState || "none"}.`);
492
+ if ("deny" === decision) {
493
+ for (const media of job.media)if (media.path) try {
494
+ (0, node_fs__rspack_import_1.unlinkSync)(media.path);
495
+ } catch {}
496
+ try {
497
+ (0, node_fs__rspack_import_1.rmSync)((0, node_path__rspack_import_2.join)(PENDING_DIR, job.jobId), {
498
+ recursive: true,
499
+ force: true
500
+ });
501
+ } catch {}
502
+ const denied = saveJobState({
503
+ ...job,
504
+ status: "denied",
505
+ reviewState: "denied",
506
+ media: job.media.map((entry)=>({
507
+ ...entry,
508
+ path: null
509
+ }))
510
+ });
511
+ return toToolResult(denied);
512
+ }
513
+ const acceptedMedia = job.media.map((entry)=>{
514
+ if (!entry.path) return entry;
515
+ const targetDir = resolveFinalOutputDir(entry.modality);
516
+ const targetPath = (0, node_path__rspack_import_2.join)(targetDir, (0, node_path__rspack_import_2.basename)(entry.path));
517
+ moveFile(entry.path, targetPath);
518
+ return {
519
+ ...entry,
520
+ path: targetPath
521
+ };
522
+ });
523
+ try {
524
+ (0, node_fs__rspack_import_1.rmSync)((0, node_path__rspack_import_2.join)(PENDING_DIR, job.jobId), {
525
+ recursive: true,
526
+ force: true
527
+ });
528
+ } catch {}
529
+ const accepted = saveJobState({
530
+ ...job,
531
+ status: "completed",
532
+ reviewState: "accepted",
533
+ media: acceptedMedia
534
+ });
535
+ return toToolResult(accepted);
536
+ }
537
+ function moveFile(sourcePath, targetPath) {
538
+ try {
539
+ (0, node_fs__rspack_import_1.renameSync)(sourcePath, targetPath);
540
+ return;
541
+ } catch {
542
+ (0, node_fs__rspack_import_1.copyFileSync)(sourcePath, targetPath);
543
+ (0, node_fs__rspack_import_1.unlinkSync)(sourcePath);
544
+ }
545
+ }
546
+ function extractMediaCandidates(payload, fallbackModality) {
547
+ const output = [];
548
+ const seen = new Set();
549
+ const walk = (value, path, parent)=>{
550
+ if (!value) return;
551
+ if (Array.isArray(value)) {
552
+ for (const item of value)walk(item, path, parent);
553
+ return;
554
+ }
555
+ if ("string" == typeof value) {
556
+ const trimmed = value.trim();
557
+ if (!/^https?:\/\//i.test(trimmed)) return;
558
+ if (seen.has(trimmed)) return;
559
+ const hint = path[path.length - 1] || "";
560
+ const mimeType = inferMimeTypeFromContext(trimmed, parent);
561
+ if (!looksLikeMediaCandidate(trimmed, hint, mimeType)) return;
562
+ seen.add(trimmed);
563
+ output.push({
564
+ url: trimmed,
565
+ mimeType,
566
+ modality: inferModalityFromHint(hint, mimeType, fallbackModality),
567
+ label: buildCandidateLabel(path)
568
+ });
569
+ return;
570
+ }
571
+ if ("object" != typeof value) return;
572
+ const record = value;
573
+ for (const [key, nested] of Object.entries(record))walk(nested, [
574
+ ...path,
575
+ key
576
+ ], record);
577
+ };
578
+ walk(payload, [], null);
579
+ return output;
580
+ }
581
+ function looksLikeMediaCandidate(url, hint, mimeType) {
582
+ if (mimeType) {
583
+ if (mimeType.startsWith("image/") || mimeType.startsWith("audio/") || mimeType.startsWith("video/")) return true;
584
+ }
585
+ const normalizedHint = hint.toLowerCase();
586
+ for (const fragment of [
587
+ "image",
588
+ "audio",
589
+ "music",
590
+ "video",
591
+ "file",
592
+ "media"
593
+ ])if (normalizedHint.includes(fragment)) return true;
594
+ const extension = extensionFromUrl(url).toLowerCase();
595
+ return Boolean(mimeTypeFromExtension(extension));
596
+ }
597
+ function buildCandidateLabel(path) {
598
+ if (0 === path.length) return;
599
+ return sanitizeName(path.join("-")) || void 0;
600
+ }
601
+ function inferMimeTypeFromContext(url, parent) {
602
+ if (parent) for (const key of [
603
+ "mime_type",
604
+ "mimeType",
605
+ "content_type",
606
+ "contentType"
607
+ ]){
608
+ const value = parent[key];
609
+ if ("string" == typeof value && value.trim()) return normalizeMimeType(value);
610
+ }
611
+ const extension = extensionFromUrl(url);
612
+ if (!extension) return;
613
+ return mimeTypeFromExtension(extension) || void 0;
614
+ }
615
+ function inferModalityFromHint(hint, mimeType, fallback) {
616
+ const normalizedHint = hint.toLowerCase();
617
+ if (mimeType) {
618
+ if (mimeType.startsWith("image/")) return "image";
619
+ if (mimeType.startsWith("audio/")) return "audio";
620
+ if (mimeType.startsWith("video/")) return "video";
621
+ }
622
+ if (normalizedHint.includes("image")) return "image";
623
+ if (normalizedHint.includes("audio") || normalizedHint.includes("music")) return "audio";
624
+ if (normalizedHint.includes("video")) return "video";
625
+ return fallback;
626
+ }
627
+ function classifyModality(candidate, mimeType) {
628
+ if (mimeType.startsWith("image/")) return "image";
629
+ if (mimeType.startsWith("audio/")) return "audio";
630
+ if (mimeType.startsWith("video/")) return "video";
631
+ return candidate.modality;
632
+ }
633
+ async function downloadRemoteFile(url) {
634
+ const response = await fetchWithTimeout(url, {
635
+ method: "GET",
636
+ headers: {
637
+ "User-Agent": "wingman-mcp-fal-ai"
638
+ }
639
+ });
640
+ if (!response.ok) throw new Error(`Failed to download media (${response.status}): ${url}`);
641
+ const mimeType = normalizeMimeType(response.headers.get("content-type") || "");
642
+ const arrayBuffer = await response.arrayBuffer();
643
+ const bytes = Buffer.from(arrayBuffer);
644
+ if (0 === bytes.length) throw new Error(`Downloaded media is empty: ${url}`);
645
+ return {
646
+ bytes,
647
+ mimeType
648
+ };
649
+ }
650
+ function parseBase64DataUrl(dataUrl) {
651
+ const match = dataUrl.match(/^data:([^;,]+);base64,(.+)$/i);
652
+ if (!match) return null;
653
+ return {
654
+ mimeType: normalizeMimeType(match[1]),
655
+ data: match[2].trim()
656
+ };
657
+ }
658
+ function extensionFromUrl(url) {
659
+ try {
660
+ const parsed = new URL(url);
661
+ const extension = (0, node_path__rspack_import_2.extname)(parsed.pathname || "").replace(/^\./, "").toLowerCase();
662
+ return extension;
663
+ } catch {
664
+ return "";
665
+ }
666
+ }
667
+ function normalizeMimeType(raw) {
668
+ return raw.split(";")[0]?.trim().toLowerCase() || "";
669
+ }
670
+ function mimeTypeFromExtension(extension) {
671
+ switch(extension.replace(/^\./, "").toLowerCase()){
672
+ case "png":
673
+ return "image/png";
674
+ case "jpg":
675
+ case "jpeg":
676
+ return "image/jpeg";
677
+ case "webp":
678
+ return "image/webp";
679
+ case "gif":
680
+ return "image/gif";
681
+ case "bmp":
682
+ return "image/bmp";
683
+ case "tif":
684
+ case "tiff":
685
+ return "image/tiff";
686
+ case "avif":
687
+ return "image/avif";
688
+ case "mp3":
689
+ return "audio/mpeg";
690
+ case "wav":
691
+ return "audio/wav";
692
+ case "ogg":
693
+ return "audio/ogg";
694
+ case "m4a":
695
+ return "audio/mp4";
696
+ case "webm":
697
+ return "video/webm";
698
+ case "mp4":
699
+ return "video/mp4";
700
+ case "mov":
701
+ return "video/quicktime";
702
+ default:
703
+ return null;
704
+ }
705
+ }
706
+ function mimeTypeFromPath(pathname) {
707
+ return mimeTypeFromExtension((0, node_path__rspack_import_2.extname)(pathname).replace(/^\./, ""));
708
+ }
709
+ function extensionFromMimeType(mimeType) {
710
+ switch(normalizeMimeType(mimeType)){
711
+ case "image/jpeg":
712
+ return "jpg";
713
+ case "image/png":
714
+ return "png";
715
+ case "image/webp":
716
+ return "webp";
717
+ case "image/gif":
718
+ return "gif";
719
+ case "image/bmp":
720
+ return "bmp";
721
+ case "image/tiff":
722
+ return "tiff";
723
+ case "image/avif":
724
+ return "avif";
725
+ case "audio/mpeg":
726
+ return "mp3";
727
+ case "audio/wav":
728
+ return "wav";
729
+ case "audio/ogg":
730
+ return "ogg";
731
+ case "audio/mp4":
732
+ return "m4a";
733
+ case "video/mp4":
734
+ return "mp4";
735
+ case "video/webm":
736
+ return "webm";
737
+ case "video/quicktime":
738
+ return "mov";
739
+ default:
740
+ {
741
+ const subtype = normalizeMimeType(mimeType).split("/")[1] || "";
742
+ const sanitized = subtype.replace(/[^a-z0-9]/g, "");
743
+ return sanitized || "bin";
744
+ }
745
+ }
746
+ }
747
+ function sanitizeName(value) {
748
+ const normalized = (value || "").trim().toLowerCase();
749
+ const sanitized = normalized.replace(/[^a-z0-9._-]+/g, "-").replace(/^-+|-+$/g, "");
750
+ return sanitized.slice(0, 80) || "asset";
751
+ }
752
+ function toGatewayFileUrl(pathname) {
753
+ return `/api/fs/file?path=${encodeURIComponent(pathname)}`;
754
+ }
755
+ function buildStructuredImages(media) {
756
+ return media.filter((entry)=>"image" === entry.modality).map((entry)=>({
757
+ path: entry.path,
758
+ url: entry.path ? toGatewayFileUrl(entry.path) : entry.remoteUrl,
759
+ mimeType: entry.mimeType,
760
+ name: entry.name
761
+ }));
762
+ }
763
+ function toToolResult(job) {
764
+ const summary = (0, _fal_summary_js__rspack_import_8.buildFalGenerationSummary)({
765
+ toolName: job.toolName,
766
+ jobId: job.jobId,
767
+ status: job.status,
768
+ modelId: job.modelId,
769
+ reviewState: job.reviewState,
770
+ media: job.media.map((entry)=>({
771
+ modality: entry.modality,
772
+ path: entry.path,
773
+ remoteUrl: entry.remoteUrl,
774
+ mimeType: entry.mimeType
775
+ })),
776
+ error: job.error,
777
+ cwd: FAL_WORKDIR
778
+ });
779
+ const content = [
780
+ {
781
+ type: "text",
782
+ text: summary
783
+ }
784
+ ];
785
+ for (const media of job.media)if ("image" === media.modality && media.path) try {
786
+ const bytes = (0, node_fs__rspack_import_1.readFileSync)(media.path);
787
+ if (bytes.length > 0 && bytes.length <= 5242880) content.push({
788
+ type: "image",
789
+ data: bytes.toString("base64"),
790
+ mimeType: media.mimeType || "image/png"
791
+ });
792
+ } catch {}
793
+ return {
794
+ content,
795
+ structuredContent: {
796
+ jobId: job.jobId,
797
+ requestId: job.requestId,
798
+ toolName: job.toolName,
799
+ modelId: job.modelId,
800
+ modality: job.modality,
801
+ status: job.status,
802
+ reviewRequired: job.reviewRequired,
803
+ reviewState: job.reviewState,
804
+ error: job.error,
805
+ submittedAt: job.submittedAt,
806
+ updatedAt: job.updatedAt,
807
+ queueStatus: job.queueStatus,
808
+ queueStatusUrl: job.queueStatusUrl,
809
+ queueResponseUrl: job.queueResponseUrl,
810
+ queueCancelUrl: job.queueCancelUrl,
811
+ logs: job.logs,
812
+ images: buildStructuredImages(job.media),
813
+ media: job.media.map((entry)=>({
814
+ id: entry.id,
815
+ modality: entry.modality,
816
+ mimeType: entry.mimeType,
817
+ name: entry.name,
818
+ path: entry.path,
819
+ url: entry.path ? toGatewayFileUrl(entry.path) : entry.remoteUrl,
820
+ remoteUrl: entry.remoteUrl,
821
+ sizeBytes: entry.sizeBytes,
822
+ createdAt: entry.createdAt
823
+ }))
824
+ }
825
+ };
826
+ }
827
+ async function fetchWithTimeout(url, init, timeoutMs = FAL_HTTP_TIMEOUT_MS) {
828
+ const controller = new AbortController();
829
+ const timeout = setTimeout(()=>controller.abort(), Math.max(1000, timeoutMs));
830
+ try {
831
+ return await fetch(url, {
832
+ ...init,
833
+ signal: controller.signal
834
+ });
835
+ } finally{
836
+ clearTimeout(timeout);
837
+ }
838
+ }
839
+ function sleep(ms) {
840
+ return new Promise((resolve)=>setTimeout(resolve, ms));
841
+ }
842
+ function errorToString(error) {
843
+ if (error instanceof Error) return error.message;
844
+ if ("string" == typeof error) return error;
845
+ try {
846
+ return JSON.stringify(error);
847
+ } catch {
848
+ return String(error);
849
+ }
850
+ }
851
+ (0, node_fs__rspack_import_1.mkdirSync)(FAL_STATE_DIR, {
852
+ recursive: true
853
+ });
854
+ (0, node_fs__rspack_import_1.mkdirSync)(FAL_OUTPUT_DIR, {
855
+ recursive: true
856
+ });
857
+ (0, node_fs__rspack_import_1.mkdirSync)(JOBS_DIR, {
858
+ recursive: true
859
+ });
860
+ (0, node_fs__rspack_import_1.mkdirSync)(PENDING_DIR, {
861
+ recursive: true
862
+ });
863
+ const transport = new _modelcontextprotocol_sdk_server_stdio_js__rspack_import_5.StdioServerTransport();
864
+ await server.connect(transport);
865
+ console.error([
866
+ "wingman-mcp-fal-ai ready",
867
+ `workdir=${FAL_WORKDIR}`,
868
+ `stateDir=${FAL_STATE_DIR}`,
869
+ `outputDir=${FAL_OUTPUT_DIR}`,
870
+ `reviewMode=${FAL_REVIEW_MODE}`,
871
+ `defaultImageModel=${FAL_MODELS.imageOrTexture}`
872
+ ].join(" | "));
873
+ __rspack_async_done();
874
+ } catch (e) {
875
+ __rspack_async_done(e);
876
+ }
877
+ }, 1);
878
+ },
879
+ "./fal/runtime.js" (module) {
880
+ module.exports = require("./fal/runtime.cjs");
881
+ },
882
+ "./fal/summary.js" (module) {
883
+ module.exports = require("./fal/summary.cjs");
884
+ },
885
+ "@fal-ai/client" (module) {
886
+ module.exports = require("@fal-ai/client");
887
+ },
888
+ "@modelcontextprotocol/sdk/server/mcp.js" (module) {
889
+ module.exports = require("@modelcontextprotocol/sdk/server/mcp.js");
890
+ },
891
+ "@modelcontextprotocol/sdk/server/stdio.js" (module) {
892
+ module.exports = require("@modelcontextprotocol/sdk/server/stdio.js");
893
+ },
894
+ "node:crypto" (module) {
895
+ module.exports = require("node:crypto");
896
+ },
897
+ "node:fs" (module) {
898
+ module.exports = require("node:fs");
899
+ },
900
+ "node:path" (module) {
901
+ module.exports = require("node:path");
902
+ },
903
+ zod (module) {
904
+ module.exports = require("zod");
905
+ }
906
+ };
907
+ var __webpack_module_cache__ = {};
908
+ function __webpack_require__(moduleId) {
909
+ var cachedModule = __webpack_module_cache__[moduleId];
910
+ if (void 0 !== cachedModule) return cachedModule.exports;
911
+ var module = __webpack_module_cache__[moduleId] = {
912
+ exports: {}
913
+ };
914
+ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
915
+ return module.exports;
916
+ }
917
+ (()=>{
918
+ var hasSymbol = "function" == typeof Symbol;
919
+ var rspackQueues = hasSymbol ? Symbol("rspack queues") : "__rspack_queues";
920
+ var rspackExports = __webpack_require__.aE = hasSymbol ? Symbol("rspack exports") : "__webpack_exports__";
921
+ var rspackError = hasSymbol ? Symbol("rspack error") : "__rspack_error";
922
+ var rspackDone = hasSymbol ? Symbol("rspack done") : "__rspack_done";
923
+ var rspackDefer = __webpack_require__.zS = hasSymbol ? Symbol("rspack defer") : "__rspack_defer";
924
+ var resolveQueue = (queue)=>{
925
+ if (queue && queue.d < 1) {
926
+ queue.d = 1;
927
+ queue.forEach((fn)=>fn.r--);
928
+ queue.forEach((fn)=>fn.r-- ? fn.r++ : fn());
929
+ }
930
+ };
931
+ var wrapDeps = (deps)=>deps.map((dep)=>{
932
+ if (null !== dep && "object" == typeof dep) {
933
+ if (!dep[rspackQueues] && dep[rspackDefer]) {
934
+ var asyncDeps = dep[rspackDefer];
935
+ var hasUnresolvedAsyncSubgraph = asyncDeps.some((id)=>{
936
+ var cache = __webpack_module_cache__[id];
937
+ return !cache || false === cache[rspackDone];
938
+ });
939
+ if (!hasUnresolvedAsyncSubgraph) return dep;
940
+ var d = dep;
941
+ dep = {
942
+ then (callback) {
943
+ Promise.all(asyncDeps.map(__webpack_require__)).then(()=>callback(d));
944
+ }
945
+ };
946
+ }
947
+ if (dep[rspackQueues]) return dep;
948
+ if (dep.then) {
949
+ var queue = [];
950
+ queue.d = 0;
951
+ dep.then((r)=>{
952
+ obj[rspackExports] = r;
953
+ resolveQueue(queue);
954
+ }, (e)=>{
955
+ obj[rspackError] = e;
956
+ resolveQueue(queue);
957
+ });
958
+ var obj = {};
959
+ obj[rspackDefer] = false;
960
+ obj[rspackQueues] = (fn)=>fn(queue);
961
+ return obj;
962
+ }
963
+ }
964
+ var ret = {};
965
+ ret[rspackQueues] = ()=>{};
966
+ ret[rspackExports] = dep;
967
+ return ret;
968
+ });
969
+ __webpack_require__.a = (module, body, hasAwait)=>{
970
+ var queue;
971
+ hasAwait && ((queue = []).d = -1);
972
+ var depQueues = new Set();
973
+ var exports1 = module.exports;
974
+ var currentDeps;
975
+ var outerResolve;
976
+ var reject;
977
+ var promise = new Promise((resolve, rej)=>{
978
+ reject = rej;
979
+ outerResolve = resolve;
980
+ });
981
+ promise[rspackExports] = exports1;
982
+ promise[rspackQueues] = (fn)=>{
983
+ queue && fn(queue), depQueues.forEach(fn), promise["catch"](()=>{});
984
+ };
985
+ module.exports = promise;
986
+ var handle = (deps)=>{
987
+ currentDeps = wrapDeps(deps);
988
+ var fn;
989
+ var getResult = ()=>currentDeps.map((d)=>{
990
+ if (d[rspackDefer]) return d;
991
+ if (d[rspackError]) throw d[rspackError];
992
+ return d[rspackExports];
993
+ });
994
+ var promise = new Promise((resolve)=>{
995
+ fn = ()=>resolve(getResult);
996
+ fn.r = 0;
997
+ var fnQueue = (q)=>q !== queue && !depQueues.has(q) && (depQueues.add(q), q && !q.d && (fn.r++, q.push(fn)));
998
+ currentDeps.map((dep)=>dep[rspackDefer] || dep[rspackQueues](fnQueue));
999
+ });
1000
+ return fn.r ? promise : getResult();
1001
+ };
1002
+ var done = (err)=>(err ? reject(promise[rspackError] = err) : outerResolve(exports1), resolveQueue(queue), promise[rspackDone] = true);
1003
+ body(handle, done);
1004
+ queue && queue.d < 0 && (queue.d = 0);
1005
+ };
1006
+ })();
1007
+ (()=>{
1008
+ __webpack_require__.n = (module)=>{
1009
+ var getter = module && module.__esModule ? ()=>module['default'] : ()=>module;
1010
+ __webpack_require__.d(getter, {
1011
+ a: getter
1012
+ });
1013
+ return getter;
1014
+ };
1015
+ })();
1016
+ (()=>{
1017
+ __webpack_require__.d = (exports1, definition)=>{
1018
+ for(var key in definition)if (__webpack_require__.o(definition, key) && !__webpack_require__.o(exports1, key)) Object.defineProperty(exports1, key, {
1019
+ enumerable: true,
1020
+ get: definition[key]
1021
+ });
1022
+ };
1023
+ })();
1024
+ (()=>{
1025
+ __webpack_require__.o = (obj, prop)=>Object.prototype.hasOwnProperty.call(obj, prop);
1026
+ })();
1027
+ (()=>{
1028
+ __webpack_require__.r = (exports1)=>{
1029
+ if ("u" > typeof Symbol && Symbol.toStringTag) Object.defineProperty(exports1, Symbol.toStringTag, {
1030
+ value: 'Module'
1031
+ });
1032
+ Object.defineProperty(exports1, '__esModule', {
1033
+ value: true
1034
+ });
1035
+ };
1036
+ })();
1037
+ var __webpack_exports__ = __webpack_require__("./src/tools/mcp-fal-ai.ts");
1038
+ for(var __rspack_i in __webpack_exports__)exports[__rspack_i] = __webpack_exports__[__rspack_i];
1039
+ Object.defineProperty(exports, '__esModule', {
1040
+ value: true
1041
+ });