@mindstudio-ai/local-model-tunnel 0.5.7 → 0.5.8

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mindstudio-ai/local-model-tunnel",
3
- "version": "0.5.7",
3
+ "version": "0.5.8",
4
4
  "description": "Run local AI models with MindStudio",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,6 +11,7 @@
11
11
  "build": "tsup",
12
12
  "dev": "tsup && node dist/cli.js",
13
13
  "start": "node dist/cli.js",
14
+ "prepare": "npm run build",
14
15
  "prepublishOnly": "npm run build",
15
16
  "local-update": "npm run build && npm link",
16
17
  "clean:destructive": "bash scripts/clean-destructive.sh"
@@ -34,6 +35,7 @@
34
35
  "url": "https://github.com/mindstudio-ai/mindstudio-local-model-tunnel"
35
36
  },
36
37
  "dependencies": {
38
+ "esbuild": "^0.24.0",
37
39
  "chalk": "^5.3.0",
38
40
  "commander": "^12.0.0",
39
41
  "conf": "^12.0.0",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/config.ts","../src/api.ts","../src/providers/ollama/index.ts","../src/providers/utils.ts","../src/providers/ollama/readme.md","../src/providers/lmstudio/index.ts","../src/providers/lmstudio/readme.md","../src/providers/stable-diffusion/index.ts","../src/providers/stable-diffusion/readme.md","../src/providers/comfyui/index.ts","../src/providers/comfyui/workflow-discovery.ts","../src/providers/comfyui/converter-install.ts","../src/providers/comfyui/workflow-executor.ts","../src/providers/comfyui/readme.md","../src/providers/index.ts","../src/events.ts","../src/runner.ts"],"sourcesContent":["import Conf from 'conf';\nimport os from 'node:os';\nimport path from 'node:path';\n\nexport type Environment = 'prod' | 'local';\n\ninterface EnvironmentConfig {\n apiKey?: string;\n userId?: string;\n apiBaseUrl: string;\n}\n\ninterface ConfigSchema {\n environment: Environment;\n providerBaseUrls: Record<string, string>;\n providerInstallPaths: Record<string, string>;\n localInterfaces: Record<string, string>;\n environments: {\n prod: EnvironmentConfig;\n local: EnvironmentConfig;\n };\n}\n\nexport const config = new Conf<ConfigSchema>({\n projectName: 'mindstudio-local',\n cwd: path.join(os.homedir(), '.mindstudio-local-tunnel'),\n configName: 'config',\n defaults: {\n environment: 'prod',\n providerBaseUrls: {},\n providerInstallPaths: {},\n localInterfaces: {},\n environments: {\n prod: {\n apiBaseUrl: 'https://api.mindstudio.ai',\n },\n local: {\n apiBaseUrl: 'http://localhost:3129',\n },\n },\n },\n});\n\n// Environment management\nexport function getEnvironment(): Environment {\n return config.get('environment');\n}\n\nexport function setEnvironment(env: Environment): void {\n config.set('environment', env);\n}\n\n// Get config for current environment\nfunction getEnvConfig(): EnvironmentConfig {\n const env = getEnvironment();\n return config.get(`environments.${env}`) as EnvironmentConfig;\n}\n\nfunction setEnvConfig(key: keyof EnvironmentConfig, value: string): void {\n const env = getEnvironment();\n config.set(`environments.${env}.${key}`, value);\n}\n\n// API Key (per environment)\nexport function getApiKey(): string | undefined {\n return getEnvConfig().apiKey;\n}\n\nexport function setApiKey(key: string): void {\n setEnvConfig('apiKey', key);\n}\n\nexport function clearApiKey(): void {\n const env = getEnvironment();\n config.delete(`environments.${env}.apiKey` as keyof ConfigSchema);\n}\n\n// User ID (per environment)\nexport function getUserId(): string | undefined {\n return getEnvConfig().userId;\n}\n\nexport function setUserId(id: string): void {\n setEnvConfig('userId', id);\n}\n\nexport function clearUserId(): void {\n const env = getEnvironment();\n config.delete(`environments.${env}.userId` as keyof ConfigSchema);\n}\n\n// API Base URL (per environment)\nexport function getApiBaseUrl(): string {\n return getEnvConfig().apiBaseUrl;\n}\n\nexport function setApiBaseUrl(url: string): void {\n setEnvConfig('apiBaseUrl', url);\n}\n\nexport function getConfigPath(): string {\n return config.path;\n}\n\n// Provider helpers\nexport function getProviderBaseUrl(name: string, defaultUrl: string): string {\n const urls = config.get('providerBaseUrls');\n return urls[name] ?? defaultUrl;\n}\n\nexport function setProviderBaseUrl(name: string, url: string): void {\n const urls = config.get('providerBaseUrls');\n urls[name] = url;\n config.set('providerBaseUrls', urls);\n}\n\nexport function getProviderInstallPath(name: string): string | undefined {\n const paths = config.get('providerInstallPaths');\n return paths[name];\n}\n\nexport function setProviderInstallPath(\n name: string,\n installPath: string,\n): void {\n const paths = config.get('providerInstallPaths');\n paths[name] = installPath;\n config.set('providerInstallPaths', paths);\n}\n\n// Local interface helpers\nexport function getLocalInterfacesDir(): string {\n return path.join(os.homedir(), '.mindstudio-local-tunnel', 'interfaces');\n}\n\nexport function getLocalInterfacePath(key: string): string | undefined {\n const interfaces = config.get('localInterfaces');\n return interfaces[key];\n}\n\nexport function setLocalInterfacePath(key: string, dirPath: string): void {\n const interfaces = config.get('localInterfaces');\n interfaces[key] = dirPath;\n config.set('localInterfaces', interfaces);\n}\n\nexport function deleteLocalInterfacePath(key: string): void {\n const interfaces = config.get('localInterfaces');\n delete interfaces[key];\n config.set('localInterfaces', interfaces);\n}\n\n// Get all environment info for display\nexport function getEnvironmentInfo(): {\n current: Environment;\n apiBaseUrl: string;\n hasApiKey: boolean;\n} {\n const env = getEnvironment();\n const envConfig = getEnvConfig();\n return {\n current: env,\n apiBaseUrl: envConfig.apiBaseUrl,\n hasApiKey: !!envConfig.apiKey,\n };\n}\n","import { getApiKey, getApiBaseUrl, getUserId } from './config';\n\nexport interface LocalModelRequest {\n id: string;\n organizationId: string;\n modelId: string;\n requestType: 'llm_chat' | 'image_generation' | 'video_generation';\n payload: {\n messages?: Array<{ role: string; content: string }>;\n prompt?: string;\n temperature?: number;\n maxTokens?: number;\n config?: Record<string, unknown>;\n };\n createdAt: number;\n}\n\nfunction getHeaders(): Record<string, string> {\n const apiKey = getApiKey();\n if (!apiKey) {\n throw new Error('Not authenticated. Run: mindstudio-local auth');\n }\n\n const headers: Record<string, string> = {\n Authorization: `Bearer ${apiKey}`,\n 'Content-Type': 'application/json',\n };\n\n const userId = getUserId();\n if (userId) {\n headers['x-user-id'] = userId;\n }\n\n return headers;\n}\n\nexport async function pollForRequest(\n modelIds: string[],\n): Promise<LocalModelRequest | null> {\n const baseUrl = getApiBaseUrl();\n const modelIdsParam = modelIds.join(',');\n\n const response = await fetch(\n `${baseUrl}/v1/local-models/poll?modelIds=${encodeURIComponent(modelIdsParam)}`,\n {\n method: 'GET',\n headers: getHeaders(),\n },\n );\n\n if (response.status === 204) {\n return null;\n }\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Poll failed: ${response.status} ${error}`);\n }\n const data = (await response.json()) as { request: LocalModelRequest };\n return data.request;\n}\n\n/**\n * Submit a progress update for a running request.\n * @param type - 'chunk' for streaming text content, 'log' for raw log lines\n */\nexport async function submitProgress(\n requestId: string,\n content: string,\n type: 'chunk' | 'log' = 'chunk',\n): Promise<void> {\n const baseUrl = getApiBaseUrl();\n\n const response = await fetch(\n `${baseUrl}/v1/local-models/requests/${requestId}/progress`,\n {\n method: 'POST',\n headers: getHeaders(),\n body: JSON.stringify({ type, content }),\n },\n );\n\n if (!response.ok) {\n console.warn(`Progress update failed: ${response.status}`);\n }\n}\n\n/**\n * Result for text/chat completions\n */\nexport interface TextResult {\n content?: string;\n usage?: { promptTokens: number; completionTokens: number };\n}\n\n/**\n * Result for image generation\n */\nexport interface ImageResult {\n /** Base64-encoded image data */\n imageBase64: string;\n /** MIME type (e.g., \"image/png\") */\n mimeType: string;\n /** Seed used for generation */\n seed?: number;\n}\n\n/**\n * Result for video generation\n */\nexport interface VideoResult {\n /** Base64-encoded video data */\n videoBase64: string;\n /** MIME type (e.g., \"video/webp\", \"video/mp4\") */\n mimeType: string;\n /** Duration in seconds */\n duration?: number;\n /** Frames per second */\n fps?: number;\n /** Seed used for generation */\n seed?: number;\n}\n\n/**\n * Combined result type\n */\nexport type RequestResult = TextResult | ImageResult | VideoResult;\n\nexport async function submitResult(\n requestId: string,\n success: boolean,\n result?: RequestResult,\n error?: string,\n): Promise<void> {\n const baseUrl = getApiBaseUrl();\n\n const response = await fetch(\n `${baseUrl}/v1/local-models/requests/${requestId}/result`,\n {\n method: 'POST',\n headers: getHeaders(),\n body: JSON.stringify({ success, result, error }),\n },\n );\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Result submission failed: ${response.status} ${errorText}`,\n );\n }\n}\n\nexport async function verifyApiKey(): Promise<boolean> {\n const baseUrl = getApiBaseUrl();\n\n try {\n const response = await fetch(`${baseUrl}/v1/local-models/verify-api-key`, {\n method: 'GET',\n headers: getHeaders(),\n });\n\n return response.status === 204 || response.ok;\n } catch {\n return false;\n }\n}\n\nexport type ModelTypeMindStudio =\n | 'llm_chat'\n | 'image_generation'\n | 'video_generation';\n\nexport interface SyncModelEntry {\n name: string;\n provider: string;\n type: ModelTypeMindStudio;\n parameters?: unknown[];\n}\n\nexport async function syncModels(models: SyncModelEntry[]): Promise<void> {\n const baseUrl = getApiBaseUrl();\n\n const response = await fetch(`${baseUrl}/v1/local-models/models/sync`, {\n method: 'POST',\n headers: getHeaders(),\n body: JSON.stringify({ models }),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(`Sync failed: ${response.status} ${errorText}`);\n }\n}\n\nexport interface SyncedModel {\n id: string;\n name: string;\n}\n\nexport async function getSyncedModels(): Promise<SyncedModel[]> {\n const baseUrl = getApiBaseUrl();\n\n const response = await fetch(`${baseUrl}/v1/local-models/models`, {\n method: 'GET',\n headers: getHeaders(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch synced models: ${response.status} ${errorText}`,\n );\n }\n\n const data = (await response.json()) as { models: SyncedModel[] };\n return data.models;\n}\n\nexport async function requestDeviceAuth(): Promise<{\n url: string;\n token: string;\n}> {\n const baseUrl = getApiBaseUrl();\n\n const response = await fetch(`${baseUrl}/developer/v2/request-auth-url`, {\n method: 'GET',\n headers: { 'Content-Type': 'application/json' },\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Device auth request failed: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n url: string;\n token: string;\n };\n\n return data;\n}\n\nexport async function pollDeviceAuth(token: string): Promise<{\n status: 'pending' | 'completed' | 'expired';\n apiKey: string | null;\n userId: string | null;\n}> {\n const baseUrl = getApiBaseUrl();\n\n const response = await fetch(`${baseUrl}/developer/v2/poll-auth-url`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ token }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Device auth poll failed: ${response.status} ${error}`);\n }\n\n const data = (await response.json()) as {\n status: 'pending' | 'completed' | 'expired';\n apiKey: string | null;\n userId: string | null;\n };\n\n return data;\n}\n\nexport interface SpaEditorSessionInfo {\n sessionId: string;\n status: string;\n previewDomain: string | null;\n hotUpdateDomain: string | null;\n}\n\nexport interface CustomInterfaceStepInfo {\n stepId: string;\n stepType: string;\n displayName: string;\n workflowId: string;\n workflowName: string;\n spaEditorSession: SpaEditorSessionInfo | null;\n}\n\nexport interface ScriptStepInfo {\n stepId: string;\n displayName: string;\n workflowId: string;\n workflowName: string;\n files: Record<string, string>;\n entryFile: string;\n}\n\nexport interface EditorSession {\n appId: string;\n appName: string;\n customInterfaceSteps: CustomInterfaceStepInfo[];\n scriptSteps: ScriptStepInfo[];\n}\n\nexport async function getEditorSessions(): Promise<EditorSession[]> {\n const baseUrl = getApiBaseUrl();\n\n const response = await fetch(`${baseUrl}/v1/local-editor/sessions`, {\n method: 'GET',\n headers: getHeaders(),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Failed to fetch editor sessions: ${response.status} ${errorText}`,\n );\n }\n\n const data = (await response.json()) as { editors: EditorSession[] };\n return data.editors;\n}\n\nexport async function disconnectHeartbeat(): Promise<void> {\n const baseUrl = getApiBaseUrl();\n\n const response = await fetch(`${baseUrl}/v1/local-models/disconnect`, {\n method: 'POST',\n headers: getHeaders(),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Heartbeat disconnect failed: ${response.status} ${error}`);\n }\n}\n","import { Ollama } from 'ollama';\nimport { getProviderBaseUrl } from '../../config';\nimport { commandExists } from '../utils';\nimport readme from './readme.md';\nimport type {\n Provider,\n LocalModel,\n ChatMessage,\n ChatOptions,\n ChatResponse,\n ProviderSetupStatus,\n} from '../types';\n\nclass OllamaProvider implements Provider {\n readonly name = 'ollama';\n readonly displayName = 'Ollama';\n readonly description =\n 'Run open-source LLMs locally via CLI. Supports Llama, Mistral, Gemma, and more.';\n readonly capabilities = ['text'] as const;\n readonly readme = readme;\n readonly defaultBaseUrl = 'http://localhost:11434';\n\n get baseUrl(): string {\n return getProviderBaseUrl(this.name, this.defaultBaseUrl);\n }\n\n private createClient(): Ollama {\n return new Ollama({ host: this.baseUrl });\n }\n\n async isRunning(): Promise<boolean> {\n try {\n const client = this.createClient();\n await client.list();\n return true;\n } catch {\n return false;\n }\n }\n\n async discoverModels(): Promise<LocalModel[]> {\n try {\n const client = this.createClient();\n const response = await client.list();\n\n return response.models.map((m) => ({\n name: m.name,\n provider: this.name,\n capability: 'text' as const,\n size: m.size,\n parameterSize: m.details?.parameter_size,\n quantization: m.details?.quantization_level,\n }));\n } catch {\n return [];\n }\n }\n\n async detect(): Promise<ProviderSetupStatus> {\n const installed = await commandExists('ollama');\n let running = false;\n\n if (installed) {\n running = await this.isRunning();\n }\n\n return { installed, running };\n }\n\n async *chat(\n model: string,\n messages: ChatMessage[],\n options?: ChatOptions,\n ): AsyncGenerator<ChatResponse> {\n const client = this.createClient();\n\n const stream = await client.chat({\n model,\n messages: messages.map((m) => ({\n role: m.role,\n content: m.content,\n })),\n stream: true,\n options: {\n temperature: options?.temperature,\n num_predict: options?.maxTokens,\n },\n });\n\n for await (const chunk of stream) {\n yield {\n content: chunk.message.content,\n done: chunk.done,\n };\n }\n }\n}\n\nexport default new OllamaProvider();\n","import { exec } from 'child_process';\nimport { promisify } from 'util';\n\nconst execAsync = promisify(exec);\n\n/**\n * Check if a command exists in PATH\n */\nexport async function commandExists(command: string): Promise<boolean> {\n try {\n const checkCmd = process.platform === 'win32' ? 'where' : 'which';\n await execAsync(`${checkCmd} ${command}`);\n return true;\n } catch {\n return false;\n }\n}\n","# Ollama\n\nOllama lets you run text generation models (Llama, Mistral, Gemma, etc.) locally. Once it's running with at least one model downloaded, MindStudio will detect it automatically.\n\n**Default port:** 11434\n**Website:** https://ollama.com\n**GitHub:** https://github.com/ollama/ollama\n\n## Step 1: Install Ollama\n\n### macOS / Linux\n\nOpen a terminal and paste this command:\n\n```\ncurl -fsSL https://ollama.com/install.sh | sh\n```\n\n### macOS (alternative)\n\nDownload the app from https://ollama.com/download, open the file, and drag it into your Applications folder.\n\n### Windows\n\nDownload the installer from https://ollama.com/download and run it. Follow the on-screen instructions.\n\n## Step 2: Start the Server\n\nOpen a terminal and run:\n\n```\nollama serve\n```\n\nLeave this terminal window open -- the server needs to keep running for MindStudio to connect to it.\n\n**macOS tip:** If you installed Ollama as a desktop app, the server starts automatically when you open it. Look for the Ollama icon in your menu bar -- if it's there, you can skip this step.\n\n## Step 3: Download a Model\n\nOpen a **new** terminal window (keep the server running in the other one) and download a model:\n\n```\nollama pull llama3.2\n```\n\nSome good models to start with:\n\n- **llama3.2** -- fast, great all-around model (2 GB download)\n- **mistral** -- efficient for most tasks (4 GB)\n- **gemma2** -- Google's open model (5 GB)\n\nBrowse more models at https://ollama.com/library\n\nOnce the download finishes, go back to the MindStudio tunnel and select **Refresh Providers**. Your models should appear.\n\n## Troubleshooting\n\n- **MindStudio says Ollama is \"not running\"** -- Make sure `ollama serve` is running in a terminal window. You should see \"Listening on 127.0.0.1:11434\" in the output.\n\n- **Ollama is running but no models show up** -- You need to download at least one model first. Run `ollama pull llama3.2` in a separate terminal window.\n\n- **\"address already in use\"** -- Ollama is probably already running. On macOS, check for the Ollama icon in your menu bar. On Linux, run `pkill ollama` and try `ollama serve` again.\n\n- **\"out of memory\" errors** -- Your machine doesn't have enough RAM for that model. Try a smaller one like `llama3.2` (2 GB).\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { getProviderBaseUrl } from '../../config';\nimport readme from './readme.md';\nimport type {\n Provider,\n LocalModel,\n ChatMessage,\n ChatOptions,\n ChatResponse,\n ProviderSetupStatus,\n} from '../types';\n\ninterface LMStudioModel {\n id: string;\n object: string;\n owned_by: string;\n}\n\ninterface LMStudioModelsResponse {\n data: LMStudioModel[];\n}\n\nclass LMStudioProvider implements Provider {\n readonly name = 'lmstudio';\n readonly displayName = 'LM Studio';\n readonly description =\n 'Desktop app for running LLMs locally with a visual interface. No terminal required.';\n readonly capabilities = ['text'] as const;\n readonly readme = readme;\n readonly defaultBaseUrl = 'http://localhost:1234/v1';\n\n get baseUrl(): string {\n return getProviderBaseUrl(this.name, this.defaultBaseUrl);\n }\n\n private getBaseUrl(): string {\n return this.baseUrl;\n }\n\n async isRunning(): Promise<boolean> {\n try {\n const response = await fetch(`${this.getBaseUrl()}/models`, {\n method: 'GET',\n signal: AbortSignal.timeout(3000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async discoverModels(): Promise<LocalModel[]> {\n try {\n const response = await fetch(`${this.getBaseUrl()}/models`);\n\n if (!response.ok) {\n return [];\n }\n\n const data = (await response.json()) as LMStudioModelsResponse;\n\n return data.data.map((m) => ({\n name: m.id,\n provider: this.name,\n capability: 'text' as const,\n }));\n } catch {\n return [];\n }\n }\n\n async detect(): Promise<ProviderSetupStatus> {\n let installed = false;\n\n const possiblePaths = {\n darwin: ['/Applications/LM Studio.app'],\n linux: [\n path.join(os.homedir(), '.local/share/LM Studio'),\n '/opt/lm-studio',\n ],\n win32: [\n path.join(process.env.LOCALAPPDATA || '', 'LM Studio'),\n path.join(process.env.PROGRAMFILES || '', 'LM Studio'),\n ],\n };\n\n const paths =\n possiblePaths[process.platform as keyof typeof possiblePaths] || [];\n for (const p of paths) {\n if (fs.existsSync(p)) {\n installed = true;\n break;\n }\n }\n\n let running = false;\n try {\n const response = await fetch('http://localhost:1234/v1/models', {\n signal: AbortSignal.timeout(1000),\n });\n running = response.ok;\n if (running) installed = true;\n } catch {\n running = false;\n }\n\n return { installed, running };\n }\n\n async *chat(\n model: string,\n messages: ChatMessage[],\n options?: ChatOptions,\n ): AsyncGenerator<ChatResponse> {\n const response = await fetch(`${this.getBaseUrl()}/chat/completions`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n model,\n messages: messages.map((m) => ({\n role: m.role,\n content: m.content,\n })),\n stream: true,\n temperature: options?.temperature,\n max_tokens: options?.maxTokens,\n }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`LM Studio request failed: ${response.status} ${error}`);\n }\n\n if (!response.body) {\n throw new Error('No response body from LM Studio');\n }\n\n const reader = response.body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n\n if (done) {\n yield { content: '', done: true };\n break;\n }\n\n buffer += decoder.decode(value, { stream: true });\n\n const lines = buffer.split('\\n');\n buffer = lines.pop() || '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n\n if (!trimmed || !trimmed.startsWith('data: ')) {\n continue;\n }\n\n const data = trimmed.slice(6);\n\n if (data === '[DONE]') {\n yield { content: '', done: true };\n return;\n }\n\n try {\n const parsed = JSON.parse(data) as {\n choices: Array<{\n delta?: { content?: string };\n finish_reason?: string | null;\n }>;\n };\n\n const choice = parsed.choices[0];\n const content = choice?.delta?.content || '';\n const isDone = choice?.finish_reason !== null;\n\n if (content) {\n yield { content, done: isDone };\n }\n } catch {\n // Skip malformed JSON chunks\n }\n }\n }\n } finally {\n reader.releaseLock();\n }\n }\n}\n\nexport default new LMStudioProvider();\n","# LM Studio\n\nLM Studio is a desktop app for running text generation models locally. No terminal needed -- everything is done through the app. Once its server is running, MindStudio will detect it automatically.\n\n**Default port:** 1234\n**Website:** https://lmstudio.ai\n**GitHub:** https://github.com/lmstudio-ai\n\n## Step 1: Install LM Studio\n\n1. Go to https://lmstudio.ai\n2. Click the download button for your operating system\n3. Open the downloaded file and install it like any other app\n\n## Step 2: Download a Model\n\n1. Open LM Studio\n2. Click the **Discover** tab on the left sidebar\n3. Search for a model (see suggestions below)\n4. Click the download button next to the model you want\n5. Wait for the download to finish\n\nGood starter models:\n\n- **Llama 3.2** -- great all-around model, fast\n- **Mistral** -- efficient and capable\n- **Phi-3** -- compact, runs well on most machines\n\n## Step 3: Start the Server\n\nThis is the key step -- LM Studio needs to be running its local server for MindStudio to connect.\n\n1. In LM Studio, click the **Developer** tab on the left sidebar\n2. Select a model from the dropdown at the top if one isn't already loaded\n3. Click **Start Server**\n\nYou should see a green indicator showing the server is running on `http://localhost:1234`.\n\n**Important:** Just opening LM Studio is not enough. You must start the server from the Developer tab.\n\nLeave LM Studio open with the server running while you use MindStudio. Go back to the tunnel and select **Refresh Providers** -- your models should appear.\n\n## Troubleshooting\n\n- **MindStudio says LM Studio is \"not running\"** -- Make sure you started the server in the Developer tab. The green indicator should be visible.\n\n- **Server is running but no models show up** -- You need to load a model in the Developer tab. Select one from the dropdown at the top of the Developer tab before starting the server.\n\n- **Port conflict** -- If something else is using port 1234, you can change the port in the Developer tab settings.\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport * as os from 'os';\nimport { getProviderBaseUrl, getProviderInstallPath } from '../../config';\nimport readme from './readme.md';\nimport type {\n Provider,\n LocalModel,\n ImageGenerationOptions,\n ImageGenerationResult,\n ImageGenerationProgress,\n ParameterSchema,\n ProviderSetupStatus,\n} from '../types';\n\n/**\n * Response from AUTOMATIC1111's /sdapi/v1/sd-models endpoint\n */\ninterface SDModel {\n title: string;\n model_name: string;\n hash?: string;\n sha256?: string;\n filename: string;\n}\n\n/**\n * Response from AUTOMATIC1111's /sdapi/v1/txt2img endpoint\n */\ninterface Txt2ImgResponse {\n images: string[];\n parameters: Record<string, unknown>;\n info: string;\n}\n\n/**\n * Response from AUTOMATIC1111's /sdapi/v1/progress endpoint\n */\ninterface ProgressResponse {\n progress: number;\n eta_relative: number;\n state: {\n skipped: boolean;\n interrupted: boolean;\n job: string;\n job_count: number;\n job_timestamp: string;\n job_no: number;\n sampling_step: number;\n sampling_steps: number;\n };\n current_image?: string;\n textinfo?: string;\n}\n\n/**\n * Response from AUTOMATIC1111's /sdapi/v1/samplers endpoint\n */\ninterface SDSampler {\n name: string;\n aliases: string[];\n options: Record<string, unknown>;\n}\n\n/**\n * Stable Diffusion provider for AUTOMATIC1111 WebUI\n * Default URL: http://127.0.0.1:7860\n */\nclass StableDiffusionProvider implements Provider {\n readonly name = 'stable-diffusion';\n readonly displayName = 'Stable Diffusion WebUI';\n readonly description =\n 'Generate images locally using Stable Diffusion checkpoints. Runs as a local web UI.';\n readonly capabilities = ['image'] as const;\n readonly readme = readme;\n readonly defaultBaseUrl = 'http://127.0.0.1:7860';\n\n get baseUrl(): string {\n return getProviderBaseUrl(this.name, this.defaultBaseUrl);\n }\n\n private getBaseUrl(): string {\n return this.baseUrl;\n }\n\n async isRunning(): Promise<boolean> {\n try {\n const response = await fetch(`${this.getBaseUrl()}/sdapi/v1/sd-models`, {\n method: 'GET',\n signal: AbortSignal.timeout(5000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n async discoverModels(): Promise<LocalModel[]> {\n try {\n const response = await fetch(`${this.getBaseUrl()}/sdapi/v1/sd-models`);\n\n if (!response.ok) {\n return [];\n }\n\n const models = (await response.json()) as SDModel[];\n\n return models.map((m) => ({\n name: m.model_name,\n provider: this.name,\n capability: 'image' as const,\n }));\n } catch {\n return [];\n }\n }\n\n async detect(): Promise<ProviderSetupStatus> {\n const savedPath = getProviderInstallPath(this.name);\n\n const possiblePaths = [\n ...(savedPath ? [savedPath] : []),\n path.join(os.homedir(), 'stable-diffusion-webui'),\n path.join(os.homedir(), 'Projects', 'stable-diffusion-webui'),\n path.join(os.homedir(), 'Code', 'stable-diffusion-webui'),\n ];\n\n let installed = false;\n for (const p of possiblePaths) {\n if (\n fs.existsSync(path.join(p, 'launch.py')) ||\n fs.existsSync(path.join(p, 'webui.sh')) ||\n fs.existsSync(path.join(p, 'webui.bat'))\n ) {\n installed = true;\n break;\n }\n }\n\n let running = false;\n try {\n const response = await fetch('http://127.0.0.1:7860/sdapi/v1/sd-models', {\n signal: AbortSignal.timeout(1000),\n });\n running = response.ok;\n if (running) installed = true;\n } catch {\n running = false;\n }\n\n return { installed, running };\n }\n\n /**\n * Get the currently loaded model\n */\n async getCurrentModel(): Promise<string | null> {\n try {\n const response = await fetch(`${this.getBaseUrl()}/sdapi/v1/options`);\n if (!response.ok) return null;\n\n const options = (await response.json()) as {\n sd_model_checkpoint?: string;\n };\n return options.sd_model_checkpoint || null;\n } catch {\n return null;\n }\n }\n\n /**\n * Switch to a different model\n */\n async setModel(modelName: string): Promise<void> {\n const response = await fetch(`${this.getBaseUrl()}/sdapi/v1/options`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ sd_model_checkpoint: modelName }),\n });\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Failed to switch model: ${error}`);\n }\n }\n\n async generateImage(\n model: string,\n prompt: string,\n options?: ImageGenerationOptions,\n onProgress?: (progress: ImageGenerationProgress) => void,\n ): Promise<ImageGenerationResult> {\n const currentModel = await this.getCurrentModel();\n if (currentModel && !currentModel.includes(model)) {\n await this.setModel(model);\n }\n\n const payload = {\n prompt,\n negative_prompt: options?.negativePrompt || '',\n steps: options?.steps || 20,\n width: options?.width || 512,\n height: options?.height || 512,\n cfg_scale: options?.cfgScale || 7,\n seed: options?.seed ?? -1,\n sampler_name: options?.sampler || 'Euler a',\n };\n\n const generatePromise = fetch(`${this.getBaseUrl()}/sdapi/v1/txt2img`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(payload),\n });\n\n if (onProgress) {\n const pollProgress = async () => {\n while (true) {\n try {\n const response = await fetch(\n `${this.getBaseUrl()}/sdapi/v1/progress`,\n );\n if (!response.ok) break;\n\n const progress = (await response.json()) as ProgressResponse;\n\n onProgress({\n step: progress.state.sampling_step,\n totalSteps: progress.state.sampling_steps,\n preview: progress.current_image,\n });\n\n if (progress.progress >= 1.0) break;\n\n await new Promise((resolve) => setTimeout(resolve, 500));\n } catch {\n break;\n }\n }\n };\n\n pollProgress().catch(() => {});\n }\n\n const response = await generatePromise;\n\n if (!response.ok) {\n const error = await response.text();\n throw new Error(`Image generation failed: ${response.status} ${error}`);\n }\n\n const result = (await response.json()) as Txt2ImgResponse;\n\n if (!result.images || result.images.length === 0) {\n throw new Error('No images returned from Stable Diffusion');\n }\n\n let info: Record<string, unknown> = {};\n let seed: number | undefined;\n try {\n info = JSON.parse(result.info);\n seed = typeof info.seed === 'number' ? info.seed : undefined;\n } catch {\n // Ignore parse errors\n }\n\n return {\n imageBase64: result.images[0],\n mimeType: 'image/png',\n seed,\n info,\n };\n }\n\n /**\n * Fetch available samplers from the backend\n */\n private async getSamplers(): Promise<string[]> {\n try {\n const response = await fetch(`${this.getBaseUrl()}/sdapi/v1/samplers`);\n if (!response.ok) return this.getDefaultSamplers();\n\n const samplers = (await response.json()) as SDSampler[];\n return samplers.map((s) => s.name);\n } catch {\n return this.getDefaultSamplers();\n }\n }\n\n private getDefaultSamplers(): string[] {\n return [\n 'Euler a',\n 'Euler',\n 'LMS',\n 'Heun',\n 'DPM2',\n 'DPM2 a',\n 'DPM++ 2S a',\n 'DPM++ 2M',\n 'DPM++ SDE',\n 'DPM fast',\n 'DPM adaptive',\n 'LMS Karras',\n 'DPM2 Karras',\n 'DPM2 a Karras',\n 'DPM++ 2S a Karras',\n 'DPM++ 2M Karras',\n 'DPM++ SDE Karras',\n 'DDIM',\n 'PLMS',\n 'UniPC',\n ];\n }\n\n private generateDimensionOptions(): Array<{ label: string; value: string }> {\n const options: Array<{ label: string; value: string }> = [];\n for (let size = 256; size <= 2048; size += 64) {\n options.push({\n label: `${size}px`,\n value: String(size),\n });\n }\n return options;\n }\n\n async getParameterSchemas(): Promise<ParameterSchema[]> {\n const samplers = await this.getSamplers();\n const dimensionOptions = this.generateDimensionOptions();\n\n return [\n {\n type: 'select',\n label: 'Sampler',\n variable: 'sampler',\n helpText: 'The sampling method used for image generation',\n defaultValue: 'Euler a',\n selectOptions: samplers.map((name) => ({\n label: name,\n value: name,\n })),\n },\n {\n type: 'select',\n label: 'Width',\n variable: 'width',\n defaultValue: '512',\n selectOptions: dimensionOptions,\n },\n {\n type: 'select',\n label: 'Height',\n variable: 'height',\n defaultValue: '512',\n selectOptions: dimensionOptions,\n },\n {\n type: 'number',\n label: 'Steps',\n variable: 'steps',\n helpText:\n 'Number of denoising steps. More steps = higher quality but slower.',\n defaultValue: '20',\n numberOptions: {\n min: 1,\n max: 150,\n step: 1,\n },\n },\n {\n type: 'number',\n label: 'CFG Scale',\n variable: 'cfgScale',\n helpText:\n 'How strongly the image should follow the prompt. Higher = more literal.',\n defaultValue: '7',\n numberOptions: {\n min: 1,\n max: 30,\n step: 0.5,\n },\n },\n {\n type: 'seed',\n label: 'Seed',\n variable: 'seed',\n helpText:\n \"A specific value used to guide the 'randomness' of generation. Use -1 for random.\",\n defaultValue: '-1',\n },\n {\n type: 'text',\n label: 'Negative Prompt',\n variable: 'negativePrompt',\n helpText: \"Things you don't want in the image\",\n placeholder: 'blurry, low quality, distorted',\n },\n ];\n }\n}\n\nexport default new StableDiffusionProvider();\n","# Stable Diffusion WebUI\n\nAUTOMATIC1111's Stable Diffusion WebUI runs image generation models locally. Once the server is running with at least one model, MindStudio will detect it automatically.\n\n**Default port:** 7860\n**GitHub:** https://github.com/AUTOMATIC1111/stable-diffusion-webui\n\n## What You'll Need\n\n- **Python 3.10 or newer** -- Check by opening a terminal and typing `python3 --version`. If you don't have it, download from https://www.python.org/downloads/\n\n- **Git** -- Check by typing `git --version`. If you don't have it, download from https://git-scm.com/downloads\n\n## Step 1: Install the WebUI\n\nOpen a terminal and paste this command:\n\n```\ngit clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git ~/stable-diffusion-webui\n```\n\n**Windows users**, use this instead:\n\n```\ngit clone https://github.com/AUTOMATIC1111/stable-diffusion-webui.git %USERPROFILE%\\stable-diffusion-webui\n```\n\n## Step 2: Download a Model\n\nYou need at least one model file for MindStudio to use. Model files have the `.safetensors` extension (typically 2-7 GB).\n\n1. Browse models at https://civitai.com or https://huggingface.co\n2. Download a `.safetensors` checkpoint file\n3. Move the file into this folder:\n\n```\n~/stable-diffusion-webui/models/Stable-diffusion/\n```\n\nGood starter models:\n\n- **Stable Diffusion XL (SDXL)** -- high quality, 1024x1024\n- **Stable Diffusion 1.5** -- fast, widely supported\n\n## Step 3: Start the Server\n\nOpen a terminal and run:\n\n```\ncd ~/stable-diffusion-webui && ./webui.sh --api\n```\n\n**Windows users:**\n\n```\ncd %USERPROFILE%\\stable-diffusion-webui && webui-user.bat --api\n```\n\nThe first time you run this it will take several minutes to install dependencies. This is normal -- let it finish.\n\n**Important:** The `--api` flag is required. Without it, MindStudio cannot send requests to the server.\n\nLeave this terminal window open while using MindStudio. Once the server is ready, go back to the tunnel and select **Refresh Providers** -- your models should appear.\n\n## Troubleshooting\n\n- **MindStudio says WebUI is \"not running\"** -- Make sure you included `--api` when launching. The terminal should show the server at `http://127.0.0.1:7860`.\n\n- **Server is running but no models show up** -- Make sure your `.safetensors` file is directly in the `models/Stable-diffusion/` folder, not inside a subfolder. Restart the server after adding new model files.\n\n- **\"Python not found\"** -- Python 3.10+ is required. Download from https://www.python.org/downloads/. On Windows, check \"Add Python to PATH\" during installation.\n\n- **Errors during first launch** -- Delete the `venv` folder inside `stable-diffusion-webui` and run the launch command again to reinstall dependencies from scratch.\n\n- **\"CUDA out of memory\"** -- Your GPU doesn't have enough memory. Add `--medvram` or `--lowvram` to the launch command: `./webui.sh --api --medvram`\n","import * as path from 'path';\nimport {\n getProviderBaseUrl,\n setProviderBaseUrl,\n getProviderInstallPath,\n setProviderInstallPath,\n} from '../../config';\nimport { discoverWorkflows } from './workflow-discovery';\nimport { ensureConverterInstalled } from './converter-install';\nimport { executeWorkflow } from './workflow-executor';\nimport readme from './readme.md';\nimport type {\n Provider,\n LocalModel,\n ImageGenerationOptions,\n ImageGenerationResult,\n ImageGenerationProgress,\n VideoGenerationOptions,\n VideoGenerationResult,\n VideoGenerationProgress,\n ProviderSetupStatus,\n} from '../types';\n\n// Desktop app uses 8000, CLI default is 8188\nconst COMFYUI_PORTS = [8000, 8188];\n\n/**\n * ComfyUI provider — discovers user-saved workflows and executes them.\n */\nclass ComfyUIProvider implements Provider {\n readonly name = 'comfyui';\n readonly displayName = 'ComfyUI';\n readonly description =\n 'Run any saved ComfyUI workflow — images, video, and more.';\n readonly capabilities = ['image', 'video'] as const;\n readonly readme = readme;\n readonly defaultBaseUrl = 'http://127.0.0.1:8000';\n\n get baseUrl(): string {\n return getProviderBaseUrl(this.name, this.defaultBaseUrl);\n }\n\n private getBaseUrl(): string {\n return this.baseUrl;\n }\n\n /**\n * Try to reach ComfyUI on the configured URL, then fall back to known ports.\n * Persists whichever URL responds so future calls go direct.\n */\n private async findRunningUrl(): Promise<string | null> {\n // Try configured URL first\n const configured = this.getBaseUrl();\n if (await this.checkUrl(configured)) return configured;\n\n // Try other known ports\n for (const port of COMFYUI_PORTS) {\n const url = `http://127.0.0.1:${port}`;\n if (url === configured) continue; // Already tried\n if (await this.checkUrl(url)) {\n setProviderBaseUrl(this.name, url);\n return url;\n }\n }\n\n return null;\n }\n\n private async checkUrl(url: string): Promise<boolean> {\n try {\n const response = await fetch(`${url}/system_stats`, {\n signal: AbortSignal.timeout(1000),\n });\n return response.ok;\n } catch {\n return false;\n }\n }\n\n /**\n * Query the running ComfyUI server for its install path via /internal/folder_paths.\n * Derives the root from the custom_nodes path and persists it for future use.\n */\n private async queryInstallPath(baseUrl: string): Promise<string | null> {\n try {\n const response = await fetch(`${baseUrl}/internal/folder_paths`, {\n signal: AbortSignal.timeout(3000),\n });\n if (!response.ok) return null;\n\n const data = (await response.json()) as Record<string, string[]>;\n const customNodesPaths = data.custom_nodes;\n if (!customNodesPaths || customNodesPaths.length === 0) return null;\n\n // custom_nodes path is like /path/to/ComfyUI/custom_nodes — parent is the install root\n const installPath = path.dirname(customNodesPaths[0]!);\n setProviderInstallPath(this.name, installPath);\n return installPath;\n } catch {\n return null;\n }\n }\n\n async isRunning(): Promise<boolean> {\n return (await this.findRunningUrl()) !== null;\n }\n\n async detect(): Promise<ProviderSetupStatus> {\n const runningUrl = await this.findRunningUrl();\n\n if (runningUrl) {\n // Query the server for its install path and auto-install converter\n const queriedPath = await this.queryInstallPath(runningUrl);\n if (queriedPath) {\n ensureConverterInstalled(queriedPath).catch(() => {});\n }\n return { installed: true, running: true };\n }\n\n // Offline: use previously saved path\n const savedPath = getProviderInstallPath(this.name);\n return { installed: !!savedPath, running: false };\n }\n\n /**\n * Discover workflow-based models from user-saved ComfyUI workflows.\n */\n async discoverModels(): Promise<LocalModel[]> {\n const installPath = getProviderInstallPath(this.name) ?? null;\n return discoverWorkflows(this.getBaseUrl(), installPath);\n }\n\n /**\n * Generate an image using a ComfyUI workflow, with progress tracking.\n */\n async generateImage(\n _model: string,\n _prompt: string,\n options?: ImageGenerationOptions,\n onProgress?: (progress: ImageGenerationProgress) => void,\n ): Promise<ImageGenerationResult> {\n if (!options?.workflow) {\n throw new Error('ComfyUI image generation requires a workflow');\n }\n\n const result = await executeWorkflow({\n baseUrl: this.getBaseUrl(),\n workflow: options.workflow,\n onProgress: onProgress\n ? (p) => onProgress({ step: p.step, totalSteps: p.totalSteps })\n : undefined,\n });\n\n return {\n imageBase64: result.dataBase64,\n mimeType: result.mimeType,\n };\n }\n\n /**\n * Generate a video using a ComfyUI workflow.\n */\n async generateVideo(\n _model: string,\n _prompt: string,\n options?: VideoGenerationOptions,\n onProgress?: (progress: VideoGenerationProgress) => void,\n ): Promise<VideoGenerationResult> {\n if (!options?.workflow) {\n throw new Error('ComfyUI video generation requires a workflow');\n }\n\n const result = await executeWorkflow({\n baseUrl: this.getBaseUrl(),\n workflow: options.workflow,\n onProgress: onProgress\n ? (p) =>\n onProgress({\n step: p.step,\n totalSteps: p.totalSteps,\n currentNode: p.currentNode,\n })\n : undefined,\n });\n\n return {\n videoBase64: result.dataBase64,\n mimeType: result.mimeType,\n };\n }\n}\n\nexport default new ComfyUIProvider();\n","import * as fs from 'fs';\nimport * as path from 'path';\nimport type { LocalModel, ComfyWorkflowParameterSchema } from '../types';\nimport {\n ensureConverterInstalled,\n isConverterEndpointAvailable,\n convertWorkflow,\n resetConverterCache,\n} from './converter-install';\n\nconst VIDEO_OUTPUT_NODES = ['VHS_VideoCombine', 'SaveVideo'];\nconst IMAGE_OUTPUT_NODES = ['SaveImage', 'PreviewImage'];\n\n/**\n * Discover user-saved ComfyUI workflows and return them as aggregated LocalModel entries.\n * Returns at most 2 models: \"ComfyUI Image Generation\" and \"ComfyUI Video Generation\",\n * with all discovered workflows bundled in the parameter schema.\n */\nexport async function discoverWorkflows(\n baseUrl: string,\n installPath: string | null,\n): Promise<LocalModel[]> {\n resetConverterCache();\n\n // Try to install converter if we know the install path\n let converterJustInstalled = false;\n if (installPath) {\n const wasInstalled = await ensureConverterInstalled(installPath);\n if (wasInstalled) {\n converterJustInstalled = true;\n }\n }\n\n const converterAvailable = await isConverterEndpointAvailable(baseUrl);\n\n // If we just installed files but endpoint isn't available, ComfyUI needs restart.\n // We still discover API-format workflows that don't need conversion.\n const needsRestart = converterJustInstalled && !converterAvailable;\n\n // List workflow files\n const workflowFiles = await listWorkflowFiles(baseUrl, installPath);\n\n // Collect converted workflows and unconverted counts per capability\n const converted: {\n image: Array<{ name: string; workflow: Record<string, unknown> }>;\n video: Array<{ name: string; workflow: Record<string, unknown> }>;\n } = { image: [], video: [] };\n const unconvertedCapabilities = new Set<'image' | 'video'>();\n\n for (const file of workflowFiles) {\n try {\n const workflowJson = await fetchWorkflowJson(baseUrl, installPath, file);\n if (!workflowJson) continue;\n\n let apiWorkflow: Record<string, unknown>;\n\n if (isApiFormat(workflowJson)) {\n apiWorkflow = workflowJson;\n } else if (converterAvailable) {\n try {\n apiWorkflow = await convertWorkflow(baseUrl, workflowJson);\n } catch {\n continue; // Skip workflows that fail conversion\n }\n } else {\n // Can't convert UI-format without the endpoint — track as unconverted\n unconvertedCapabilities.add('image');\n continue;\n }\n\n const capability = detectCapability(apiWorkflow);\n const name = path.basename(file, path.extname(file));\n converted[capability].push({ name, workflow: apiWorkflow });\n } catch {\n // Silent failure per-workflow — one broken workflow doesn't block others\n }\n }\n\n const models: LocalModel[] = [];\n\n // Emit aggregated models for each capability that has converted workflows\n for (const capability of ['image', 'video'] as const) {\n if (converted[capability].length > 0) {\n const displayName =\n capability === 'image'\n ? 'ComfyUI Image Generation'\n : 'ComfyUI Video Generation';\n const workflowParam: ComfyWorkflowParameterSchema = {\n type: 'comfyWorkflow',\n variable: 'workflow',\n label: 'Workflow',\n comfyWorkflowOptions: { availableWorkflows: converted[capability] },\n };\n models.push({\n name: displayName,\n provider: 'comfyui',\n capability,\n parameters: [workflowParam],\n });\n }\n }\n\n // Emit statusHint models for capabilities that have only unconverted workflows\n for (const capability of unconvertedCapabilities) {\n // Skip if we already have converted workflows for this capability\n if (converted[capability].length > 0) continue;\n\n const displayName =\n capability === 'image'\n ? 'ComfyUI Image Generation'\n : 'ComfyUI Video Generation';\n models.push({\n name: displayName,\n provider: 'comfyui',\n capability,\n statusHint: needsRestart\n ? 'Restart ComfyUI to enable'\n : 'Workflow converter not available',\n });\n }\n\n return models;\n}\n\n/**\n * List workflow files via the ComfyUI userdata API, falling back to filesystem.\n */\nasync function listWorkflowFiles(\n baseUrl: string,\n installPath: string | null,\n): Promise<string[]> {\n // Try API first\n try {\n const response = await fetch(\n `${baseUrl}/userdata?dir=workflows/&recurse=true&full_info=true`,\n { signal: AbortSignal.timeout(5000) },\n );\n if (response.ok) {\n const data = (await response.json()) as Array<string | { path: string }>;\n return data\n .map((entry) => (typeof entry === 'string' ? entry : entry.path))\n .filter((p) => p.endsWith('.json'));\n }\n } catch {\n // Fall through to filesystem\n }\n\n // Filesystem fallback\n if (installPath) {\n const workflowsDir = path.join(installPath, 'user', 'default', 'workflows');\n return scanDirectory(workflowsDir);\n }\n\n return [];\n}\n\n/**\n * Recursively scan a directory for .json files.\n */\nfunction scanDirectory(dir: string): string[] {\n if (!fs.existsSync(dir)) return [];\n\n const results: string[] = [];\n try {\n const entries = fs.readdirSync(dir, { withFileTypes: true });\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n if (entry.isDirectory()) {\n results.push(\n ...scanDirectory(fullPath).map((f) => path.join(entry.name, f)),\n );\n } else if (entry.name.endsWith('.json')) {\n results.push(entry.name);\n }\n }\n } catch {\n // Ignore read errors\n }\n return results;\n}\n\n/**\n * Fetch a workflow's JSON content via the userdata API, falling back to filesystem.\n */\nasync function fetchWorkflowJson(\n baseUrl: string,\n installPath: string | null,\n filePath: string,\n): Promise<Record<string, unknown> | null> {\n // Try API first — the {file} param needs the full path from the userdata root,\n // with slashes encoded (aiohttp matches {file} as a single path segment)\n try {\n const userdataPath = `workflows/${filePath}`;\n const response = await fetch(\n `${baseUrl}/userdata/${encodeURIComponent(userdataPath)}`,\n { signal: AbortSignal.timeout(5000) },\n );\n if (response.ok) {\n return (await response.json()) as Record<string, unknown>;\n }\n } catch {\n // Fall through to filesystem\n }\n\n // Filesystem fallback\n if (installPath) {\n const fullPath = path.join(\n installPath,\n 'user',\n 'default',\n 'workflows',\n filePath,\n );\n try {\n const content = fs.readFileSync(fullPath, 'utf-8');\n return JSON.parse(content) as Record<string, unknown>;\n } catch {\n return null;\n }\n }\n\n return null;\n}\n\n/**\n * Check if a workflow JSON is already in API format.\n * API format has numeric string keys with objects containing `class_type`.\n */\nexport function isApiFormat(json: Record<string, unknown>): boolean {\n const keys = Object.keys(json);\n if (keys.length === 0) return false;\n\n // API format: keys are numeric strings, values have class_type\n return keys.some((key) => {\n const node = json[key];\n return (\n /^\\d+$/.test(key) &&\n typeof node === 'object' &&\n node !== null &&\n 'class_type' in node\n );\n });\n}\n\n/**\n * Detect whether a workflow produces image or video output.\n */\nexport function detectCapability(\n apiWorkflow: Record<string, unknown>,\n): 'image' | 'video' {\n for (const node of Object.values(apiWorkflow)) {\n if (typeof node === 'object' && node !== null && 'class_type' in node) {\n const classType = (node as { class_type: string }).class_type;\n if (VIDEO_OUTPUT_NODES.includes(classType)) {\n return 'video';\n }\n }\n }\n\n for (const node of Object.values(apiWorkflow)) {\n if (typeof node === 'object' && node !== null && 'class_type' in node) {\n const classType = (node as { class_type: string }).class_type;\n if (IMAGE_OUTPUT_NODES.includes(classType)) {\n return 'image';\n }\n }\n }\n\n // Default to image if we can't determine\n return 'image';\n}\n","import * as fs from 'fs';\nimport * as path from 'path';\n\nconst CONVERTER_DIR = 'comfyui-workflow-to-api-converter-endpoint';\nconst GITHUB_RAW_BASE =\n 'https://raw.githubusercontent.com/SethRobinson/comfyui-workflow-to-api-converter-endpoint/main';\nconst FILES_TO_DOWNLOAD = ['__init__.py', 'workflow_converter.py'];\n\n/**\n * Ensure the workflow converter custom node is installed in ComfyUI's custom_nodes directory.\n * Returns true if files are on disk (ComfyUI may still need a restart to load the endpoint).\n */\nexport async function ensureConverterInstalled(\n installPath: string,\n): Promise<boolean> {\n const customNodesDir = path.join(installPath, 'custom_nodes');\n const converterDir = path.join(customNodesDir, CONVERTER_DIR);\n\n // Check if already installed\n const allFilesExist = FILES_TO_DOWNLOAD.every((f) =>\n fs.existsSync(path.join(converterDir, f)),\n );\n if (allFilesExist) {\n return true;\n }\n\n // Ensure custom_nodes directory exists\n if (!fs.existsSync(customNodesDir)) {\n return false; // Not a valid ComfyUI install\n }\n\n try {\n if (!fs.existsSync(converterDir)) {\n fs.mkdirSync(converterDir, { recursive: true });\n }\n\n for (const filename of FILES_TO_DOWNLOAD) {\n const url = `${GITHUB_RAW_BASE}/${filename}`;\n const response = await fetch(url, {\n signal: AbortSignal.timeout(15000),\n });\n if (!response.ok) {\n throw new Error(`Failed to download ${filename}: ${response.status}`);\n }\n const content = await response.text();\n fs.writeFileSync(path.join(converterDir, filename), content, 'utf-8');\n }\n\n return true;\n } catch {\n return false;\n }\n}\n\nlet converterAvailableCache: boolean | null = null;\n\n/**\n * Check if the /workflow/convert endpoint is available on the running ComfyUI server.\n * Caches result for the duration of the discovery run.\n */\nexport async function isConverterEndpointAvailable(\n baseUrl: string,\n): Promise<boolean> {\n if (converterAvailableCache !== null) {\n return converterAvailableCache;\n }\n\n try {\n const response = await fetch(`${baseUrl}/workflow/convert`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ nodes: [], links: [] }),\n signal: AbortSignal.timeout(5000),\n });\n // Only a 200 means the endpoint is actually loaded;\n // ComfyUI returns 405 for unknown routes, not 404\n converterAvailableCache = response.ok;\n return converterAvailableCache;\n } catch {\n converterAvailableCache = false;\n return false;\n }\n}\n\n/**\n * Reset the converter availability cache. Call at the start of each discovery run.\n */\nexport function resetConverterCache(): void {\n converterAvailableCache = null;\n}\n\n/**\n * Convert a UI-format workflow to API format using ComfyUI's /workflow/convert endpoint.\n * The endpoint auto-detects if already API format and passes through.\n */\nexport async function convertWorkflow(\n baseUrl: string,\n uiWorkflow: unknown,\n): Promise<Record<string, unknown>> {\n const response = await fetch(`${baseUrl}/workflow/convert`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(uiWorkflow),\n signal: AbortSignal.timeout(10000),\n });\n\n if (!response.ok) {\n const errorText = await response.text();\n throw new Error(\n `Workflow conversion failed: ${response.status} ${errorText}`,\n );\n }\n\n return (await response.json()) as Record<string, unknown>;\n}\n","import * as path from 'path';\n\nexport interface WorkflowExecutionResult {\n dataBase64: string;\n mimeType: string;\n filename: string;\n}\n\nexport interface WorkflowExecutionProgress {\n step: number;\n totalSteps: number;\n currentNode?: string;\n}\n\ninterface OutputFile {\n filename: string;\n subfolder: string;\n type: string;\n}\n\n/**\n * Execute an arbitrary workflow on ComfyUI and return the first output.\n * Handles: POST /prompt → WebSocket progress → GET /history → GET /view\n */\nexport async function executeWorkflow(options: {\n baseUrl: string;\n workflow: Record<string, unknown>;\n onProgress?: (progress: WorkflowExecutionProgress) => void;\n}): Promise<WorkflowExecutionResult> {\n const { baseUrl, workflow, onProgress } = options;\n\n const clientId = `mindstudio_${Date.now()}_${Math.random().toString(36).slice(2)}`;\n const wsUrl = baseUrl.replace(/^http/, 'ws') + `/ws?clientId=${clientId}`;\n\n // Submit prompt\n const submitResponse = await fetch(`${baseUrl}/prompt`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({\n prompt: workflow,\n client_id: clientId,\n }),\n });\n\n if (!submitResponse.ok) {\n const errorText = await submitResponse.text();\n throw new Error(\n `ComfyUI prompt submission failed: ${submitResponse.status} ${errorText}`,\n );\n }\n\n const submitResult = (await submitResponse.json()) as {\n prompt_id: string;\n number: number;\n node_errors?: Record<string, unknown>;\n };\n\n if (\n submitResult.node_errors &&\n Object.keys(submitResult.node_errors).length > 0\n ) {\n throw new Error(\n `ComfyUI workflow validation failed: ${JSON.stringify(submitResult.node_errors)}`,\n );\n }\n\n const promptId = submitResult.prompt_id;\n\n // Wait for completion via WebSocket\n await waitForCompletion(wsUrl, promptId, onProgress);\n\n // Fetch history\n const historyResponse = await fetch(`${baseUrl}/history/${promptId}`, {\n signal: AbortSignal.timeout(30000),\n });\n\n if (!historyResponse.ok) {\n throw new Error(\n `Failed to fetch result history: ${historyResponse.status}`,\n );\n }\n\n const history = (await historyResponse.json()) as Record<\n string,\n {\n outputs: Record<\n string,\n {\n images?: OutputFile[];\n gifs?: OutputFile[];\n }\n >;\n }\n >;\n\n const promptHistory = history[promptId];\n if (!promptHistory) {\n throw new Error('No result found in ComfyUI history');\n }\n\n // Scan ALL output nodes — prefer gifs (video) over images\n let outputFile: OutputFile | null = null;\n\n for (const nodeOutputs of Object.values(promptHistory.outputs)) {\n if (nodeOutputs.gifs && nodeOutputs.gifs.length > 0) {\n outputFile = nodeOutputs.gifs[0]!;\n break; // Prefer video output\n }\n if (!outputFile && nodeOutputs.images && nodeOutputs.images.length > 0) {\n outputFile = nodeOutputs.images[0]!;\n }\n }\n\n if (!outputFile) {\n throw new Error('No output files found in ComfyUI result');\n }\n\n // Download the output file\n const fileUrl = new URL(`${baseUrl}/view`);\n fileUrl.searchParams.set('filename', outputFile.filename);\n fileUrl.searchParams.set('subfolder', outputFile.subfolder || '');\n fileUrl.searchParams.set('type', outputFile.type || 'output');\n\n const fileResponse = await fetch(fileUrl.toString(), {\n signal: AbortSignal.timeout(60000),\n });\n\n if (!fileResponse.ok) {\n throw new Error(`Failed to download output file: ${fileResponse.status}`);\n }\n\n const fileBuffer = await fileResponse.arrayBuffer();\n const dataBase64 = Buffer.from(fileBuffer).toString('base64');\n\n const ext = path.extname(outputFile.filename).toLowerCase();\n const mimeType = getMimeType(ext);\n\n return { dataBase64, mimeType, filename: outputFile.filename };\n}\n\nfunction getMimeType(ext: string): string {\n switch (ext) {\n case '.mp4':\n return 'video/mp4';\n case '.webm':\n return 'video/webm';\n case '.webp':\n return 'image/webp';\n case '.gif':\n return 'image/gif';\n case '.png':\n return 'image/png';\n case '.jpg':\n case '.jpeg':\n return 'image/jpeg';\n default:\n return 'application/octet-stream';\n }\n}\n\nfunction waitForCompletion(\n wsUrl: string,\n promptId: string,\n onProgress?: (progress: WorkflowExecutionProgress) => void,\n): Promise<void> {\n return new Promise((resolve, reject) => {\n const timeoutMs = 30 * 60 * 1000;\n let ws: WebSocket;\n\n const timeout = setTimeout(() => {\n try {\n ws?.close();\n } catch {\n // Ignore\n }\n reject(new Error('Workflow execution timed out after 30 minutes'));\n }, timeoutMs);\n\n try {\n ws = new WebSocket(wsUrl);\n } catch (err) {\n clearTimeout(timeout);\n reject(\n new Error(\n `Failed to connect to ComfyUI WebSocket: ${err instanceof Error ? err.message : err}`,\n ),\n );\n return;\n }\n\n ws.onmessage = (event) => {\n try {\n const message = JSON.parse(\n typeof event.data === 'string' ? event.data : '',\n ) as {\n type: string;\n data: Record<string, unknown>;\n };\n\n if (message.type === 'progress') {\n const data = message.data as {\n value: number;\n max: number;\n prompt_id?: string;\n node?: string;\n };\n if (!data.prompt_id || data.prompt_id === promptId) {\n onProgress?.({\n step: data.value,\n totalSteps: data.max,\n currentNode: data.node as string | undefined,\n });\n }\n }\n\n if (message.type === 'execution_success') {\n const data = message.data as { prompt_id: string };\n if (data.prompt_id === promptId) {\n clearTimeout(timeout);\n ws.close();\n resolve();\n }\n }\n\n if (message.type === 'execution_error') {\n const data = message.data as {\n prompt_id: string;\n exception_message?: string;\n node_type?: string;\n };\n if (data.prompt_id === promptId) {\n clearTimeout(timeout);\n ws.close();\n reject(\n new Error(\n `ComfyUI execution error${data.node_type ? ` in ${data.node_type}` : ''}: ${data.exception_message || 'Unknown error'}`,\n ),\n );\n }\n }\n } catch {\n // Ignore non-JSON messages\n }\n };\n\n ws.onerror = () => {\n clearTimeout(timeout);\n reject(new Error('ComfyUI WebSocket error: connection failed'));\n };\n\n ws.onclose = (event) => {\n if (!event.wasClean) {\n clearTimeout(timeout);\n reject(new Error('ComfyUI WebSocket connection closed unexpectedly'));\n }\n };\n });\n}\n","# ComfyUI\n\nComfyUI is a node-based workflow tool for running image and video generation models locally. MindStudio automatically discovers your saved workflows and makes them available as models -- any workflow you build or download in ComfyUI can be used through MindStudio.\n\n**Default port:** 8188\n**Website:** https://www.comfy.org\n\n## What You'll Need\n\n- **A GPU with 8+ GB of VRAM** -- Image and video generation is demanding. Without enough GPU memory, generation will fail or be extremely slow.\n\n## Step 1: Install ComfyUI\n\nDownload and install ComfyUI Desktop from the official website:\n\nhttps://www.comfy.org/download\n\nThe installer handles Python, dependencies, and everything else automatically. Follow the on-screen prompts to complete setup.\n\n## Step 2: Save a Workflow\n\nMindStudio discovers workflows you've saved in ComfyUI. If you don't have any saved workflows yet, open the ComfyUI interface in your browser (http://127.0.0.1:8188), build or load a workflow, and save it using the menu. Downloaded workflow files placed in ComfyUI's workflows folder will also be discovered.\n\nAny workflow that produces image or video output will work. MindStudio detects the output type automatically based on the nodes in your workflow.\n\n## Step 3: Start the Server\n\nOpen ComfyUI Desktop. Once it's running, go back to the tunnel and select **Refresh Providers** -- your saved workflows should appear as models.\n\nIf you're running ComfyUI from the command line instead, start it with:\n\n```\ncd ~/ComfyUI && python main.py --listen\n```\n\n**Important:** The `--listen` flag is required when running from the command line. Without it, MindStudio cannot connect to the server.\n\n## Tip: Workflow Converter\n\nMindStudio automatically installs a custom node called `comfyui-workflow-to-api-converter-endpoint` into ComfyUI's `custom_nodes/` folder. This converts workflows saved in ComfyUI's UI format into the API format needed for execution. **After the first run, you'll need to restart ComfyUI once** so it picks up the new node — after that, it works automatically. If the auto-install doesn't work (e.g. permissions issues), you can install it manually by cloning https://github.com/SethRobinson/comfyui-workflow-to-api-converter-endpoint into your ComfyUI `custom_nodes/` directory and restarting ComfyUI. Without this node, only workflows already saved in API format will be discovered.\n\n## Troubleshooting\n\n- **MindStudio says ComfyUI is \"not running\"** -- Make sure ComfyUI Desktop is open, or if running from the terminal, that you started with the `--listen` flag.\n\n- **Server is running but no workflows show up** -- Make sure you have at least one saved workflow in ComfyUI. Open the ComfyUI interface, load or build a workflow, and save it.\n\n- **\"CUDA out of memory\"** -- Your GPU doesn't have enough memory for the workflow you're running. Try a lighter model or reduce resolution in your workflow.\n\n- **Server crashes mid-generation** -- Restart ComfyUI Desktop, or press Ctrl+C in the terminal and run the start command again.\n","import ollama from './ollama';\nimport lmstudio from './lmstudio';\nimport stableDiffusion from './stable-diffusion';\nimport comfyui from './comfyui';\nimport type {\n Provider,\n LocalModel,\n ProviderSetupStatus,\n ModelCapability,\n} from './types';\n\nexport * from './types';\n\n// Registry of all available providers\nexport const allProviders: Provider[] = [\n ollama,\n lmstudio,\n stableDiffusion,\n comfyui,\n];\n\n/**\n * Get a provider instance by name\n */\nexport function getProvider(name: string): Provider | undefined {\n return allProviders.find((p) => p.name === name);\n}\n\n/**\n * Get all providers that support a given capability\n */\nexport function getProvidersByCapability(cap: ModelCapability): Provider[] {\n return allProviders.filter((p) => p.capabilities.includes(cap));\n}\n\n/**\n * Discover which providers are currently running\n */\nexport async function discoverRunningProviders(): Promise<Provider[]> {\n const results = await Promise.all(\n allProviders.map(async (provider) => ({\n provider,\n running: await provider.isRunning(),\n })),\n );\n\n return results.filter((r) => r.running).map((r) => r.provider);\n}\n\n/**\n * Discover all models from all running providers\n */\nexport async function discoverAllModels(): Promise<LocalModel[]> {\n const runningProviders = await discoverRunningProviders();\n\n const modelArrays = await Promise.all(\n runningProviders.map((p) => p.discoverModels()),\n );\n\n return modelArrays.flat();\n}\n\n/**\n * Check if any provider is running\n */\nexport async function isAnyProviderRunning(): Promise<boolean> {\n const results = await Promise.all(allProviders.map((p) => p.isRunning()));\n return results.some((r) => r);\n}\n\n/**\n * Get provider status for all providers\n */\nexport async function getProviderStatuses(): Promise<\n Array<{ provider: Provider; running: boolean }>\n> {\n return Promise.all(\n allProviders.map(async (provider) => ({\n provider,\n running: await provider.isRunning(),\n })),\n );\n}\n\n/**\n * Discover models filtered by capability\n */\nexport async function discoverModelsByCapability(\n capability: ModelCapability,\n): Promise<LocalModel[]> {\n const runningProviders = await discoverRunningProviders();\n const filteredProviders = runningProviders.filter((p) =>\n p.capabilities.includes(capability),\n );\n\n const modelArrays = await Promise.all(\n filteredProviders.map((p) => p.discoverModels()),\n );\n\n return modelArrays.flat();\n}\n\n/**\n * Detect installation/running status for all providers\n */\nexport async function detectAllProviderStatuses(): Promise<\n Array<{ provider: Provider; status: ProviderSetupStatus }>\n> {\n return Promise.all(\n allProviders.map(async (provider) => ({\n provider,\n status: await provider.detect(),\n })),\n );\n}\n\n/**\n * Discover all models with their parameter schemas\n * For providers with getParameterSchemas, fetches available parameters dynamically\n */\nexport async function discoverAllModelsWithParameters(): Promise<LocalModel[]> {\n const runningProviders = await discoverRunningProviders();\n\n const modelsWithParams = await Promise.all(\n runningProviders.map(async (provider) => {\n const models = await provider.discoverModels();\n\n // Filter out status hint entries — they're for TUI display only\n const realModels = models.filter((m) => !m.statusHint);\n\n if (typeof provider.getParameterSchemas === 'function') {\n const parameters = await provider.getParameterSchemas();\n return realModels.map((model) => ({\n ...model,\n parameters: model.parameters ?? parameters,\n }));\n }\n\n return realModels;\n }),\n );\n\n return modelsWithParams.flat();\n}\n","import { EventEmitter } from 'events';\n\nexport interface RequestStartEvent {\n id: string;\n modelId: string;\n requestType: 'llm_chat' | 'image_generation' | 'video_generation';\n timestamp: number;\n}\n\nexport interface RequestProgressEvent {\n id: string;\n content?: string;\n step?: number;\n totalSteps?: number;\n}\n\nexport interface RequestCompleteEvent {\n id: string;\n success: boolean;\n duration: number;\n result?: {\n chars?: number;\n imageSize?: number;\n videoSize?: number;\n };\n error?: string;\n}\n\nclass RequestEventEmitter extends EventEmitter {\n emitStart(event: RequestStartEvent) {\n this.emit('request:start', event);\n }\n\n emitProgress(event: RequestProgressEvent) {\n this.emit('request:progress', event);\n }\n\n emitComplete(event: RequestCompleteEvent) {\n this.emit('request:complete', event);\n }\n\n onStart(handler: (event: RequestStartEvent) => void) {\n this.on('request:start', handler);\n return () => this.off('request:start', handler);\n }\n\n onProgress(handler: (event: RequestProgressEvent) => void) {\n this.on('request:progress', handler);\n return () => this.off('request:progress', handler);\n }\n\n onComplete(handler: (event: RequestCompleteEvent) => void) {\n this.on('request:complete', handler);\n return () => this.off('request:complete', handler);\n }\n}\n\nexport const requestEvents = new RequestEventEmitter();\n","import {\n pollForRequest,\n submitProgress,\n submitResult,\n disconnectHeartbeat,\n type LocalModelRequest,\n type SyncedModel,\n} from './api';\nimport {\n getProvider,\n discoverAllModels,\n type Provider,\n type LocalModel,\n} from './providers';\nimport { requestEvents } from './events';\n\ninterface ModelMapping {\n provider: Provider;\n localModelName: string;\n}\n\n/**\n * TunnelRunner handles the polling and request processing loop.\n * It emits events that listeners (TUI or simple chalk output) can subscribe to.\n */\nexport class TunnelRunner {\n private isRunning = false;\n private modelMap: Map<string, ModelMapping> = new Map();\n private modelIds: string[] = [];\n\n /**\n * Start with a pre-discovered list of synced models.\n * Used by the TUI, which discovers models itself.\n */\n async start(syncedModels: SyncedModel[]): Promise<void> {\n if (this.isRunning) return;\n\n this.modelIds = syncedModels.map((m) => m.id);\n this.isRunning = true;\n\n // Build cloud ID -> { provider, localModelName } mapping\n const allModels = await discoverAllModels();\n this.buildModelMap(syncedModels, allModels);\n\n // Start polling loop\n this.pollLoop();\n }\n\n stop(): void {\n this.isRunning = false;\n disconnectHeartbeat().catch(() => {});\n }\n\n private buildModelMap(\n syncedModels: SyncedModel[],\n localModels: LocalModel[],\n ): void {\n this.modelMap.clear();\n // Index local models by name for fast lookup\n const localByName = new Map<string, LocalModel>();\n for (const model of localModels) {\n localByName.set(model.name, model);\n }\n // Map cloud ID -> provider + local model name\n for (const synced of syncedModels) {\n const local = localByName.get(synced.name);\n if (local) {\n const provider = getProvider(local.provider);\n if (provider) {\n this.modelMap.set(synced.id, {\n provider,\n localModelName: local.name,\n });\n }\n }\n }\n }\n\n private async pollLoop(): Promise<void> {\n while (this.isRunning) {\n try {\n const request = await pollForRequest(this.modelIds);\n if (request) {\n // Process request in background\n this.processRequest(request);\n }\n } catch (error) {\n // Wait before retrying on error\n await this.sleep(5000);\n }\n }\n }\n\n private async processRequest(request: LocalModelRequest): Promise<void> {\n const startTime = Date.now();\n\n // Emit start event\n requestEvents.emitStart({\n id: request.id,\n modelId: request.modelId,\n requestType: request.requestType,\n timestamp: startTime,\n });\n\n const mapping = this.modelMap.get(request.modelId);\n\n if (!mapping) {\n const error = `Model ${request.modelId} not found`;\n await submitResult(request.id, false, undefined, error);\n requestEvents.emitComplete({\n id: request.id,\n success: false,\n duration: Date.now() - startTime,\n error,\n });\n return;\n }\n\n try {\n switch (request.requestType) {\n case 'llm_chat':\n await this.handleTextRequest(request, mapping, startTime);\n break;\n case 'image_generation':\n await this.handleImageRequest(request, mapping, startTime);\n break;\n case 'video_generation':\n await this.handleVideoRequest(request, mapping, startTime);\n break;\n default:\n throw new Error(`Unsupported request type: ${request.requestType}`);\n }\n } catch (error) {\n const message = error instanceof Error ? error.message : 'Unknown error';\n await submitResult(request.id, false, undefined, message);\n requestEvents.emitComplete({\n id: request.id,\n success: false,\n duration: Date.now() - startTime,\n error: message,\n });\n }\n }\n\n private async handleTextRequest(\n request: LocalModelRequest,\n { provider, localModelName }: ModelMapping,\n startTime: number,\n ): Promise<void> {\n if (!provider.chat) {\n throw new Error(`Provider does not support text generation`);\n }\n\n const messages = (request.payload.messages || []).map((m) => ({\n role: m.role as 'user' | 'assistant' | 'system',\n content: m.content,\n }));\n\n const stream = provider.chat(localModelName, messages, {\n temperature: request.payload.temperature,\n maxTokens: request.payload.maxTokens,\n });\n\n let fullContent = '';\n let lastProgressUpdate = 0;\n const progressInterval = 100;\n\n for await (const chunk of stream) {\n fullContent += chunk.content;\n\n const now = Date.now();\n if (now - lastProgressUpdate > progressInterval) {\n await submitProgress(request.id, fullContent);\n requestEvents.emitProgress({\n id: request.id,\n content: fullContent,\n });\n lastProgressUpdate = now;\n }\n }\n\n await submitProgress(request.id, fullContent);\n await submitResult(request.id, true, {\n content: fullContent,\n usage: { promptTokens: 0, completionTokens: 0 },\n });\n\n requestEvents.emitComplete({\n id: request.id,\n success: true,\n duration: Date.now() - startTime,\n result: { chars: fullContent.length },\n });\n }\n\n private async handleImageRequest(\n request: LocalModelRequest,\n { provider, localModelName }: ModelMapping,\n startTime: number,\n ): Promise<void> {\n if (!provider.generateImage) {\n throw new Error(`Provider does not support image generation`);\n }\n\n const prompt = request.payload.prompt || '';\n const config = request.payload.config || {};\n\n const result = await provider.generateImage(\n localModelName,\n prompt,\n {\n negativePrompt: config.negativePrompt as string | undefined,\n width: config.width as number | undefined,\n height: config.height as number | undefined,\n steps: config.steps as number | undefined,\n cfgScale: config.cfgScale as number | undefined,\n seed: config.seed as number | undefined,\n sampler: config.sampler as string | undefined,\n workflow: config.workflow as Record<string, unknown> | undefined,\n },\n async (progress) => {\n await submitProgress(\n request.id,\n `Step ${progress.step}/${progress.totalSteps}`,\n 'log',\n );\n requestEvents.emitProgress({\n id: request.id,\n step: progress.step,\n totalSteps: progress.totalSteps,\n });\n },\n );\n\n await submitResult(request.id, true, {\n imageBase64: result.imageBase64,\n mimeType: result.mimeType,\n seed: result.seed,\n });\n\n const imageSize = Math.round((result.imageBase64.length * 3) / 4);\n\n requestEvents.emitComplete({\n id: request.id,\n success: true,\n duration: Date.now() - startTime,\n result: { imageSize },\n });\n }\n\n private async handleVideoRequest(\n request: LocalModelRequest,\n { provider, localModelName }: ModelMapping,\n startTime: number,\n ): Promise<void> {\n if (!provider.generateVideo) {\n throw new Error(`Provider does not support video generation`);\n }\n\n const prompt = request.payload.prompt || '';\n const config = request.payload.config || {};\n\n const result = await provider.generateVideo(\n localModelName,\n prompt,\n {\n negativePrompt: config.negativePrompt as string | undefined,\n width: config.width as number | undefined,\n height: config.height as number | undefined,\n numFrames: config.numFrames as number | undefined,\n fps: config.fps as number | undefined,\n steps: config.steps as number | undefined,\n cfgScale: config.cfgScale as number | undefined,\n seed: config.seed as number | undefined,\n workflow: config.workflow as Record<string, unknown> | undefined,\n },\n async (progress) => {\n await submitProgress(\n request.id,\n `Step ${progress.step}/${progress.totalSteps}`,\n 'log',\n );\n requestEvents.emitProgress({\n id: request.id,\n step: progress.step,\n totalSteps: progress.totalSteps,\n });\n },\n );\n\n await submitResult(request.id, true, {\n videoBase64: result.videoBase64,\n mimeType: result.mimeType,\n duration: result.duration,\n fps: result.fps,\n seed: result.seed,\n });\n\n const videoSize = Math.round((result.videoBase64.length * 3) / 4);\n\n requestEvents.emitComplete({\n id: request.id,\n success: true,\n duration: Date.now() - startTime,\n result: { videoSize },\n });\n }\n\n private sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n }\n}\n"],"mappings":"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","names":["path","os","readme_default","readme_default","fs","path","os","readme_default","readme_default","response","path","fs","path","fs","path","path","submitResult","readme_default","readme_default","config"]}