@surfmate.team/digital-human-runninghub 0.2.0 → 0.2.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.
package/dist/index.d.ts CHANGED
@@ -264,4 +264,10 @@ type RunningHubQwen2511Config = {
264
264
  };
265
265
  declare function createRunningHubQwen2511(config: RunningHubQwen2511Config): ImageEditPort;
266
266
 
267
- export { DIALOGUE_APP_ID, DIALOGUE_NODE_IDS, DIALOGUE_RUN_OPTIONS, type DialogueVideoInput, type DialogueVideoPort, type DialogueVideoProgress, FLUX2_KLEIN_SPEC, GREETING_APP_ID, GREETING_CROP_POSITION, GREETING_DEFAULT_TTS_MODEL, GREETING_LORA, GREETING_LORA_STRENGTH, GREETING_MODEL, GREETING_NEGATIVE_PROMPT, GREETING_NODE_IDS, GREETING_POSITIVE_PROMPT_DEFAULT, GREETING_RUN_OPTIONS, GREETING_STEPS, type ImageEditInput, type ImageEditPort, type ImageEditProgress, type ImageEditSpec, type NodeInfo, QWEN2511_SPEC, QWEN2512_SPEC, type RunAppOptions, type RunningHubClient, type RunningHubConfig, type RunningHubDialogueVideoConfig, type RunningHubFlux2KleinConfig, type RunningHubGreetingVideoConfig, type RunningHubQwen2511Config, type RunningHubQwen2512Config, type RunningHubStaticWaitingVideoConfig, type TaskOutputFile, type TaskPollResult, type TaskProgress, type Text2ImageSpec, type TextToImageInput, type TextToImagePort, type TextToImageProgress, type WatchOptions, buildGreetingNodeInfoList, createImageEditPort, createRunningHubClient, createRunningHubDialogueVideo, createRunningHubFlux2Klein, createRunningHubGreetingVideo, createRunningHubQwen2511, createRunningHubQwen2512, createRunningHubStaticWaitingVideo, createText2ImagePort, fetchAvatarAsImageFile, parsePollResponse, silenceAudioFile };
267
+ declare const FLUX_EDIT_SPEC: ImageEditSpec;
268
+ type RunningHubFluxEditConfig = {
269
+ readonly client: RunningHubClient;
270
+ };
271
+ declare function createRunningHubFluxEdit(config: RunningHubFluxEditConfig): ImageEditPort;
272
+
273
+ export { DIALOGUE_APP_ID, DIALOGUE_NODE_IDS, DIALOGUE_RUN_OPTIONS, type DialogueVideoInput, type DialogueVideoPort, type DialogueVideoProgress, FLUX2_KLEIN_SPEC, FLUX_EDIT_SPEC, GREETING_APP_ID, GREETING_CROP_POSITION, GREETING_DEFAULT_TTS_MODEL, GREETING_LORA, GREETING_LORA_STRENGTH, GREETING_MODEL, GREETING_NEGATIVE_PROMPT, GREETING_NODE_IDS, GREETING_POSITIVE_PROMPT_DEFAULT, GREETING_RUN_OPTIONS, GREETING_STEPS, type ImageEditInput, type ImageEditPort, type ImageEditProgress, type ImageEditSpec, type NodeInfo, QWEN2511_SPEC, QWEN2512_SPEC, type RunAppOptions, type RunningHubClient, type RunningHubConfig, type RunningHubDialogueVideoConfig, type RunningHubFlux2KleinConfig, type RunningHubFluxEditConfig, type RunningHubGreetingVideoConfig, type RunningHubQwen2511Config, type RunningHubQwen2512Config, type RunningHubStaticWaitingVideoConfig, type TaskOutputFile, type TaskPollResult, type TaskProgress, type Text2ImageSpec, type TextToImageInput, type TextToImagePort, type TextToImageProgress, type WatchOptions, buildGreetingNodeInfoList, createImageEditPort, createRunningHubClient, createRunningHubDialogueVideo, createRunningHubFlux2Klein, createRunningHubFluxEdit, createRunningHubGreetingVideo, createRunningHubQwen2511, createRunningHubQwen2512, createRunningHubStaticWaitingVideo, createText2ImagePort, fetchAvatarAsImageFile, parsePollResponse, silenceAudioFile };
package/dist/index.js CHANGED
@@ -524,11 +524,23 @@ var QWEN2511_SPEC = {
524
524
  function createRunningHubQwen2511(config) {
525
525
  return createImageEditPort(config.client, QWEN2511_SPEC);
526
526
  }
527
+
528
+ // src/imageedit/fluxedit.ts
529
+ var FLUX_EDIT_SPEC = {
530
+ appId: "2052941353117597697",
531
+ promptNodeId: "178",
532
+ // primary (original) → 175; the reference image → 159
533
+ imageNodeIds: ["175", "159"]
534
+ };
535
+ function createRunningHubFluxEdit(config) {
536
+ return createImageEditPort(config.client, FLUX_EDIT_SPEC);
537
+ }
527
538
  export {
528
539
  DIALOGUE_APP_ID,
529
540
  DIALOGUE_NODE_IDS,
530
541
  DIALOGUE_RUN_OPTIONS,
531
542
  FLUX2_KLEIN_SPEC,
543
+ FLUX_EDIT_SPEC,
532
544
  GREETING_APP_ID,
533
545
  GREETING_CROP_POSITION,
534
546
  GREETING_DEFAULT_TTS_MODEL,
@@ -547,6 +559,7 @@ export {
547
559
  createRunningHubClient,
548
560
  createRunningHubDialogueVideo,
549
561
  createRunningHubFlux2Klein,
562
+ createRunningHubFluxEdit,
550
563
  createRunningHubGreetingVideo,
551
564
  createRunningHubQwen2511,
552
565
  createRunningHubQwen2512,
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/client/parsePollResponse.ts","../src/client/createRunningHubClient.ts","../src/greeting/constants.ts","../src/greeting/buildNodeInfoList.ts","../src/greeting/silence.ts","../src/shared/media.ts","../src/shared/runVideoApp.ts","../src/greeting/createRunningHubGreetingVideo.ts","../src/waiting/createRunningHubStaticWaitingVideo.ts","../src/dialogue/constants.ts","../src/dialogue/createRunningHubDialogueVideo.ts","../src/text2image/run.ts","../src/text2image/qwen2512.ts","../src/text2image/flux2klein.ts","../src/imageedit/run.ts","../src/imageedit/qwen2511.ts"],"sourcesContent":["// Pure: normalize RunningHub's several poll-response shapes into TaskPollResult.\n// Isolated from the HTTP client so it can be tested with fixed data.\n// (Faithful port of mate's runningHubService.parsePollResponse.)\n\nimport type { TaskOutputFile, TaskPollResult } from './types'\n\nexport function parsePollResponse(data: unknown): TaskPollResult {\n const obj = data as Record<string, unknown>\n\n // Format A: { code, data, msg }\n if (obj.code !== undefined) {\n if (obj.code === 804 || obj.msg === 'APIKEY_TASK_IS_RUNNING') {\n const inner = obj.data as Record<string, unknown> | undefined\n const wsUrl = typeof inner?.netWssUrl === 'string' ? (inner.netWssUrl as string) : undefined\n return wsUrl ? { status: 'RUNNING', wsUrl } : { status: 'RUNNING' }\n }\n if (obj.code !== 0) {\n return { status: 'FAILED', msg: String(obj.msg ?? 'Unknown error') }\n }\n const payload = obj.data\n if (Array.isArray(payload)) {\n return { status: 'SUCCESS', files: payload as TaskOutputFile[] }\n }\n const inner = payload as Record<string, unknown> | undefined\n const taskStatus = inner?.taskStatus as string | undefined\n if (taskStatus === 'RUNNING') return { status: 'RUNNING' }\n if (taskStatus === 'QUEUED') return { status: 'QUEUED' }\n if (taskStatus === 'SUCCESS' || taskStatus === 'SUCCEDD') {\n const fileUrl = inner?.fileUrl as string | undefined\n if (fileUrl) return { status: 'SUCCESS', files: [{ fileUrl, fileType: 'video' }] }\n return { status: 'SUCCESS', files: [] }\n }\n if (taskStatus === 'FAILED') {\n return { status: 'FAILED', msg: String(inner?.errorInfo ?? 'Task failed') }\n }\n return { status: 'FAILED', msg: `Unexpected taskStatus: ${taskStatus}` }\n }\n\n // Format B: flat { taskStatus, fileUrl, outputs, ... }\n const taskStatus = obj.taskStatus as string | undefined\n if (taskStatus === 'RUNNING') return { status: 'RUNNING' }\n if (taskStatus === 'QUEUED') return { status: 'QUEUED' }\n if (taskStatus === 'SUCCESS' || taskStatus === 'SUCCEDD') {\n const fileUrl = obj.fileUrl as string | undefined\n const outputs = obj.outputs as TaskOutputFile[] | undefined\n if (Array.isArray(outputs) && outputs.length > 0) {\n return { status: 'SUCCESS', files: outputs }\n }\n if (fileUrl) return { status: 'SUCCESS', files: [{ fileUrl, fileType: 'video' }] }\n return { status: 'SUCCESS', files: [] }\n }\n if (taskStatus === 'FAILED') {\n return { status: 'FAILED', msg: String(obj.errorMessage ?? obj.errorInfo ?? 'Task failed') }\n }\n return { status: 'FAILED', msg: `Unexpected response: ${JSON.stringify(obj).slice(0, 200)}` }\n}\n","// RunningHub OpenAPI client — action layer (HTTP + optional WebSocket).\n// Generic and workflow-agnostic: upload files, run an AI App, watch a task.\n// (Faithful port of mate's runningHubService, minus localStorage task\n// persistence — that's an app concern, kept out of the library.)\n\nimport type {\n NodeInfo,\n RunAppOptions,\n RunningHubClient,\n RunningHubConfig,\n TaskOutputFile,\n TaskPollResult,\n TaskProgress,\n WatchOptions,\n} from './types'\nimport { parsePollResponse } from './parsePollResponse'\n\nconst DEFAULT_BASE = '/api/runninghub'\nconst DEFAULT_POLL_INTERVAL_MS = 3000\n// Long ceiling to bound runaway polling; normal end is SUCCESS/FAILED.\nconst DEFAULT_TIMEOUT_MS = 15 * 60 * 1000\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms))\n\ntype UploadResponse = { code: number; msg: string; data: { fileName: string } }\ntype CreateTaskResponse = {\n taskId: string\n status: string\n errorCode: string\n errorMessage: string\n}\n\n// Attach a ComfyUI-style WebSocket to stream step-level progress. Best-effort:\n// parse 'progress' frames and mutate `latest` in place; the poll loop forwards\n// the latest value each tick. Any failure degrades to poll-only (no percent).\nfunction attachProgressWs(\n url: string,\n latest: { percent?: number; step?: number; total?: number },\n): WebSocket | null {\n try {\n const ws = new WebSocket(url)\n ws.onmessage = (event) => {\n if (typeof event.data !== 'string') return // binary previews — ignore\n try {\n const msg = JSON.parse(event.data) as { type?: string; data?: Record<string, unknown> }\n if (msg.type !== 'progress' || !msg.data) return\n const value = msg.data.value\n const max = msg.data.max\n if (typeof value === 'number' && typeof max === 'number' && max > 0) {\n latest.step = value\n latest.total = max\n latest.percent = Math.round((value / max) * 100)\n }\n } catch {\n /* malformed frame — keep listening */\n }\n }\n ws.onerror = () => {\n /* best-effort; polling carries the flow */\n }\n return ws\n } catch {\n return null\n }\n}\n\nfunction closeWs(ws: WebSocket | null): void {\n if (!ws) return\n if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) ws.close()\n}\n\nexport function createRunningHubClient(config: RunningHubConfig): RunningHubClient {\n const base = config.baseUrl ?? DEFAULT_BASE\n const apiKey = config.apiKey\n const doFetch = config.fetchImpl ?? fetch\n\n async function uploadFile(file: File, fileType: 'image' | 'audio'): Promise<string> {\n const form = new FormData()\n form.append('file', file)\n form.append('apiKey', apiKey)\n form.append('fileType', fileType)\n const res = await doFetch(`${base}/task/openapi/upload`, { method: 'POST', body: form })\n const json = (await res.json()) as UploadResponse\n if (json.code !== 0) throw new Error(json.msg || 'RunningHub upload failed')\n return json.data.fileName\n }\n\n async function runApp(\n appId: string,\n nodeInfoList: NodeInfo[],\n options: RunAppOptions = {},\n ): Promise<string> {\n const body: Record<string, unknown> = {\n nodeInfoList,\n usePersonalQueue: options.usePersonalQueue ?? true,\n }\n if (options.instanceType) body.instanceType = options.instanceType\n const res = await doFetch(`${base}/openapi/v2/run/ai-app/${appId}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },\n body: JSON.stringify(body),\n })\n const json = (await res.json()) as CreateTaskResponse\n if (json.errorCode) {\n const detail = json.errorMessage ? ` ${json.errorMessage}` : ''\n throw new Error(`RunningHub ${json.errorCode}${detail}`)\n }\n return json.taskId\n }\n\n async function pollTask(taskId: string): Promise<TaskPollResult> {\n const res = await doFetch(`${base}/task/openapi/outputs`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ taskId, apiKey }),\n })\n return parsePollResponse(await res.json())\n }\n\n async function watchTask(\n taskId: string,\n onProgress?: (p: TaskProgress) => void,\n options: WatchOptions = {},\n ): Promise<TaskOutputFile[]> {\n const interval = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS\n const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n let ws: WebSocket | null = null\n const latest: { percent?: number; step?: number; total?: number } = {}\n try {\n const deadline = Date.now() + timeout\n while (Date.now() < deadline) {\n await sleep(interval)\n const result = await pollTask(taskId)\n switch (result.status) {\n case 'QUEUED':\n onProgress?.({ tag: 'queued' })\n break\n case 'RUNNING':\n if (!ws && result.wsUrl) ws = attachProgressWs(result.wsUrl, latest)\n onProgress?.({ tag: 'running', ...latest })\n break\n case 'SUCCESS':\n return result.files\n case 'FAILED':\n // Transient right after creation, before status is queryable — retry.\n if (result.msg === 'APIKEY_TASK_STATUS_ERROR') {\n onProgress?.({ tag: 'queued' })\n break\n }\n throw new Error(result.msg)\n }\n }\n throw new Error(`RunningHub task timed out (${Math.round(timeout / 1000)}s)`)\n } finally {\n closeWs(ws)\n }\n }\n\n // Cancel endpoint accepts { taskId, apiKey } (one task) or { apiKey } (all of\n // this key's tasks). Best-effort: we don't surface the body (often empty).\n async function cancelTask(taskId: string): Promise<void> {\n await doFetch(`${base}/task/openapi/cancel`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ taskId, apiKey }),\n })\n }\n\n async function cancelByApiKey(): Promise<void> {\n await doFetch(`${base}/task/openapi/cancel`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiKey }),\n })\n }\n\n return { uploadFile, runApp, pollTask, watchTask, cancelTask, cancelByApiKey }\n}\n","// RunningHub greeting-video AI App config (Wan2.1 I2V 720p with audio sync).\n// One image + one speech audio + a fixed action prompt → MP4 with baked-in\n// audio in a single task. Values pinned here to avoid drift (mirror mate's\n// types/greetingTalkingVideo.ts).\n\nexport const GREETING_APP_ID = '2048355544552968194'\n\nexport const GREETING_NODE_IDS = {\n // Variable per request\n IMAGE: '133',\n AUDIO: '125',\n // Secondary audio slot — always filled with 1s of silence (see silence.ts).\n // Omitting it triggers a RunningHub 803 \"invalid node info\" error.\n AUDIO_SECONDARY: '209',\n POSITIVE_PROMPT: '216',\n // Fixed knobs\n CROP_POSITION: '171',\n SAVE_OUTPUT: '131',\n NEGATIVE_PROMPT: '135',\n LORA: '138',\n MODEL: '122',\n STEPS: '201',\n} as const\n\nexport const GREETING_MODEL = 'Wan2_1-I2V-14B-720p_fp8_e4m3fn_scaled_KJ.safetensors'\nexport const GREETING_LORA = 'Wan21_I2V_14B_lightx2v_cfg_step_distill_lora_rank64.safetensors'\nexport const GREETING_LORA_STRENGTH = 0.8\nexport const GREETING_STEPS = 25\nexport const GREETING_CROP_POSITION = 'center'\n\nexport const GREETING_NEGATIVE_PROMPT =\n 'bright tones, overexposed, static, blurred details, subtitles, style, works, paintings, images, static, overall gray, worst quality, low quality, JPEG compression residue, ugly, incomplete, extra fingers, poorly drawn hands, poorly drawn faces, deformed, disfigured, misshapen limbs, fused fingers, still picture, messy background, three legs, many people in the background, walking backwards'\n\n// Default action prompt (node 216) — \"she looks at the camera, holds the pose,\n// natural blink/breathing, stays still\". Used when the caller passes none.\nexport const GREETING_POSITIVE_PROMPT_DEFAULT =\n '她看着镜头说话,保持姿势,保持表情,自然眨眼和呼吸,保持不动'\n\n/** The 720p I2V app runs on the shared pool — the personal queue returns 803. */\nexport const GREETING_RUN_OPTIONS = { usePersonalQueue: false, instanceType: 'default' } as const\n\n/** TTS model used to synthesize the greeting speech when the caller gives none. */\nexport const GREETING_DEFAULT_TTS_MODEL = 'speech-02-turbo'\n","// Pure: build the node-override list for the greeting AI App from the uploaded\n// file names + prompt. Extracted for fixed-data testing (mirrors mate's\n// buildGreetingTalkingNodeInfoList).\n\nimport type { NodeInfo } from '../client'\nimport {\n GREETING_NODE_IDS as N,\n GREETING_MODEL,\n GREETING_LORA,\n GREETING_LORA_STRENGTH,\n GREETING_STEPS,\n GREETING_CROP_POSITION,\n GREETING_NEGATIVE_PROMPT,\n} from './constants'\n\nexport function buildGreetingNodeInfoList(args: {\n imageFileName: string\n audioFileName: string\n silenceAudioFileName: string\n positivePrompt: string\n}): NodeInfo[] {\n return [\n // Variable per request\n { nodeId: N.IMAGE, fieldName: 'image', fieldValue: args.imageFileName },\n { nodeId: N.AUDIO, fieldName: 'audio', fieldValue: args.audioFileName },\n { nodeId: N.AUDIO_SECONDARY, fieldName: 'audio', fieldValue: args.silenceAudioFileName },\n { nodeId: N.POSITIVE_PROMPT, fieldName: 'text', fieldValue: args.positivePrompt },\n // Fixed knobs\n { nodeId: N.CROP_POSITION, fieldName: 'crop_position', fieldValue: GREETING_CROP_POSITION },\n { nodeId: N.SAVE_OUTPUT, fieldName: 'save_output', fieldValue: 'true' },\n { nodeId: N.NEGATIVE_PROMPT, fieldName: 'negative_prompt', fieldValue: GREETING_NEGATIVE_PROMPT },\n { nodeId: N.LORA, fieldName: 'lora', fieldValue: GREETING_LORA },\n { nodeId: N.LORA, fieldName: 'strength', fieldValue: String(GREETING_LORA_STRENGTH) },\n { nodeId: N.MODEL, fieldName: 'model', fieldValue: GREETING_MODEL },\n { nodeId: N.STEPS, fieldName: 'value', fieldValue: String(GREETING_STEPS) },\n ]\n}\n","// The workflow's secondary audio slot (node 209) is schema-required; we always\n// fill it with 1 second of silence. mate ships a 4 KB silence_1s.mp3 asset — to\n// keep this package asset-free (batteries-included), the same file is embedded\n// here as base64 and decoded to a File at runtime.\n\nconst SILENCE_1S_MP3_BASE64 =\n 'SUQzBAAAAAAAIlRTU0UAAAAOAAADTGF2ZjYxLjcuMTAwAAAAAAAAAAAAAAD/+0DAAAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAACgAABD2ABAQFhYdHR0jIykpKS8vNTU1OztBQUFISE5OTlRUWlpaYGBmZmZsbHJycnl5f39/hYWLi4uRkZeXl52dpKSkqqqwsLC2try8vMLCyMjIzs7V1dXb2+Hh4efn7e3t8/P5+fn//wAAAABMYXZjNjEuMTkAAAAAAAAAAAAAAAAkBXwAAAAAAAAQ9in4b6YAAAAAAP/7EMQAA8AAAaQAAAAgAAA0gAAABExBTUUzLjEwMFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxCmDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDEUwPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMR8g8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxKYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDEz4PAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV'\n\nlet cached: File | null = null\n\n/** A 1-second silent MP3 as a File (cached for the page lifetime). */\nexport function silenceAudioFile(): File {\n if (cached) return cached\n const bin = atob(SILENCE_1S_MP3_BASE64)\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n cached = new File([bytes], 'silence_1s.mp3', { type: 'audio/mpeg' })\n return cached\n}\n","// Shared media helpers used by the greeting + waiting bindings.\n\n// ── Avatar → raster image File ──\n// DiceBear (the default avatar provider) returns SVG; Wan2.1 I2V needs a raster\n// image, so SVG is rasterized to PNG via an offscreen canvas.\n\nasync function rasterizeSvg(svgBlob: Blob, size = 512): Promise<Blob> {\n const svgUrl = URL.createObjectURL(svgBlob)\n try {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.src = svgUrl\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve()\n img.onerror = () => reject(new Error('Failed to load SVG into <img>'))\n })\n const canvas = document.createElement('canvas')\n canvas.width = size\n canvas.height = size\n const ctx = canvas.getContext('2d')\n if (!ctx) throw new Error('Canvas 2D context unavailable')\n ctx.fillStyle = '#ffffff'\n ctx.fillRect(0, 0, size, size)\n ctx.drawImage(img, 0, 0, size, size)\n return await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (blob) => (blob ? resolve(blob) : reject(new Error('Canvas toBlob returned null'))),\n 'image/png',\n )\n })\n } finally {\n URL.revokeObjectURL(svgUrl)\n }\n}\n\nexport async function fetchAvatarAsImageFile(url: string, filename: string): Promise<File> {\n const res = await fetch(url)\n if (!res.ok) throw new Error(`Failed to fetch avatar: ${res.status}`)\n const blob = await res.blob()\n if (blob.type === 'image/svg+xml' || url.toLowerCase().endsWith('.svg')) {\n const png = await rasterizeSvg(blob)\n return new File([png], `${filename}.png`, { type: 'image/png' })\n }\n if (!blob.type.startsWith('image/')) {\n throw new Error(`Avatar URL did not return an image (got ${blob.type || 'unknown'})`)\n }\n return new File([blob], filename, { type: blob.type })\n}\n\n// ── Silent WAV ──\n// The static-waiting workflow has no length parameter — the result duration is\n// driven by a silent audio track. Build N seconds of 16 kHz mono 16-bit PCM\n// silence as a WAV Blob (mirrors mate's buildSilenceWavBlob).\n\nconst SAMPLE_RATE = 16000\nconst CHANNELS = 1\nconst BITS = 16\n\nexport function buildSilenceWavBlob(seconds: number): Blob {\n const sampleCount = Math.round(seconds * SAMPLE_RATE)\n const blockAlign = (CHANNELS * BITS) / 8\n const byteRate = SAMPLE_RATE * blockAlign\n const dataSize = sampleCount * blockAlign\n const buffer = new ArrayBuffer(44 + dataSize)\n const view = new DataView(buffer)\n const writeStr = (offset: number, s: string) => {\n for (let i = 0; i < s.length; i++) view.setUint8(offset + i, s.charCodeAt(i))\n }\n writeStr(0, 'RIFF')\n view.setUint32(4, 36 + dataSize, true)\n writeStr(8, 'WAVE')\n writeStr(12, 'fmt ')\n view.setUint32(16, 16, true) // PCM fmt chunk size\n view.setUint16(20, 1, true) // PCM\n view.setUint16(22, CHANNELS, true)\n view.setUint32(24, SAMPLE_RATE, true)\n view.setUint32(28, byteRate, true)\n view.setUint16(32, blockAlign, true)\n view.setUint16(34, BITS, true)\n writeStr(36, 'data')\n view.setUint32(40, dataSize, true)\n // PCM samples left zero-filled = silence.\n return new Blob([buffer], { type: 'audio/wav' })\n}\n","// Shared RunningHub image-to-video pipeline. The greeting and waiting bindings\n// differ ONLY in which files they prepare and how they map the uploaded names to\n// nodes; everything else — upload-all → runApp → watchTask → pick the video URL,\n// plus the progress wording — lives here once.\n\nimport type { NodeInfo, RunAppOptions, RunningHubClient } from '../client'\n\nexport type VideoUpload = {\n /** Key the buildNodes callback uses to reference this file's uploaded name. */\n readonly key: string\n readonly file: File\n readonly fileType: 'image' | 'audio'\n}\n\nexport type RunVideoAppArgs = {\n readonly client: RunningHubClient\n readonly appId: string\n readonly runOptions?: RunAppOptions | undefined\n /** Files to upload before submitting; their RunningHub names feed buildNodes. */\n readonly uploads: VideoUpload[]\n /** Build the node overrides from the uploaded file names (keyed by upload.key). */\n readonly buildNodes: (fileNames: Record<string, string>) => NodeInfo[]\n /** Surface a human progress message (上传素材中… / 提交任务中… / 排队中… / 生成中… N%). */\n readonly onMessage?: ((message: string) => void) | undefined\n}\n\nexport async function runVideoApp(args: RunVideoAppArgs): Promise<string> {\n args.onMessage?.('上传素材中…')\n const entries = await Promise.all(\n args.uploads.map(async (u) => [u.key, await args.client.uploadFile(u.file, u.fileType)] as const),\n )\n const fileNames: Record<string, string> = Object.fromEntries(entries)\n\n args.onMessage?.('提交任务中…')\n const taskId = await args.client.runApp(args.appId, args.buildNodes(fileNames), args.runOptions)\n\n const files = await args.client.watchTask(taskId, (p) => {\n if (p.tag === 'queued') args.onMessage?.('排队中…')\n else args.onMessage?.(p.percent != null ? `生成中… ${p.percent}%` : '生成中…')\n })\n\n const url = files.find((f) => /\\.(mp4|webm|mov)$/i.test(f.fileUrl))?.fileUrl ?? files[0]?.fileUrl\n if (!url) throw new Error('RunningHub task succeeded but returned no video URL')\n return url\n}\n","// RunningHub implementation of greeting's GreetingVideoPort.\n// Prepares the two greeting-specific inputs — TTS audio (via the injected\n// VoiceSynthesisPort) and the avatar image — then hands the upload→run→watch\n// pipeline to the shared runVideoApp. The schema-required secondary audio slot\n// (node 209) is filled with the embedded 1s silence.\n//\n// Returns the RunningHub CDN URL (~24h expiry) — persist via greeting's\n// GreetingVideoStoragePort / a storage mirror for anything long-lived.\n\nimport type {\n GreetingVideoPort,\n GreetingVideoInput,\n GreetingVideoProgress,\n} from '@surfmate.team/digital-human-greeting'\nimport type { VoiceSynthesisPort } from '@surfmate.team/digital-human-voice'\nimport type { RunningHubClient } from '../client'\nimport { runVideoApp } from '../shared/runVideoApp'\nimport { fetchAvatarAsImageFile } from '../shared/media'\nimport {\n GREETING_APP_ID,\n GREETING_RUN_OPTIONS,\n GREETING_POSITIVE_PROMPT_DEFAULT,\n GREETING_DEFAULT_TTS_MODEL,\n} from './constants'\nimport { buildGreetingNodeInfoList } from './buildNodeInfoList'\nimport { silenceAudioFile } from './silence'\n\nexport type RunningHubGreetingVideoConfig = {\n /** Generic RunningHub client (createRunningHubClient). */\n readonly client: RunningHubClient\n /** TTS backend that turns the greeting text into audio bytes (e.g. MiniMax). */\n readonly synth: VoiceSynthesisPort\n /** TTS model id. Defaults to MiniMax speech-02-turbo. */\n readonly modelId?: string\n /** Action prompt for node 216. Defaults to the \"looks at camera, holds pose\" prompt. */\n readonly positivePrompt?: string\n}\n\nexport function createRunningHubGreetingVideo(\n config: RunningHubGreetingVideoConfig,\n): GreetingVideoPort {\n const { client, synth } = config\n const modelId = config.modelId ?? GREETING_DEFAULT_TTS_MODEL\n const positivePrompt =\n (config.positivePrompt && config.positivePrompt.trim()) || GREETING_POSITIVE_PROMPT_DEFAULT\n\n return {\n async generate(\n input: GreetingVideoInput,\n onProgress?: (p: GreetingVideoProgress) => void,\n ): Promise<string> {\n const msg = (message: string) => onProgress?.({ message })\n\n // Greeting-specific inputs: synthesized speech + the avatar image.\n msg('合成语音中…')\n const audioBytes = await synth.synthesize({ text: input.text, voiceId: input.voiceId, modelId })\n const audioFile = new File([audioBytes], 'greeting-speech.mp3', { type: 'audio/mpeg' })\n\n msg('获取头像中…')\n const imageFile = await fetchAvatarAsImageFile(input.avatarUrl, 'greeting-avatar')\n\n return runVideoApp({\n client,\n appId: GREETING_APP_ID,\n runOptions: GREETING_RUN_OPTIONS,\n uploads: [\n { key: 'image', file: imageFile, fileType: 'image' },\n { key: 'audio', file: audioFile, fileType: 'audio' },\n { key: 'silence', file: silenceAudioFile(), fileType: 'audio' },\n ],\n buildNodes: (n) =>\n buildGreetingNodeInfoList({\n imageFileName: n.image ?? '',\n audioFileName: n.audio ?? '',\n silenceAudioFileName: n.silence ?? '',\n positivePrompt,\n }),\n onMessage: msg,\n })\n },\n }\n}\n","// RunningHub implementation of waiting's WaitingVideoGenerationPort (\"Generate\n// Static\"). Prepares the two waiting-specific inputs — the avatar image and N\n// seconds of silence (which drive the clip length, no TTS) — then hands the\n// upload→run→watch pipeline to the shared runVideoApp.\n//\n// Returns the RunningHub CDN URL (~24h expiry) — persist via the waiting storage\n// port / a mirror for anything long-lived.\n\nimport type {\n WaitingVideoGenerationPort,\n WaitingVideoGenerationInput,\n WaitingVideoProgress,\n} from '@surfmate.team/digital-human-waiting'\nimport type { NodeInfo, RunningHubClient } from '../client'\nimport { runVideoApp } from '../shared/runVideoApp'\nimport { fetchAvatarAsImageFile, buildSilenceWavBlob } from '../shared/media'\n\n// mate's static-waiting AI App + node ids.\nconst STATIC_WAITING_APP_ID = '2050552043860963330'\nconst NODE_IDS = { IMAGE: '34', PROMPT: '17', AUDIO: '38' } as const\nconst RUN_OPTIONS = { usePersonalQueue: false, instanceType: 'default' } as const\n\nexport type RunningHubStaticWaitingVideoConfig = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubStaticWaitingVideo(\n config: RunningHubStaticWaitingVideoConfig,\n): WaitingVideoGenerationPort {\n const { client } = config\n return {\n async generateStatic(\n input: WaitingVideoGenerationInput,\n onProgress?: (p: WaitingVideoProgress) => void,\n ): Promise<string> {\n const msg = (message: string) => onProgress?.({ message })\n\n // Waiting-specific inputs: the avatar image + N seconds of silence.\n msg('获取头像中…')\n const imageFile = await fetchAvatarAsImageFile(input.avatarUrl, `${input.characterId}-avatar`)\n const silence = buildSilenceWavBlob(input.seconds)\n const audioFile = new File([silence], `silence-${input.seconds}s.wav`, { type: 'audio/wav' })\n\n return runVideoApp({\n client,\n appId: STATIC_WAITING_APP_ID,\n runOptions: RUN_OPTIONS,\n uploads: [\n { key: 'image', file: imageFile, fileType: 'image' },\n { key: 'audio', file: audioFile, fileType: 'audio' },\n ],\n buildNodes: (n): NodeInfo[] => [\n { nodeId: NODE_IDS.IMAGE, fieldName: 'image', fieldValue: n.image ?? '' },\n { nodeId: NODE_IDS.PROMPT, fieldName: 'prompt', fieldValue: input.prompt },\n { nodeId: NODE_IDS.AUDIO, fieldName: 'audio', fieldValue: n.audio ?? '' },\n ],\n onMessage: msg,\n })\n },\n }\n}\n","// RunningHub 多人对话 (multi-person lip-sync dialogue) AI App config.\n// InfiniteTalk dual-image driving: one reference image holding two people +\n// three audio tracks → one MP4 where each person lip-syncs to their own voice.\n//\n// The two per-person tracks drive the MOUTHS (each audio only animates its\n// person's face, assigned by the workflow's auto/painted masks); the full\n// conversation track is the OUTPUT soundtrack and sets the total length.\n// (See docs/workflows.md — node ids match the published app, App 2062039217370394626.)\n\nexport const DIALOGUE_APP_ID = '2062039217370394626'\n\nexport const DIALOGUE_NODE_IDS = {\n /** Reference image (the two people). */\n IMAGE: '57',\n /** audio_1 — isolated track that drives the LEFT person's mouth. */\n AUDIO_LEFT: '64',\n /** audio_2 — isolated track that drives the RIGHT person's mouth. */\n AUDIO_RIGHT: '59',\n /** Full conversation — the output soundtrack + drives total video length. */\n AUDIO_FULL: '58',\n /** Action / camera prompt (e.g. 固定镜头). Empty by default. */\n PROMPT: '86',\n} as const\n\n/** Like the other I2V apps, this runs on the shared pool — personal queue → 803. */\nexport const DIALOGUE_RUN_OPTIONS = { usePersonalQueue: false, instanceType: 'default' } as const\n","// RunningHub 多人对话 binding: a two-person lip-sync dialogue video. Self-\n// contained (no feature-package port yet) — it owns its input/port types and\n// composes the generic client via the shared runVideoApp pipeline.\n//\n// The caller prepares three time-aligned audio tracks (e.g. via Web Audio):\n// - leftAudio : only the LEFT person's voice (silence elsewhere) → audio_1\n// - rightAudio : only the RIGHT person's voice (silence elsewhere) → audio_2\n// - fullAudio : the complete conversation (both, mixed) → soundtrack + length\n// All three MUST be the same length and aligned on one timeline.\n//\n// Returns the RunningHub CDN URL (~24h expiry) — persist/mirror anything you\n// need long-lived.\n\nimport type { NodeInfo, RunningHubClient } from '../client'\nimport { runVideoApp } from '../shared/runVideoApp'\nimport { DIALOGUE_APP_ID, DIALOGUE_NODE_IDS, DIALOGUE_RUN_OPTIONS } from './constants'\n\nexport type DialogueVideoInput = {\n /** Reference image containing the two people. */\n readonly image: File\n /** Isolated audio for the LEFT person (drives audio_1). */\n readonly leftAudio: File\n /** Isolated audio for the RIGHT person (drives audio_2). */\n readonly rightAudio: File\n /** The full conversation track — output soundtrack + total length. */\n readonly fullAudio: File\n /** Optional action / camera prompt (e.g. 固定镜头). Omit → empty. */\n readonly prompt?: string | undefined\n}\n\nexport type DialogueVideoProgress = { readonly message: string }\n\nexport interface DialogueVideoPort {\n /** Generate the dialogue video; resolves to the output video URL (~24h CDN). */\n generate(\n input: DialogueVideoInput,\n onProgress?: (p: DialogueVideoProgress) => void,\n ): Promise<string>\n}\n\nexport type RunningHubDialogueVideoConfig = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubDialogueVideo(\n config: RunningHubDialogueVideoConfig,\n): DialogueVideoPort {\n const { client } = config\n return {\n async generate(\n input: DialogueVideoInput,\n onProgress?: (p: DialogueVideoProgress) => void,\n ): Promise<string> {\n const msg = (message: string) => onProgress?.({ message })\n return runVideoApp({\n client,\n appId: DIALOGUE_APP_ID,\n runOptions: DIALOGUE_RUN_OPTIONS,\n uploads: [\n { key: 'image', file: input.image, fileType: 'image' },\n { key: 'left', file: input.leftAudio, fileType: 'audio' },\n { key: 'right', file: input.rightAudio, fileType: 'audio' },\n { key: 'full', file: input.fullAudio, fileType: 'audio' },\n ],\n buildNodes: (n): NodeInfo[] => [\n { nodeId: DIALOGUE_NODE_IDS.IMAGE, fieldName: 'image', fieldValue: n.image ?? '' },\n { nodeId: DIALOGUE_NODE_IDS.AUDIO_LEFT, fieldName: 'audio', fieldValue: n.left ?? '' },\n { nodeId: DIALOGUE_NODE_IDS.AUDIO_RIGHT, fieldName: 'audio', fieldValue: n.right ?? '' },\n { nodeId: DIALOGUE_NODE_IDS.AUDIO_FULL, fieldName: 'audio', fieldValue: n.full ?? '' },\n { nodeId: DIALOGUE_NODE_IDS.PROMPT, fieldName: 'text', fieldValue: input.prompt ?? '' },\n ],\n onMessage: msg,\n })\n },\n }\n}\n","// Shared text-to-image runner. Every RunningHub text-to-image app has the same\n// shape — prompt + width + height + batch → image URL(s), no uploads — and\n// differs ONLY in its app id, the four node ids, and the default dimensions.\n// Each model file (qwen2512, flux2klein, …) supplies that spec; this builds the\n// TextToImagePort once.\n\nimport type { NodeInfo, RunAppOptions, RunningHubClient } from '../client'\nimport type { TextToImageInput, TextToImagePort, TextToImageProgress } from './types'\n\nconst IMAGE_RE = /\\.(png|jpe?g|webp)$/i\n\n/** Per-model spec: the app id, its four node ids, and default dimensions. */\nexport type Text2ImageSpec = {\n readonly appId: string\n readonly nodeIds: {\n readonly prompt: string\n readonly width: string\n readonly height: string\n readonly batch: string\n }\n readonly defaults: { readonly width: number; readonly height: number; readonly batchSize: number }\n /** Defaults to the shared-pool options (personal queue → 803 on most apps). */\n readonly runOptions?: RunAppOptions\n}\n\nconst SHARED_POOL: RunAppOptions = { usePersonalQueue: false, instanceType: 'default' }\n\nexport function createText2ImagePort(client: RunningHubClient, spec: Text2ImageSpec): TextToImagePort {\n return {\n async generate(\n input: TextToImageInput,\n onProgress?: (p: TextToImageProgress) => void,\n ): Promise<string[]> {\n const msg = (message: string) => onProgress?.({ message })\n const width = input.width ?? spec.defaults.width\n const height = input.height ?? spec.defaults.height\n const batchSize = input.batchSize ?? spec.defaults.batchSize\n\n const nodes: NodeInfo[] = [\n { nodeId: spec.nodeIds.prompt, fieldName: 'text', fieldValue: input.prompt },\n { nodeId: spec.nodeIds.width, fieldName: 'value', fieldValue: String(width) },\n { nodeId: spec.nodeIds.height, fieldName: 'value', fieldValue: String(height) },\n { nodeId: spec.nodeIds.batch, fieldName: 'value', fieldValue: String(batchSize) },\n ]\n\n msg('提交任务中…')\n const taskId = await client.runApp(spec.appId, nodes, spec.runOptions ?? SHARED_POOL)\n\n const files = await client.watchTask(taskId, (p) => {\n if (p.tag === 'queued') msg('排队中…')\n else msg(p.percent != null ? `生成中… ${p.percent}%` : '生成中…')\n })\n\n const images = files.filter((f) => IMAGE_RE.test(f.fileUrl)).map((f) => f.fileUrl)\n const urls = images.length > 0 ? images : files.map((f) => f.fileUrl)\n if (urls.length === 0) throw new Error('RunningHub task succeeded but returned no image URL')\n return urls\n },\n }\n}\n","// RunningHub 文生图 — Qwen-Image 2512 binding. Just the per-model spec; the\n// shared runner builds the TextToImagePort. (Published app 2052988750950617090.)\n\nimport type { RunningHubClient } from '../client'\nimport type { TextToImagePort } from './types'\nimport { createText2ImagePort, type Text2ImageSpec } from './run'\n\nexport const QWEN2512_SPEC: Text2ImageSpec = {\n appId: '2052988750950617090',\n // node 6 prompt · 80 width · 81 height · 117 batch size\n nodeIds: { prompt: '6', width: '80', height: '81', batch: '117' },\n defaults: { width: 1920, height: 1080, batchSize: 1 },\n}\n\nexport type RunningHubQwen2512Config = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubQwen2512(config: RunningHubQwen2512Config): TextToImagePort {\n return createText2ImagePort(config.client, QWEN2512_SPEC)\n}\n","// RunningHub 文生图 — Flux2 Klein binding. Same shape as Qwen2512, different\n// app + node ids. (Published app 2026466502756536322.)\n\nimport type { RunningHubClient } from '../client'\nimport type { TextToImagePort } from './types'\nimport { createText2ImagePort, type Text2ImageSpec } from './run'\n\nexport const FLUX2_KLEIN_SPEC: Text2ImageSpec = {\n appId: '2026466502756536322',\n // node 64 prompt · 99 width · 101 height · 103 batch size\n nodeIds: { prompt: '64', width: '99', height: '101', batch: '103' },\n defaults: { width: 1920, height: 1088, batchSize: 1 },\n}\n\nexport type RunningHubFlux2KleinConfig = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubFlux2Klein(config: RunningHubFlux2KleinConfig): TextToImagePort {\n return createText2ImagePort(config.client, FLUX2_KLEIN_SPEC)\n}\n","// Shared image-edit runner. Every RunningHub image-edit app has the same shape:\n// upload 1–N source images → fill the prompt node + the image nodes in order →\n// run → watch → image URL(s). Each model file supplies the spec; this builds\n// the ImageEditPort once.\n\nimport type { NodeInfo, RunAppOptions, RunningHubClient } from '../client'\nimport type { ImageEditInput, ImageEditPort, ImageEditProgress } from './types'\n\nconst IMAGE_RE = /\\.(png|jpe?g|webp)$/i\nconst SHARED_POOL: RunAppOptions = { usePersonalQueue: false, instanceType: 'default' }\n\n/** Per-model spec: the app id, the prompt node, and the image nodes in fill order. */\nexport type ImageEditSpec = {\n readonly appId: string\n readonly promptNodeId: string\n /** Image node ids in fill order (primary first). The Nth image → imageNodeIds[N-1]. */\n readonly imageNodeIds: readonly string[]\n readonly runOptions?: RunAppOptions\n}\n\nconst toFile = (blob: Blob, i: number): File =>\n blob instanceof File ? blob : new File([blob], `image-${i}.png`, { type: blob.type || 'image/png' })\n\nexport function createImageEditPort(client: RunningHubClient, spec: ImageEditSpec): ImageEditPort {\n return {\n async generate(\n input: ImageEditInput,\n onProgress?: (p: ImageEditProgress) => void,\n ): Promise<string[]> {\n const msg = (message: string) => onProgress?.({ message })\n // Primary is required by the type; extras are optional and capped to the\n // model's remaining image slots.\n const images = [input.image, ...(input.extraImages ?? [])].slice(0, spec.imageNodeIds.length)\n\n msg('上传素材中…')\n const names = await Promise.all(images.map((img, i) => client.uploadFile(toFile(img, i), 'image')))\n\n const nodes: NodeInfo[] = [\n { nodeId: spec.promptNodeId, fieldName: 'text', fieldValue: input.prompt },\n ]\n for (let i = 0; i < names.length; i++) {\n const nodeId = spec.imageNodeIds[i]\n const fileValue = names[i]\n if (nodeId && fileValue) nodes.push({ nodeId, fieldName: 'image', fieldValue: fileValue })\n }\n\n msg('提交任务中…')\n const taskId = await client.runApp(spec.appId, nodes, spec.runOptions ?? SHARED_POOL)\n\n const files = await client.watchTask(taskId, (p) => {\n if (p.tag === 'queued') msg('排队中…')\n else msg(p.percent != null ? `生成中… ${p.percent}%` : '生成中…')\n })\n\n const out = files.filter((f) => IMAGE_RE.test(f.fileUrl)).map((f) => f.fileUrl)\n const urls = out.length > 0 ? out : files.map((f) => f.fileUrl)\n if (urls.length === 0) throw new Error('RunningHub task succeeded but returned no image URL')\n return urls\n },\n }\n}\n","// RunningHub 图像编辑 — Qwen-Image 2511 binding. Up to 3 source images + a\n// prompt. (Published app 2025738520135995394.)\n\nimport type { RunningHubClient } from '../client'\nimport type { ImageEditPort } from './types'\nimport { createImageEditPort, type ImageEditSpec } from './run'\n\nexport const QWEN2511_SPEC: ImageEditSpec = {\n appId: '2025738520135995394',\n promptNodeId: '182',\n // primary first: 1 img → 157; 2 → 157,180; 3 → 157,180,181\n imageNodeIds: ['157', '180', '181'],\n}\n\nexport type RunningHubQwen2511Config = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubQwen2511(config: RunningHubQwen2511Config): ImageEditPort {\n return createImageEditPort(config.client, QWEN2511_SPEC)\n}\n"],"mappings":"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","names":["inner","taskStatus","IMAGE_RE","SHARED_POOL"]}
1
+ {"version":3,"sources":["../src/client/parsePollResponse.ts","../src/client/createRunningHubClient.ts","../src/greeting/constants.ts","../src/greeting/buildNodeInfoList.ts","../src/greeting/silence.ts","../src/shared/media.ts","../src/shared/runVideoApp.ts","../src/greeting/createRunningHubGreetingVideo.ts","../src/waiting/createRunningHubStaticWaitingVideo.ts","../src/dialogue/constants.ts","../src/dialogue/createRunningHubDialogueVideo.ts","../src/text2image/run.ts","../src/text2image/qwen2512.ts","../src/text2image/flux2klein.ts","../src/imageedit/run.ts","../src/imageedit/qwen2511.ts","../src/imageedit/fluxedit.ts"],"sourcesContent":["// Pure: normalize RunningHub's several poll-response shapes into TaskPollResult.\n// Isolated from the HTTP client so it can be tested with fixed data.\n// (Faithful port of mate's runningHubService.parsePollResponse.)\n\nimport type { TaskOutputFile, TaskPollResult } from './types'\n\nexport function parsePollResponse(data: unknown): TaskPollResult {\n const obj = data as Record<string, unknown>\n\n // Format A: { code, data, msg }\n if (obj.code !== undefined) {\n if (obj.code === 804 || obj.msg === 'APIKEY_TASK_IS_RUNNING') {\n const inner = obj.data as Record<string, unknown> | undefined\n const wsUrl = typeof inner?.netWssUrl === 'string' ? (inner.netWssUrl as string) : undefined\n return wsUrl ? { status: 'RUNNING', wsUrl } : { status: 'RUNNING' }\n }\n if (obj.code !== 0) {\n return { status: 'FAILED', msg: String(obj.msg ?? 'Unknown error') }\n }\n const payload = obj.data\n if (Array.isArray(payload)) {\n return { status: 'SUCCESS', files: payload as TaskOutputFile[] }\n }\n const inner = payload as Record<string, unknown> | undefined\n const taskStatus = inner?.taskStatus as string | undefined\n if (taskStatus === 'RUNNING') return { status: 'RUNNING' }\n if (taskStatus === 'QUEUED') return { status: 'QUEUED' }\n if (taskStatus === 'SUCCESS' || taskStatus === 'SUCCEDD') {\n const fileUrl = inner?.fileUrl as string | undefined\n if (fileUrl) return { status: 'SUCCESS', files: [{ fileUrl, fileType: 'video' }] }\n return { status: 'SUCCESS', files: [] }\n }\n if (taskStatus === 'FAILED') {\n return { status: 'FAILED', msg: String(inner?.errorInfo ?? 'Task failed') }\n }\n return { status: 'FAILED', msg: `Unexpected taskStatus: ${taskStatus}` }\n }\n\n // Format B: flat { taskStatus, fileUrl, outputs, ... }\n const taskStatus = obj.taskStatus as string | undefined\n if (taskStatus === 'RUNNING') return { status: 'RUNNING' }\n if (taskStatus === 'QUEUED') return { status: 'QUEUED' }\n if (taskStatus === 'SUCCESS' || taskStatus === 'SUCCEDD') {\n const fileUrl = obj.fileUrl as string | undefined\n const outputs = obj.outputs as TaskOutputFile[] | undefined\n if (Array.isArray(outputs) && outputs.length > 0) {\n return { status: 'SUCCESS', files: outputs }\n }\n if (fileUrl) return { status: 'SUCCESS', files: [{ fileUrl, fileType: 'video' }] }\n return { status: 'SUCCESS', files: [] }\n }\n if (taskStatus === 'FAILED') {\n return { status: 'FAILED', msg: String(obj.errorMessage ?? obj.errorInfo ?? 'Task failed') }\n }\n return { status: 'FAILED', msg: `Unexpected response: ${JSON.stringify(obj).slice(0, 200)}` }\n}\n","// RunningHub OpenAPI client — action layer (HTTP + optional WebSocket).\n// Generic and workflow-agnostic: upload files, run an AI App, watch a task.\n// (Faithful port of mate's runningHubService, minus localStorage task\n// persistence — that's an app concern, kept out of the library.)\n\nimport type {\n NodeInfo,\n RunAppOptions,\n RunningHubClient,\n RunningHubConfig,\n TaskOutputFile,\n TaskPollResult,\n TaskProgress,\n WatchOptions,\n} from './types'\nimport { parsePollResponse } from './parsePollResponse'\n\nconst DEFAULT_BASE = '/api/runninghub'\nconst DEFAULT_POLL_INTERVAL_MS = 3000\n// Long ceiling to bound runaway polling; normal end is SUCCESS/FAILED.\nconst DEFAULT_TIMEOUT_MS = 15 * 60 * 1000\n\nconst sleep = (ms: number): Promise<void> => new Promise((r) => setTimeout(r, ms))\n\ntype UploadResponse = { code: number; msg: string; data: { fileName: string } }\ntype CreateTaskResponse = {\n taskId: string\n status: string\n errorCode: string\n errorMessage: string\n}\n\n// Attach a ComfyUI-style WebSocket to stream step-level progress. Best-effort:\n// parse 'progress' frames and mutate `latest` in place; the poll loop forwards\n// the latest value each tick. Any failure degrades to poll-only (no percent).\nfunction attachProgressWs(\n url: string,\n latest: { percent?: number; step?: number; total?: number },\n): WebSocket | null {\n try {\n const ws = new WebSocket(url)\n ws.onmessage = (event) => {\n if (typeof event.data !== 'string') return // binary previews — ignore\n try {\n const msg = JSON.parse(event.data) as { type?: string; data?: Record<string, unknown> }\n if (msg.type !== 'progress' || !msg.data) return\n const value = msg.data.value\n const max = msg.data.max\n if (typeof value === 'number' && typeof max === 'number' && max > 0) {\n latest.step = value\n latest.total = max\n latest.percent = Math.round((value / max) * 100)\n }\n } catch {\n /* malformed frame — keep listening */\n }\n }\n ws.onerror = () => {\n /* best-effort; polling carries the flow */\n }\n return ws\n } catch {\n return null\n }\n}\n\nfunction closeWs(ws: WebSocket | null): void {\n if (!ws) return\n if (ws.readyState === WebSocket.OPEN || ws.readyState === WebSocket.CONNECTING) ws.close()\n}\n\nexport function createRunningHubClient(config: RunningHubConfig): RunningHubClient {\n const base = config.baseUrl ?? DEFAULT_BASE\n const apiKey = config.apiKey\n const doFetch = config.fetchImpl ?? fetch\n\n async function uploadFile(file: File, fileType: 'image' | 'audio'): Promise<string> {\n const form = new FormData()\n form.append('file', file)\n form.append('apiKey', apiKey)\n form.append('fileType', fileType)\n const res = await doFetch(`${base}/task/openapi/upload`, { method: 'POST', body: form })\n const json = (await res.json()) as UploadResponse\n if (json.code !== 0) throw new Error(json.msg || 'RunningHub upload failed')\n return json.data.fileName\n }\n\n async function runApp(\n appId: string,\n nodeInfoList: NodeInfo[],\n options: RunAppOptions = {},\n ): Promise<string> {\n const body: Record<string, unknown> = {\n nodeInfoList,\n usePersonalQueue: options.usePersonalQueue ?? true,\n }\n if (options.instanceType) body.instanceType = options.instanceType\n const res = await doFetch(`${base}/openapi/v2/run/ai-app/${appId}`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${apiKey}` },\n body: JSON.stringify(body),\n })\n const json = (await res.json()) as CreateTaskResponse\n if (json.errorCode) {\n const detail = json.errorMessage ? ` ${json.errorMessage}` : ''\n throw new Error(`RunningHub ${json.errorCode}${detail}`)\n }\n return json.taskId\n }\n\n async function pollTask(taskId: string): Promise<TaskPollResult> {\n const res = await doFetch(`${base}/task/openapi/outputs`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ taskId, apiKey }),\n })\n return parsePollResponse(await res.json())\n }\n\n async function watchTask(\n taskId: string,\n onProgress?: (p: TaskProgress) => void,\n options: WatchOptions = {},\n ): Promise<TaskOutputFile[]> {\n const interval = options.pollIntervalMs ?? DEFAULT_POLL_INTERVAL_MS\n const timeout = options.timeoutMs ?? DEFAULT_TIMEOUT_MS\n let ws: WebSocket | null = null\n const latest: { percent?: number; step?: number; total?: number } = {}\n try {\n const deadline = Date.now() + timeout\n while (Date.now() < deadline) {\n await sleep(interval)\n const result = await pollTask(taskId)\n switch (result.status) {\n case 'QUEUED':\n onProgress?.({ tag: 'queued' })\n break\n case 'RUNNING':\n if (!ws && result.wsUrl) ws = attachProgressWs(result.wsUrl, latest)\n onProgress?.({ tag: 'running', ...latest })\n break\n case 'SUCCESS':\n return result.files\n case 'FAILED':\n // Transient right after creation, before status is queryable — retry.\n if (result.msg === 'APIKEY_TASK_STATUS_ERROR') {\n onProgress?.({ tag: 'queued' })\n break\n }\n throw new Error(result.msg)\n }\n }\n throw new Error(`RunningHub task timed out (${Math.round(timeout / 1000)}s)`)\n } finally {\n closeWs(ws)\n }\n }\n\n // Cancel endpoint accepts { taskId, apiKey } (one task) or { apiKey } (all of\n // this key's tasks). Best-effort: we don't surface the body (often empty).\n async function cancelTask(taskId: string): Promise<void> {\n await doFetch(`${base}/task/openapi/cancel`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ taskId, apiKey }),\n })\n }\n\n async function cancelByApiKey(): Promise<void> {\n await doFetch(`${base}/task/openapi/cancel`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify({ apiKey }),\n })\n }\n\n return { uploadFile, runApp, pollTask, watchTask, cancelTask, cancelByApiKey }\n}\n","// RunningHub greeting-video AI App config (Wan2.1 I2V 720p with audio sync).\n// One image + one speech audio + a fixed action prompt → MP4 with baked-in\n// audio in a single task. Values pinned here to avoid drift (mirror mate's\n// types/greetingTalkingVideo.ts).\n\nexport const GREETING_APP_ID = '2048355544552968194'\n\nexport const GREETING_NODE_IDS = {\n // Variable per request\n IMAGE: '133',\n AUDIO: '125',\n // Secondary audio slot — always filled with 1s of silence (see silence.ts).\n // Omitting it triggers a RunningHub 803 \"invalid node info\" error.\n AUDIO_SECONDARY: '209',\n POSITIVE_PROMPT: '216',\n // Fixed knobs\n CROP_POSITION: '171',\n SAVE_OUTPUT: '131',\n NEGATIVE_PROMPT: '135',\n LORA: '138',\n MODEL: '122',\n STEPS: '201',\n} as const\n\nexport const GREETING_MODEL = 'Wan2_1-I2V-14B-720p_fp8_e4m3fn_scaled_KJ.safetensors'\nexport const GREETING_LORA = 'Wan21_I2V_14B_lightx2v_cfg_step_distill_lora_rank64.safetensors'\nexport const GREETING_LORA_STRENGTH = 0.8\nexport const GREETING_STEPS = 25\nexport const GREETING_CROP_POSITION = 'center'\n\nexport const GREETING_NEGATIVE_PROMPT =\n 'bright tones, overexposed, static, blurred details, subtitles, style, works, paintings, images, static, overall gray, worst quality, low quality, JPEG compression residue, ugly, incomplete, extra fingers, poorly drawn hands, poorly drawn faces, deformed, disfigured, misshapen limbs, fused fingers, still picture, messy background, three legs, many people in the background, walking backwards'\n\n// Default action prompt (node 216) — \"she looks at the camera, holds the pose,\n// natural blink/breathing, stays still\". Used when the caller passes none.\nexport const GREETING_POSITIVE_PROMPT_DEFAULT =\n '她看着镜头说话,保持姿势,保持表情,自然眨眼和呼吸,保持不动'\n\n/** The 720p I2V app runs on the shared pool — the personal queue returns 803. */\nexport const GREETING_RUN_OPTIONS = { usePersonalQueue: false, instanceType: 'default' } as const\n\n/** TTS model used to synthesize the greeting speech when the caller gives none. */\nexport const GREETING_DEFAULT_TTS_MODEL = 'speech-02-turbo'\n","// Pure: build the node-override list for the greeting AI App from the uploaded\n// file names + prompt. Extracted for fixed-data testing (mirrors mate's\n// buildGreetingTalkingNodeInfoList).\n\nimport type { NodeInfo } from '../client'\nimport {\n GREETING_NODE_IDS as N,\n GREETING_MODEL,\n GREETING_LORA,\n GREETING_LORA_STRENGTH,\n GREETING_STEPS,\n GREETING_CROP_POSITION,\n GREETING_NEGATIVE_PROMPT,\n} from './constants'\n\nexport function buildGreetingNodeInfoList(args: {\n imageFileName: string\n audioFileName: string\n silenceAudioFileName: string\n positivePrompt: string\n}): NodeInfo[] {\n return [\n // Variable per request\n { nodeId: N.IMAGE, fieldName: 'image', fieldValue: args.imageFileName },\n { nodeId: N.AUDIO, fieldName: 'audio', fieldValue: args.audioFileName },\n { nodeId: N.AUDIO_SECONDARY, fieldName: 'audio', fieldValue: args.silenceAudioFileName },\n { nodeId: N.POSITIVE_PROMPT, fieldName: 'text', fieldValue: args.positivePrompt },\n // Fixed knobs\n { nodeId: N.CROP_POSITION, fieldName: 'crop_position', fieldValue: GREETING_CROP_POSITION },\n { nodeId: N.SAVE_OUTPUT, fieldName: 'save_output', fieldValue: 'true' },\n { nodeId: N.NEGATIVE_PROMPT, fieldName: 'negative_prompt', fieldValue: GREETING_NEGATIVE_PROMPT },\n { nodeId: N.LORA, fieldName: 'lora', fieldValue: GREETING_LORA },\n { nodeId: N.LORA, fieldName: 'strength', fieldValue: String(GREETING_LORA_STRENGTH) },\n { nodeId: N.MODEL, fieldName: 'model', fieldValue: GREETING_MODEL },\n { nodeId: N.STEPS, fieldName: 'value', fieldValue: String(GREETING_STEPS) },\n ]\n}\n","// The workflow's secondary audio slot (node 209) is schema-required; we always\n// fill it with 1 second of silence. mate ships a 4 KB silence_1s.mp3 asset — to\n// keep this package asset-free (batteries-included), the same file is embedded\n// here as base64 and decoded to a File at runtime.\n\nconst SILENCE_1S_MP3_BASE64 =\n 'SUQzBAAAAAAAIlRTU0UAAAAOAAADTGF2ZjYxLjcuMTAwAAAAAAAAAAAAAAD/+0DAAAAAAAAAAAAAAAAAAAAAAABJbmZvAAAADwAAACgAABD2ABAQFhYdHR0jIykpKS8vNTU1OztBQUFISE5OTlRUWlpaYGBmZmZsbHJycnl5f39/hYWLi4uRkZeXl52dpKSkqqqwsLC2try8vMLCyMjIzs7V1dXb2+Hh4efn7e3t8/P5+fn//wAAAABMYXZjNjEuMTkAAAAAAAAAAAAAAAAkBXwAAAAAAAAQ9in4b6YAAAAAAP/7EMQAA8AAAaQAAAAgAAA0gAAABExBTUUzLjEwMFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxCmDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDEUwPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMR8g8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxKYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDEz4PAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVUxBTUUzLjEwMFVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVTEFNRTMuMTAwVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVMQU1FMy4xMDBVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV//sQxNYDwAABpAAAACAAADSAAAAEVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVX/+xDE1gPAAAGkAAAAIAAANIAAAARVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVf/7EMTWA8AAAaQAAAAgAAA0gAAABFVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVVV'\n\nlet cached: File | null = null\n\n/** A 1-second silent MP3 as a File (cached for the page lifetime). */\nexport function silenceAudioFile(): File {\n if (cached) return cached\n const bin = atob(SILENCE_1S_MP3_BASE64)\n const bytes = new Uint8Array(bin.length)\n for (let i = 0; i < bin.length; i++) bytes[i] = bin.charCodeAt(i)\n cached = new File([bytes], 'silence_1s.mp3', { type: 'audio/mpeg' })\n return cached\n}\n","// Shared media helpers used by the greeting + waiting bindings.\n\n// ── Avatar → raster image File ──\n// DiceBear (the default avatar provider) returns SVG; Wan2.1 I2V needs a raster\n// image, so SVG is rasterized to PNG via an offscreen canvas.\n\nasync function rasterizeSvg(svgBlob: Blob, size = 512): Promise<Blob> {\n const svgUrl = URL.createObjectURL(svgBlob)\n try {\n const img = new Image()\n img.crossOrigin = 'anonymous'\n img.src = svgUrl\n await new Promise<void>((resolve, reject) => {\n img.onload = () => resolve()\n img.onerror = () => reject(new Error('Failed to load SVG into <img>'))\n })\n const canvas = document.createElement('canvas')\n canvas.width = size\n canvas.height = size\n const ctx = canvas.getContext('2d')\n if (!ctx) throw new Error('Canvas 2D context unavailable')\n ctx.fillStyle = '#ffffff'\n ctx.fillRect(0, 0, size, size)\n ctx.drawImage(img, 0, 0, size, size)\n return await new Promise<Blob>((resolve, reject) => {\n canvas.toBlob(\n (blob) => (blob ? resolve(blob) : reject(new Error('Canvas toBlob returned null'))),\n 'image/png',\n )\n })\n } finally {\n URL.revokeObjectURL(svgUrl)\n }\n}\n\nexport async function fetchAvatarAsImageFile(url: string, filename: string): Promise<File> {\n const res = await fetch(url)\n if (!res.ok) throw new Error(`Failed to fetch avatar: ${res.status}`)\n const blob = await res.blob()\n if (blob.type === 'image/svg+xml' || url.toLowerCase().endsWith('.svg')) {\n const png = await rasterizeSvg(blob)\n return new File([png], `${filename}.png`, { type: 'image/png' })\n }\n if (!blob.type.startsWith('image/')) {\n throw new Error(`Avatar URL did not return an image (got ${blob.type || 'unknown'})`)\n }\n return new File([blob], filename, { type: blob.type })\n}\n\n// ── Silent WAV ──\n// The static-waiting workflow has no length parameter — the result duration is\n// driven by a silent audio track. Build N seconds of 16 kHz mono 16-bit PCM\n// silence as a WAV Blob (mirrors mate's buildSilenceWavBlob).\n\nconst SAMPLE_RATE = 16000\nconst CHANNELS = 1\nconst BITS = 16\n\nexport function buildSilenceWavBlob(seconds: number): Blob {\n const sampleCount = Math.round(seconds * SAMPLE_RATE)\n const blockAlign = (CHANNELS * BITS) / 8\n const byteRate = SAMPLE_RATE * blockAlign\n const dataSize = sampleCount * blockAlign\n const buffer = new ArrayBuffer(44 + dataSize)\n const view = new DataView(buffer)\n const writeStr = (offset: number, s: string) => {\n for (let i = 0; i < s.length; i++) view.setUint8(offset + i, s.charCodeAt(i))\n }\n writeStr(0, 'RIFF')\n view.setUint32(4, 36 + dataSize, true)\n writeStr(8, 'WAVE')\n writeStr(12, 'fmt ')\n view.setUint32(16, 16, true) // PCM fmt chunk size\n view.setUint16(20, 1, true) // PCM\n view.setUint16(22, CHANNELS, true)\n view.setUint32(24, SAMPLE_RATE, true)\n view.setUint32(28, byteRate, true)\n view.setUint16(32, blockAlign, true)\n view.setUint16(34, BITS, true)\n writeStr(36, 'data')\n view.setUint32(40, dataSize, true)\n // PCM samples left zero-filled = silence.\n return new Blob([buffer], { type: 'audio/wav' })\n}\n","// Shared RunningHub image-to-video pipeline. The greeting and waiting bindings\n// differ ONLY in which files they prepare and how they map the uploaded names to\n// nodes; everything else — upload-all → runApp → watchTask → pick the video URL,\n// plus the progress wording — lives here once.\n\nimport type { NodeInfo, RunAppOptions, RunningHubClient } from '../client'\n\nexport type VideoUpload = {\n /** Key the buildNodes callback uses to reference this file's uploaded name. */\n readonly key: string\n readonly file: File\n readonly fileType: 'image' | 'audio'\n}\n\nexport type RunVideoAppArgs = {\n readonly client: RunningHubClient\n readonly appId: string\n readonly runOptions?: RunAppOptions | undefined\n /** Files to upload before submitting; their RunningHub names feed buildNodes. */\n readonly uploads: VideoUpload[]\n /** Build the node overrides from the uploaded file names (keyed by upload.key). */\n readonly buildNodes: (fileNames: Record<string, string>) => NodeInfo[]\n /** Surface a human progress message (上传素材中… / 提交任务中… / 排队中… / 生成中… N%). */\n readonly onMessage?: ((message: string) => void) | undefined\n}\n\nexport async function runVideoApp(args: RunVideoAppArgs): Promise<string> {\n args.onMessage?.('上传素材中…')\n const entries = await Promise.all(\n args.uploads.map(async (u) => [u.key, await args.client.uploadFile(u.file, u.fileType)] as const),\n )\n const fileNames: Record<string, string> = Object.fromEntries(entries)\n\n args.onMessage?.('提交任务中…')\n const taskId = await args.client.runApp(args.appId, args.buildNodes(fileNames), args.runOptions)\n\n const files = await args.client.watchTask(taskId, (p) => {\n if (p.tag === 'queued') args.onMessage?.('排队中…')\n else args.onMessage?.(p.percent != null ? `生成中… ${p.percent}%` : '生成中…')\n })\n\n const url = files.find((f) => /\\.(mp4|webm|mov)$/i.test(f.fileUrl))?.fileUrl ?? files[0]?.fileUrl\n if (!url) throw new Error('RunningHub task succeeded but returned no video URL')\n return url\n}\n","// RunningHub implementation of greeting's GreetingVideoPort.\n// Prepares the two greeting-specific inputs — TTS audio (via the injected\n// VoiceSynthesisPort) and the avatar image — then hands the upload→run→watch\n// pipeline to the shared runVideoApp. The schema-required secondary audio slot\n// (node 209) is filled with the embedded 1s silence.\n//\n// Returns the RunningHub CDN URL (~24h expiry) — persist via greeting's\n// GreetingVideoStoragePort / a storage mirror for anything long-lived.\n\nimport type {\n GreetingVideoPort,\n GreetingVideoInput,\n GreetingVideoProgress,\n} from '@surfmate.team/digital-human-greeting'\nimport type { VoiceSynthesisPort } from '@surfmate.team/digital-human-voice'\nimport type { RunningHubClient } from '../client'\nimport { runVideoApp } from '../shared/runVideoApp'\nimport { fetchAvatarAsImageFile } from '../shared/media'\nimport {\n GREETING_APP_ID,\n GREETING_RUN_OPTIONS,\n GREETING_POSITIVE_PROMPT_DEFAULT,\n GREETING_DEFAULT_TTS_MODEL,\n} from './constants'\nimport { buildGreetingNodeInfoList } from './buildNodeInfoList'\nimport { silenceAudioFile } from './silence'\n\nexport type RunningHubGreetingVideoConfig = {\n /** Generic RunningHub client (createRunningHubClient). */\n readonly client: RunningHubClient\n /** TTS backend that turns the greeting text into audio bytes (e.g. MiniMax). */\n readonly synth: VoiceSynthesisPort\n /** TTS model id. Defaults to MiniMax speech-02-turbo. */\n readonly modelId?: string\n /** Action prompt for node 216. Defaults to the \"looks at camera, holds pose\" prompt. */\n readonly positivePrompt?: string\n}\n\nexport function createRunningHubGreetingVideo(\n config: RunningHubGreetingVideoConfig,\n): GreetingVideoPort {\n const { client, synth } = config\n const modelId = config.modelId ?? GREETING_DEFAULT_TTS_MODEL\n const positivePrompt =\n (config.positivePrompt && config.positivePrompt.trim()) || GREETING_POSITIVE_PROMPT_DEFAULT\n\n return {\n async generate(\n input: GreetingVideoInput,\n onProgress?: (p: GreetingVideoProgress) => void,\n ): Promise<string> {\n const msg = (message: string) => onProgress?.({ message })\n\n // Greeting-specific inputs: synthesized speech + the avatar image.\n msg('合成语音中…')\n const audioBytes = await synth.synthesize({ text: input.text, voiceId: input.voiceId, modelId })\n const audioFile = new File([audioBytes], 'greeting-speech.mp3', { type: 'audio/mpeg' })\n\n msg('获取头像中…')\n const imageFile = await fetchAvatarAsImageFile(input.avatarUrl, 'greeting-avatar')\n\n return runVideoApp({\n client,\n appId: GREETING_APP_ID,\n runOptions: GREETING_RUN_OPTIONS,\n uploads: [\n { key: 'image', file: imageFile, fileType: 'image' },\n { key: 'audio', file: audioFile, fileType: 'audio' },\n { key: 'silence', file: silenceAudioFile(), fileType: 'audio' },\n ],\n buildNodes: (n) =>\n buildGreetingNodeInfoList({\n imageFileName: n.image ?? '',\n audioFileName: n.audio ?? '',\n silenceAudioFileName: n.silence ?? '',\n positivePrompt,\n }),\n onMessage: msg,\n })\n },\n }\n}\n","// RunningHub implementation of waiting's WaitingVideoGenerationPort (\"Generate\n// Static\"). Prepares the two waiting-specific inputs — the avatar image and N\n// seconds of silence (which drive the clip length, no TTS) — then hands the\n// upload→run→watch pipeline to the shared runVideoApp.\n//\n// Returns the RunningHub CDN URL (~24h expiry) — persist via the waiting storage\n// port / a mirror for anything long-lived.\n\nimport type {\n WaitingVideoGenerationPort,\n WaitingVideoGenerationInput,\n WaitingVideoProgress,\n} from '@surfmate.team/digital-human-waiting'\nimport type { NodeInfo, RunningHubClient } from '../client'\nimport { runVideoApp } from '../shared/runVideoApp'\nimport { fetchAvatarAsImageFile, buildSilenceWavBlob } from '../shared/media'\n\n// mate's static-waiting AI App + node ids.\nconst STATIC_WAITING_APP_ID = '2050552043860963330'\nconst NODE_IDS = { IMAGE: '34', PROMPT: '17', AUDIO: '38' } as const\nconst RUN_OPTIONS = { usePersonalQueue: false, instanceType: 'default' } as const\n\nexport type RunningHubStaticWaitingVideoConfig = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubStaticWaitingVideo(\n config: RunningHubStaticWaitingVideoConfig,\n): WaitingVideoGenerationPort {\n const { client } = config\n return {\n async generateStatic(\n input: WaitingVideoGenerationInput,\n onProgress?: (p: WaitingVideoProgress) => void,\n ): Promise<string> {\n const msg = (message: string) => onProgress?.({ message })\n\n // Waiting-specific inputs: the avatar image + N seconds of silence.\n msg('获取头像中…')\n const imageFile = await fetchAvatarAsImageFile(input.avatarUrl, `${input.characterId}-avatar`)\n const silence = buildSilenceWavBlob(input.seconds)\n const audioFile = new File([silence], `silence-${input.seconds}s.wav`, { type: 'audio/wav' })\n\n return runVideoApp({\n client,\n appId: STATIC_WAITING_APP_ID,\n runOptions: RUN_OPTIONS,\n uploads: [\n { key: 'image', file: imageFile, fileType: 'image' },\n { key: 'audio', file: audioFile, fileType: 'audio' },\n ],\n buildNodes: (n): NodeInfo[] => [\n { nodeId: NODE_IDS.IMAGE, fieldName: 'image', fieldValue: n.image ?? '' },\n { nodeId: NODE_IDS.PROMPT, fieldName: 'prompt', fieldValue: input.prompt },\n { nodeId: NODE_IDS.AUDIO, fieldName: 'audio', fieldValue: n.audio ?? '' },\n ],\n onMessage: msg,\n })\n },\n }\n}\n","// RunningHub 多人对话 (multi-person lip-sync dialogue) AI App config.\n// InfiniteTalk dual-image driving: one reference image holding two people +\n// three audio tracks → one MP4 where each person lip-syncs to their own voice.\n//\n// The two per-person tracks drive the MOUTHS (each audio only animates its\n// person's face, assigned by the workflow's auto/painted masks); the full\n// conversation track is the OUTPUT soundtrack and sets the total length.\n// (See docs/workflows.md — node ids match the published app, App 2062039217370394626.)\n\nexport const DIALOGUE_APP_ID = '2062039217370394626'\n\nexport const DIALOGUE_NODE_IDS = {\n /** Reference image (the two people). */\n IMAGE: '57',\n /** audio_1 — isolated track that drives the LEFT person's mouth. */\n AUDIO_LEFT: '64',\n /** audio_2 — isolated track that drives the RIGHT person's mouth. */\n AUDIO_RIGHT: '59',\n /** Full conversation — the output soundtrack + drives total video length. */\n AUDIO_FULL: '58',\n /** Action / camera prompt (e.g. 固定镜头). Empty by default. */\n PROMPT: '86',\n} as const\n\n/** Like the other I2V apps, this runs on the shared pool — personal queue → 803. */\nexport const DIALOGUE_RUN_OPTIONS = { usePersonalQueue: false, instanceType: 'default' } as const\n","// RunningHub 多人对话 binding: a two-person lip-sync dialogue video. Self-\n// contained (no feature-package port yet) — it owns its input/port types and\n// composes the generic client via the shared runVideoApp pipeline.\n//\n// The caller prepares three time-aligned audio tracks (e.g. via Web Audio):\n// - leftAudio : only the LEFT person's voice (silence elsewhere) → audio_1\n// - rightAudio : only the RIGHT person's voice (silence elsewhere) → audio_2\n// - fullAudio : the complete conversation (both, mixed) → soundtrack + length\n// All three MUST be the same length and aligned on one timeline.\n//\n// Returns the RunningHub CDN URL (~24h expiry) — persist/mirror anything you\n// need long-lived.\n\nimport type { NodeInfo, RunningHubClient } from '../client'\nimport { runVideoApp } from '../shared/runVideoApp'\nimport { DIALOGUE_APP_ID, DIALOGUE_NODE_IDS, DIALOGUE_RUN_OPTIONS } from './constants'\n\nexport type DialogueVideoInput = {\n /** Reference image containing the two people. */\n readonly image: File\n /** Isolated audio for the LEFT person (drives audio_1). */\n readonly leftAudio: File\n /** Isolated audio for the RIGHT person (drives audio_2). */\n readonly rightAudio: File\n /** The full conversation track — output soundtrack + total length. */\n readonly fullAudio: File\n /** Optional action / camera prompt (e.g. 固定镜头). Omit → empty. */\n readonly prompt?: string | undefined\n}\n\nexport type DialogueVideoProgress = { readonly message: string }\n\nexport interface DialogueVideoPort {\n /** Generate the dialogue video; resolves to the output video URL (~24h CDN). */\n generate(\n input: DialogueVideoInput,\n onProgress?: (p: DialogueVideoProgress) => void,\n ): Promise<string>\n}\n\nexport type RunningHubDialogueVideoConfig = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubDialogueVideo(\n config: RunningHubDialogueVideoConfig,\n): DialogueVideoPort {\n const { client } = config\n return {\n async generate(\n input: DialogueVideoInput,\n onProgress?: (p: DialogueVideoProgress) => void,\n ): Promise<string> {\n const msg = (message: string) => onProgress?.({ message })\n return runVideoApp({\n client,\n appId: DIALOGUE_APP_ID,\n runOptions: DIALOGUE_RUN_OPTIONS,\n uploads: [\n { key: 'image', file: input.image, fileType: 'image' },\n { key: 'left', file: input.leftAudio, fileType: 'audio' },\n { key: 'right', file: input.rightAudio, fileType: 'audio' },\n { key: 'full', file: input.fullAudio, fileType: 'audio' },\n ],\n buildNodes: (n): NodeInfo[] => [\n { nodeId: DIALOGUE_NODE_IDS.IMAGE, fieldName: 'image', fieldValue: n.image ?? '' },\n { nodeId: DIALOGUE_NODE_IDS.AUDIO_LEFT, fieldName: 'audio', fieldValue: n.left ?? '' },\n { nodeId: DIALOGUE_NODE_IDS.AUDIO_RIGHT, fieldName: 'audio', fieldValue: n.right ?? '' },\n { nodeId: DIALOGUE_NODE_IDS.AUDIO_FULL, fieldName: 'audio', fieldValue: n.full ?? '' },\n { nodeId: DIALOGUE_NODE_IDS.PROMPT, fieldName: 'text', fieldValue: input.prompt ?? '' },\n ],\n onMessage: msg,\n })\n },\n }\n}\n","// Shared text-to-image runner. Every RunningHub text-to-image app has the same\n// shape — prompt + width + height + batch → image URL(s), no uploads — and\n// differs ONLY in its app id, the four node ids, and the default dimensions.\n// Each model file (qwen2512, flux2klein, …) supplies that spec; this builds the\n// TextToImagePort once.\n\nimport type { NodeInfo, RunAppOptions, RunningHubClient } from '../client'\nimport type { TextToImageInput, TextToImagePort, TextToImageProgress } from './types'\n\nconst IMAGE_RE = /\\.(png|jpe?g|webp)$/i\n\n/** Per-model spec: the app id, its four node ids, and default dimensions. */\nexport type Text2ImageSpec = {\n readonly appId: string\n readonly nodeIds: {\n readonly prompt: string\n readonly width: string\n readonly height: string\n readonly batch: string\n }\n readonly defaults: { readonly width: number; readonly height: number; readonly batchSize: number }\n /** Defaults to the shared-pool options (personal queue → 803 on most apps). */\n readonly runOptions?: RunAppOptions\n}\n\nconst SHARED_POOL: RunAppOptions = { usePersonalQueue: false, instanceType: 'default' }\n\nexport function createText2ImagePort(client: RunningHubClient, spec: Text2ImageSpec): TextToImagePort {\n return {\n async generate(\n input: TextToImageInput,\n onProgress?: (p: TextToImageProgress) => void,\n ): Promise<string[]> {\n const msg = (message: string) => onProgress?.({ message })\n const width = input.width ?? spec.defaults.width\n const height = input.height ?? spec.defaults.height\n const batchSize = input.batchSize ?? spec.defaults.batchSize\n\n const nodes: NodeInfo[] = [\n { nodeId: spec.nodeIds.prompt, fieldName: 'text', fieldValue: input.prompt },\n { nodeId: spec.nodeIds.width, fieldName: 'value', fieldValue: String(width) },\n { nodeId: spec.nodeIds.height, fieldName: 'value', fieldValue: String(height) },\n { nodeId: spec.nodeIds.batch, fieldName: 'value', fieldValue: String(batchSize) },\n ]\n\n msg('提交任务中…')\n const taskId = await client.runApp(spec.appId, nodes, spec.runOptions ?? SHARED_POOL)\n\n const files = await client.watchTask(taskId, (p) => {\n if (p.tag === 'queued') msg('排队中…')\n else msg(p.percent != null ? `生成中… ${p.percent}%` : '生成中…')\n })\n\n const images = files.filter((f) => IMAGE_RE.test(f.fileUrl)).map((f) => f.fileUrl)\n const urls = images.length > 0 ? images : files.map((f) => f.fileUrl)\n if (urls.length === 0) throw new Error('RunningHub task succeeded but returned no image URL')\n return urls\n },\n }\n}\n","// RunningHub 文生图 — Qwen-Image 2512 binding. Just the per-model spec; the\n// shared runner builds the TextToImagePort. (Published app 2052988750950617090.)\n\nimport type { RunningHubClient } from '../client'\nimport type { TextToImagePort } from './types'\nimport { createText2ImagePort, type Text2ImageSpec } from './run'\n\nexport const QWEN2512_SPEC: Text2ImageSpec = {\n appId: '2052988750950617090',\n // node 6 prompt · 80 width · 81 height · 117 batch size\n nodeIds: { prompt: '6', width: '80', height: '81', batch: '117' },\n defaults: { width: 1920, height: 1080, batchSize: 1 },\n}\n\nexport type RunningHubQwen2512Config = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubQwen2512(config: RunningHubQwen2512Config): TextToImagePort {\n return createText2ImagePort(config.client, QWEN2512_SPEC)\n}\n","// RunningHub 文生图 — Flux2 Klein binding. Same shape as Qwen2512, different\n// app + node ids. (Published app 2026466502756536322.)\n\nimport type { RunningHubClient } from '../client'\nimport type { TextToImagePort } from './types'\nimport { createText2ImagePort, type Text2ImageSpec } from './run'\n\nexport const FLUX2_KLEIN_SPEC: Text2ImageSpec = {\n appId: '2026466502756536322',\n // node 64 prompt · 99 width · 101 height · 103 batch size\n nodeIds: { prompt: '64', width: '99', height: '101', batch: '103' },\n defaults: { width: 1920, height: 1088, batchSize: 1 },\n}\n\nexport type RunningHubFlux2KleinConfig = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubFlux2Klein(config: RunningHubFlux2KleinConfig): TextToImagePort {\n return createText2ImagePort(config.client, FLUX2_KLEIN_SPEC)\n}\n","// Shared image-edit runner. Every RunningHub image-edit app has the same shape:\n// upload 1–N source images → fill the prompt node + the image nodes in order →\n// run → watch → image URL(s). Each model file supplies the spec; this builds\n// the ImageEditPort once.\n\nimport type { NodeInfo, RunAppOptions, RunningHubClient } from '../client'\nimport type { ImageEditInput, ImageEditPort, ImageEditProgress } from './types'\n\nconst IMAGE_RE = /\\.(png|jpe?g|webp)$/i\nconst SHARED_POOL: RunAppOptions = { usePersonalQueue: false, instanceType: 'default' }\n\n/** Per-model spec: the app id, the prompt node, and the image nodes in fill order. */\nexport type ImageEditSpec = {\n readonly appId: string\n readonly promptNodeId: string\n /** Image node ids in fill order (primary first). The Nth image → imageNodeIds[N-1]. */\n readonly imageNodeIds: readonly string[]\n readonly runOptions?: RunAppOptions\n}\n\nconst toFile = (blob: Blob, i: number): File =>\n blob instanceof File ? blob : new File([blob], `image-${i}.png`, { type: blob.type || 'image/png' })\n\nexport function createImageEditPort(client: RunningHubClient, spec: ImageEditSpec): ImageEditPort {\n return {\n async generate(\n input: ImageEditInput,\n onProgress?: (p: ImageEditProgress) => void,\n ): Promise<string[]> {\n const msg = (message: string) => onProgress?.({ message })\n // Primary is required by the type; extras are optional and capped to the\n // model's remaining image slots.\n const images = [input.image, ...(input.extraImages ?? [])].slice(0, spec.imageNodeIds.length)\n\n msg('上传素材中…')\n const names = await Promise.all(images.map((img, i) => client.uploadFile(toFile(img, i), 'image')))\n\n const nodes: NodeInfo[] = [\n { nodeId: spec.promptNodeId, fieldName: 'text', fieldValue: input.prompt },\n ]\n for (let i = 0; i < names.length; i++) {\n const nodeId = spec.imageNodeIds[i]\n const fileValue = names[i]\n if (nodeId && fileValue) nodes.push({ nodeId, fieldName: 'image', fieldValue: fileValue })\n }\n\n msg('提交任务中…')\n const taskId = await client.runApp(spec.appId, nodes, spec.runOptions ?? SHARED_POOL)\n\n const files = await client.watchTask(taskId, (p) => {\n if (p.tag === 'queued') msg('排队中…')\n else msg(p.percent != null ? `生成中… ${p.percent}%` : '生成中…')\n })\n\n const out = files.filter((f) => IMAGE_RE.test(f.fileUrl)).map((f) => f.fileUrl)\n const urls = out.length > 0 ? out : files.map((f) => f.fileUrl)\n if (urls.length === 0) throw new Error('RunningHub task succeeded but returned no image URL')\n return urls\n },\n }\n}\n","// RunningHub 图像编辑 — Qwen-Image 2511 binding. Up to 3 source images + a\n// prompt. (Published app 2025738520135995394.)\n\nimport type { RunningHubClient } from '../client'\nimport type { ImageEditPort } from './types'\nimport { createImageEditPort, type ImageEditSpec } from './run'\n\nexport const QWEN2511_SPEC: ImageEditSpec = {\n appId: '2025738520135995394',\n promptNodeId: '182',\n // primary first: 1 img → 157; 2 → 157,180; 3 → 157,180,181\n imageNodeIds: ['157', '180', '181'],\n}\n\nexport type RunningHubQwen2511Config = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubQwen2511(config: RunningHubQwen2511Config): ImageEditPort {\n return createImageEditPort(config.client, QWEN2511_SPEC)\n}\n","// RunningHub 图像编辑 — Flux image-edit binding. Two images (original + a\n// reference) + a Chinese instruction → an edited image. (Published app\n// 2052941353117597697.)\n\nimport type { RunningHubClient } from '../client'\nimport type { ImageEditPort } from './types'\nimport { createImageEditPort, type ImageEditSpec } from './run'\n\nexport const FLUX_EDIT_SPEC: ImageEditSpec = {\n appId: '2052941353117597697',\n promptNodeId: '178',\n // primary (original) → 175; the reference image → 159\n imageNodeIds: ['175', '159'],\n}\n\nexport type RunningHubFluxEditConfig = {\n readonly client: RunningHubClient\n}\n\nexport function createRunningHubFluxEdit(config: RunningHubFluxEditConfig): ImageEditPort {\n return createImageEditPort(config.client, FLUX_EDIT_SPEC)\n}\n"],"mappings":"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","names":["inner","taskStatus","IMAGE_RE","SHARED_POOL"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@surfmate.team/digital-human-runninghub",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "RunningHub (ComfyUI-as-a-service) adapter. A generic, workflow-agnostic client (upload / run AI app / watch task) plus a greeting-video binding that implements greeting's GreetingVideoPort by composing the client with an injected TTS VoiceSynthesisPort. The client is reusable for ANY RunningHub workflow (waiting videos, image edit, …).",
5
5
  "license": "MIT",
6
6
  "type": "module",