From c5855135a76bd7a494dbfa32e6529a5361370c09 Mon Sep 17 00:00:00 2001 From: Ricardo-M-L Date: Sat, 24 Oct 2026 00:15:35 +0800 Subject: [PATCH] fix: read local image paths starting with http --- src/commands/image/generate.ts | 2 +- src/utils/image.ts | 2 +- test/commands/image/generate.test.ts | 38 ++++++++++++++++++++++++++++ test/commands/video/generate.test.ts | 19 +++++++++++++- 4 files changed, 58 insertions(+), 3 deletions(-) diff --git a/src/commands/image/generate.ts b/src/commands/image/generate.ts index b530a72b..7943d9c6 100644 --- a/src/commands/image/generate.ts +++ b/src/commands/image/generate.ts @@ -118,7 +118,7 @@ export default defineCommand({ }; if (params.image) { - if (params.image.startsWith('http')) { + if (params.image.startsWith('http://') || params.image.startsWith('https://')) { ref.image_url = params.image; } else { ref.image_file = localFileToDataUri(resolve(params.image)); diff --git a/src/utils/image.ts b/src/utils/image.ts index ab53b632..7e3677f2 100644 --- a/src/utils/image.ts +++ b/src/utils/image.ts @@ -30,7 +30,7 @@ export function localFileToDataUri(filePath: string, maxBytes?: number): string } export function resolveImageInput(input: string, maxBytes?: number): string { - return input.startsWith('http') || input.startsWith('data:') || input.startsWith('mm_file://') + return input.startsWith('http://') || input.startsWith('https://') || input.startsWith('data:') || input.startsWith('mm_file://') ? input : localFileToDataUri(input, maxBytes); } diff --git a/test/commands/image/generate.test.ts b/test/commands/image/generate.test.ts index 1cdd56a4..95988199 100644 --- a/test/commands/image/generate.test.ts +++ b/test/commands/image/generate.test.ts @@ -1,4 +1,6 @@ import { describe, it, expect } from 'bun:test'; +import { mkdtempSync, rmSync, writeFileSync } from 'node:fs'; +import { join } from 'node:path'; import { default as generateCommand } from '../../../src/commands/image/generate'; const baseConfig = { @@ -91,4 +93,40 @@ describe('image generate command', () => { expect(parsed.request.seed).toBe(42); expect(parsed.request.model).toBe('image-01'); }); + + it.each(['http-image-', 'https-image-'])('encodes a local subject reference under %s', async (prefix) => { + const dir = mkdtempSync(prefix); + const file = join(dir, 'reference.png'); + const png = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+a0qQAAAAASUVORK5CYII=', 'base64'); + const originalLog = console.log; + let output = ''; + console.log = (message: string) => { output += message; }; + try { + writeFileSync(file, png); + await generateCommand.execute( + { ...baseConfig, dryRun: true, output: 'json' }, + { ...baseFlags, dryRun: true, prompt: 'A cat', subjectRef: `type=character,image=${file}` }, + ); + const reference = JSON.parse(output).request.subject_reference[0]; + expect(reference).toEqual({ type: 'character', image_file: `data:image/png;base64,${png.toString('base64')}` }); + } finally { + console.log = originalLog; + rmSync(dir, { recursive: true, force: true }); + } + }); + + it.each(['http://example.com/reference.png', 'https://example.com/reference.png'])('preserves remote subject reference %s', async (url) => { + const originalLog = console.log; + let output = ''; + console.log = (message: string) => { output += message; }; + try { + await generateCommand.execute( + { ...baseConfig, dryRun: true, output: 'json' }, + { ...baseFlags, dryRun: true, prompt: 'A cat', subjectRef: `type=character,image=${url}` }, + ); + expect(JSON.parse(output).request.subject_reference).toEqual([{ type: 'character', image_url: url }]); + } finally { + console.log = originalLog; + } + }); }); diff --git a/test/commands/video/generate.test.ts b/test/commands/video/generate.test.ts index 71cae547..6711d9ed 100644 --- a/test/commands/video/generate.test.ts +++ b/test/commands/video/generate.test.ts @@ -1,5 +1,5 @@ import { describe, it, expect } from 'bun:test'; -import { mkdtempSync, readFileSync, rmSync } from 'fs'; +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'fs'; import { tmpdir } from 'os'; import { join } from 'path'; import { default as generateCommand } from '../../../src/commands/video/generate'; @@ -52,6 +52,23 @@ async function h3DryRun(flags: Partial): Promise { + it('encodes a local first frame whose relative path starts with http', async () => { + const dir = mkdtempSync('http-video-'); + const file = join(dir, 'frame.png'); + const png = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+a0qQAAAAASUVORK5CYII=', 'base64'); + try { + writeFileSync(file, png); + const body = await h3DryRun({ image: file }); + expect(body.content).toContainEqual({ + type: 'image_url', + image_url: { url: `data:image/png;base64,${png.toString('base64')}` }, + role: 'first_frame', + }); + } finally { + rmSync(dir, { recursive: true, force: true }); + } + }); + it('has correct name', () => { expect(generateCommand.name).toBe('video generate'); });