@lightcone-ai/daemon 0.15.44 → 0.15.46

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightcone-ai/daemon",
3
- "version": "0.15.44",
3
+ "version": "0.15.46",
4
4
  "type": "module",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -22,6 +22,7 @@ import {
22
22
  import { runRecordUrlNarrationTool } from './record-url-narration-tool.js';
23
23
  import { runSubmitToLibraryTool } from './submit-to-library-tool.js';
24
24
  import { runRenderTextToImageTool } from './tools/render-text-to-image.js';
25
+ import { runRenderHtmlToImageTool } from './tools/render-html-to-image.js';
25
26
  import { runSynthesisTtsTool } from './tools/synthesize-tts.js';
26
27
  import { runComposeVideoV2Tool } from './tools/compose-video-v2.js';
27
28
  import { runTakePageScreenshotTool } from './tools/take-page-screenshot.js';
@@ -1421,6 +1422,19 @@ server.tool('render_text_to_image',
1421
1422
  async (args) => runRenderTextToImageTool(args)
1422
1423
  );
1423
1424
 
1425
+ // ── render_html_to_image ───────────────────────────────────────────────────────
1426
+ server.tool('render_html_to_image',
1427
+ 'Render a raw HTML string to a PNG image by navigating to it as a local file:// page. Unlike evaluate_script+document.write on about:blank, this preserves file:// origin so <img src="file:///..."> references load correctly. Returns the output image path.',
1428
+ {
1429
+ html: z.string().describe('Full HTML document to render (including <!doctype>, <html>, <head>, <body>).'),
1430
+ output_path: z.string().optional().describe('Absolute path to save the PNG. Auto-generated in /tmp if omitted.'),
1431
+ viewport_width: z.number().optional().describe('Viewport width in pixels. Default 1080.'),
1432
+ viewport_height: z.number().optional().describe('Viewport height in pixels. Default 1920.'),
1433
+ wait_until: z.enum(['load', 'networkidle', 'domcontentloaded']).optional().describe('Navigation wait condition. Default load.'),
1434
+ },
1435
+ async (args) => runRenderHtmlToImageTool(args)
1436
+ );
1437
+
1424
1438
  // ── synthesize_tts ─────────────────────────────────────────────────────────────
1425
1439
  server.tool('synthesize_tts',
1426
1440
  'Convert text to speech using the workspace MiniMax TTS credential. Returns a local mp3 file path and duration. Use this to generate narration audio for individual video segments.',
@@ -0,0 +1,58 @@
1
+ import { mkdirSync, writeFileSync } from 'fs';
2
+ import { randomUUID } from 'crypto';
3
+ import path from 'path';
4
+ import os from 'os';
5
+
6
+ function toolText(text) {
7
+ return { content: [{ type: 'text', text }] };
8
+ }
9
+
10
+ function toolError(text) {
11
+ return { isError: true, content: [{ type: 'text', text }] };
12
+ }
13
+
14
+ async function launchBrowser() {
15
+ let playwright;
16
+ try {
17
+ playwright = await import('playwright');
18
+ } catch {
19
+ throw new Error('playwright_import_failed: run npm install playwright');
20
+ }
21
+ return playwright.chromium.launch({ headless: true });
22
+ }
23
+
24
+ export async function runRenderHtmlToImageTool({ html, output_path, viewport_width, viewport_height, wait_until }) {
25
+ const normalizedHtml = String(html ?? '').trim();
26
+ if (!normalizedHtml) return toolError('html is required and must not be empty.');
27
+
28
+ const vw = Number(viewport_width ?? 1080);
29
+ const vh = Number(viewport_height ?? 1920);
30
+ const waitUntil = String(wait_until ?? 'load');
31
+
32
+ const tmpDir = path.join(os.tmpdir(), 'lightcone-html-render');
33
+ mkdirSync(tmpDir, { recursive: true });
34
+
35
+ const htmlFile = path.join(tmpDir, `render-${randomUUID().slice(0, 8)}.html`);
36
+ writeFileSync(htmlFile, normalizedHtml, 'utf-8');
37
+
38
+ const outPath = output_path
39
+ ? String(output_path)
40
+ : path.join(tmpDir, `render-${randomUUID().slice(0, 8)}.png`);
41
+
42
+ const browser = await launchBrowser();
43
+ try {
44
+ const page = await browser.newPage();
45
+ await page.setViewportSize({ width: vw, height: vh });
46
+ // Navigate to file:// so local file:// image src attributes load correctly
47
+ await page.goto(`file://${htmlFile}`, { waitUntil, timeout: 30000 });
48
+ await page.screenshot({ path: outPath });
49
+ await page.close();
50
+ return toolText([
51
+ 'render_html_to_image completed.',
52
+ `path=${outPath}`,
53
+ `viewport=${vw}x${vh}`,
54
+ ].join('\n'));
55
+ } finally {
56
+ await browser.close();
57
+ }
58
+ }