distark-render 1.0.6 → 1.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. package/README.md +158 -154
  2. package/dist/cli/animate.d.ts +8 -0
  3. package/dist/cli/animate.d.ts.map +1 -0
  4. package/dist/cli/animate.js +121 -0
  5. package/dist/cli/animate.js.map +1 -0
  6. package/dist/cli/diff.d.ts +8 -0
  7. package/dist/cli/diff.d.ts.map +1 -0
  8. package/dist/cli/diff.js +124 -0
  9. package/dist/cli/diff.js.map +1 -0
  10. package/dist/cli/distark-check.d.ts +13 -0
  11. package/dist/cli/distark-check.d.ts.map +1 -0
  12. package/dist/cli/distark-check.js +69 -0
  13. package/dist/cli/distark-check.js.map +1 -0
  14. package/dist/cli/query.d.ts +12 -0
  15. package/dist/cli/query.d.ts.map +1 -0
  16. package/dist/cli/query.js +64 -0
  17. package/dist/cli/query.js.map +1 -0
  18. package/dist/cli/record.d.ts +13 -0
  19. package/dist/cli/record.d.ts.map +1 -0
  20. package/dist/cli/record.js +97 -0
  21. package/dist/cli/record.js.map +1 -0
  22. package/dist/cli/render.d.ts +40 -0
  23. package/dist/cli/render.d.ts.map +1 -0
  24. package/dist/cli/render.js +75 -0
  25. package/dist/cli/render.js.map +1 -0
  26. package/dist/cli/shared.d.ts +6 -0
  27. package/dist/cli/shared.d.ts.map +1 -0
  28. package/dist/cli/shared.js +54 -0
  29. package/dist/cli/shared.js.map +1 -0
  30. package/dist/cli/test.d.ts +13 -0
  31. package/dist/cli/test.d.ts.map +1 -0
  32. package/dist/cli/test.js +130 -0
  33. package/dist/cli/test.js.map +1 -0
  34. package/dist/cli/verify.d.ts +21 -0
  35. package/dist/cli/verify.d.ts.map +1 -0
  36. package/dist/cli/verify.js +175 -0
  37. package/dist/cli/verify.js.map +1 -0
  38. package/dist/modules/adapters/p5Renderer.d.ts +0 -0
  39. package/dist/modules/adapters/p5Renderer.d.ts.map +0 -0
  40. package/dist/modules/adapters/p5Renderer.js +0 -0
  41. package/dist/modules/adapters/p5Renderer.js.map +0 -0
  42. package/dist/modules/adapters/skiaRenderer.d.ts +0 -0
  43. package/dist/modules/adapters/skiaRenderer.d.ts.map +0 -0
  44. package/dist/modules/adapters/skiaRenderer.js +0 -0
  45. package/dist/modules/adapters/skiaRenderer.js.map +0 -0
  46. package/dist/modules/animationDiff.d.ts +0 -0
  47. package/dist/modules/animationDiff.d.ts.map +0 -0
  48. package/dist/modules/animationDiff.js +0 -0
  49. package/dist/modules/animationDiff.js.map +0 -0
  50. package/dist/modules/eyeSystem.d.ts +0 -0
  51. package/dist/modules/eyeSystem.d.ts.map +0 -0
  52. package/dist/modules/eyeSystem.js +0 -0
  53. package/dist/modules/eyeSystem.js.map +0 -0
  54. package/dist/modules/imageLoad.d.ts +0 -0
  55. package/dist/modules/imageLoad.d.ts.map +0 -0
  56. package/dist/modules/imageLoad.js +0 -0
  57. package/dist/modules/imageLoad.js.map +0 -0
  58. package/dist/modules/mouthSystem.d.ts +0 -0
  59. package/dist/modules/mouthSystem.d.ts.map +0 -0
  60. package/dist/modules/mouthSystem.js +0 -0
  61. package/dist/modules/mouthSystem.js.map +0 -0
  62. package/dist/modules/renderRig.d.ts +0 -0
  63. package/dist/modules/renderRig.d.ts.map +0 -0
  64. package/dist/modules/renderRig.js +0 -0
  65. package/dist/modules/renderRig.js.map +0 -0
  66. package/dist/tests/test-animation-diff-rendering.d.ts +7 -0
  67. package/dist/tests/test-animation-diff-rendering.d.ts.map +1 -0
  68. package/dist/tests/test-animation-diff-rendering.js +444 -0
  69. package/dist/tests/test-animation-diff-rendering.js.map +1 -0
  70. package/dist/tests/test-animation-diff.d.ts +7 -0
  71. package/dist/tests/test-animation-diff.d.ts.map +1 -0
  72. package/dist/tests/test-animation-diff.js +488 -0
  73. package/dist/tests/test-animation-diff.js.map +1 -0
  74. package/dist/tests/test-cli.d.ts +8 -0
  75. package/dist/tests/test-cli.d.ts.map +1 -0
  76. package/dist/tests/test-cli.js +307 -0
  77. package/dist/tests/test-cli.js.map +1 -0
  78. package/dist/tests/test-joint-movement.d.ts +0 -0
  79. package/dist/tests/test-joint-movement.d.ts.map +0 -0
  80. package/dist/tests/test-joint-movement.js +0 -0
  81. package/dist/tests/test-joint-movement.js.map +0 -0
  82. package/dist/tests/test-skia.d.ts +0 -0
  83. package/dist/tests/test-skia.d.ts.map +0 -0
  84. package/dist/tests/test-skia.js +0 -0
  85. package/dist/tests/test-skia.js.map +0 -0
  86. package/dist/tests/test-visual-verification.d.ts +0 -0
  87. package/dist/tests/test-visual-verification.d.ts.map +0 -0
  88. package/dist/tests/test-visual-verification.js +0 -0
  89. package/dist/tests/test-visual-verification.js.map +0 -0
  90. package/dist/types.d.ts +0 -0
  91. package/dist/types.d.ts.map +0 -0
  92. package/dist/types.js +0 -0
  93. package/dist/types.js.map +0 -0
  94. package/package.json +9 -2
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * distark-check - CLI tool for LLM-driven rig testing
4
+ *
5
+ * Commands:
6
+ * render <rig.json> Render rig to PNG + JSON report
7
+ * verify <rig.json> Math-based sanity checks (no rendering)
8
+ * animate <rig.json> <animation.json> Render animation frames + manifest
9
+ * diff <before.png> <after.png> Pixel-diff two images
10
+ * query <image|rig> --prompt "..." Send to Gemini for visual analysis
11
+ */
12
+ import { runRender } from './render.js';
13
+ import { runVerify } from './verify.js';
14
+ import { runAnimate } from './animate.js';
15
+ import { runDiff } from './diff.js';
16
+ import { runQuery } from './query.js';
17
+ import { runRecord } from './record.js';
18
+ import { runTest } from './test.js';
19
+ const USAGE = `distark-check - CLI tool for LLM-driven rig testing
20
+
21
+ Commands:
22
+ render <rig.json> [-o out.png] [--width N] [--height N] [--report]
23
+ verify <rig.json> [--checks all|bounds,visibility,proportions,zorder]
24
+ animate <rig.json> <animation.json> [-o frames/] [--width N] [--height N]
25
+ diff <before.png> <after.png> [-o diff.png] [--threshold N]
26
+ query <image.png|rig.json> --prompt "..." [-o report.json]
27
+ test <rig.json|image|video> --prompt "..." [--tries N] [-o report.json]
28
+ record <world.json> [--orc https://orchestrator.distark.com] [-o out.mp4] [--timeout 300]
29
+
30
+ Environment:
31
+ GEMINI_API_KEY Required for 'query' and 'test' commands
32
+ ORC_URL Orchestrator URL for 'record' command (default: http://localhost:3000)
33
+ `;
34
+ async function main() {
35
+ const args = process.argv.slice(2);
36
+ const command = args[0];
37
+ const commandArgs = args.slice(1);
38
+ switch (command) {
39
+ case 'render':
40
+ await runRender(commandArgs);
41
+ break;
42
+ case 'verify':
43
+ await runVerify(commandArgs);
44
+ break;
45
+ case 'animate':
46
+ await runAnimate(commandArgs);
47
+ break;
48
+ case 'diff':
49
+ await runDiff(commandArgs);
50
+ break;
51
+ case 'query':
52
+ await runQuery(commandArgs);
53
+ break;
54
+ case 'record':
55
+ await runRecord(commandArgs);
56
+ break;
57
+ case 'test':
58
+ await runTest(commandArgs);
59
+ break;
60
+ default:
61
+ console.log(USAGE);
62
+ process.exit(command ? 1 : 0);
63
+ }
64
+ }
65
+ main().catch(err => {
66
+ console.error(err.message || err);
67
+ process.exit(1);
68
+ });
69
+ //# sourceMappingURL=distark-check.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"distark-check.js","sourceRoot":"","sources":["../../cli/distark-check.ts"],"names":[],"mappings":";AACA;;;;;;;;;GASG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,KAAK,GAAG;;;;;;;;;;;;;;CAcb,CAAC;AAEF,KAAK,UAAU,IAAI;IACf,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,MAAM,OAAO,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;IACxB,MAAM,WAAW,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAElC,QAAQ,OAAO,EAAE,CAAC;QACd,KAAK,QAAQ;YACT,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;YAC7B,MAAM;QACV,KAAK,QAAQ;YACT,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;YAC7B,MAAM;QACV,KAAK,SAAS;YACV,MAAM,UAAU,CAAC,WAAW,CAAC,CAAC;YAC9B,MAAM;QACV,KAAK,MAAM;YACP,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;YAC3B,MAAM;QACV,KAAK,OAAO;YACR,MAAM,QAAQ,CAAC,WAAW,CAAC,CAAC;YAC5B,MAAM;QACV,KAAK,QAAQ;YACT,MAAM,SAAS,CAAC,WAAW,CAAC,CAAC;YAC7B,MAAM;QACV,KAAK,MAAM;YACP,MAAM,OAAO,CAAC,WAAW,CAAC,CAAC;YAC3B,MAAM;QACV;YACI,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;YACnB,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACtC,CAAC;AACL,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE;IACf,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,OAAO,IAAI,GAAG,CAAC,CAAC;IAClC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACpB,CAAC,CAAC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * query command - Send a rendered image to Gemini for visual analysis
3
+ *
4
+ * Usage:
5
+ * distark-check query <image.png> --prompt "Does the character look natural?"
6
+ * distark-check query <rig.json> --prompt "Describe the character pose"
7
+ *
8
+ * Requires GEMINI_API_KEY environment variable.
9
+ * Uses gemini-2.5-flash model.
10
+ */
11
+ export declare function runQuery(args: string[]): Promise<void>;
12
+ //# sourceMappingURL=query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.d.ts","sourceRoot":"","sources":["../../cli/query.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAYH,wBAAsB,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA2D5D"}
@@ -0,0 +1,64 @@
1
+ /**
2
+ * query command - Send a rendered image to Gemini for visual analysis
3
+ *
4
+ * Usage:
5
+ * distark-check query <image.png> --prompt "Does the character look natural?"
6
+ * distark-check query <rig.json> --prompt "Describe the character pose"
7
+ *
8
+ * Requires GEMINI_API_KEY environment variable.
9
+ * Uses gemini-2.5-flash model.
10
+ */
11
+ import { readFile, writeFile } from 'fs/promises';
12
+ import { renderRigToBuffer, callGemini } from './shared.js';
13
+ export async function runQuery(args) {
14
+ const positional = args.filter(a => !a.startsWith('-') && !a.startsWith('--'));
15
+ const inputFile = positional[0];
16
+ if (!inputFile) {
17
+ console.error('Usage: distark-check query <image.png|rig.json> --prompt "..."');
18
+ process.exit(1);
19
+ }
20
+ const promptIdx = args.indexOf('--prompt');
21
+ if (promptIdx === -1 || !args[promptIdx + 1]) {
22
+ console.error('--prompt is required');
23
+ process.exit(1);
24
+ }
25
+ const prompt = args[promptIdx + 1];
26
+ const width = parseInt(args[args.indexOf('--width') + 1]) || 1000;
27
+ const height = parseInt(args[args.indexOf('--height') + 1]) || 1000;
28
+ const apiKey = process.env.GEMINI_API_KEY;
29
+ if (!apiKey) {
30
+ console.error('GEMINI_API_KEY environment variable is required');
31
+ process.exit(1);
32
+ }
33
+ // If input is JSON, render it first; if PNG, read directly
34
+ let imageBuffer;
35
+ let mimeType;
36
+ if (inputFile.endsWith('.json')) {
37
+ console.error(`Rendering ${inputFile} to image...`);
38
+ imageBuffer = await renderRigToBuffer(inputFile, width, height);
39
+ mimeType = 'image/png';
40
+ }
41
+ else {
42
+ imageBuffer = await readFile(inputFile);
43
+ mimeType = inputFile.endsWith('.jpg') || inputFile.endsWith('.jpeg')
44
+ ? 'image/jpeg'
45
+ : 'image/png';
46
+ }
47
+ const imageBase64 = Buffer.from(imageBuffer).toString('base64');
48
+ console.error(`Querying Gemini (gemini-2.5-flash)...`);
49
+ const response = await callGemini(imageBase64, mimeType, prompt, apiKey);
50
+ const report = {
51
+ input: inputFile,
52
+ model: 'gemini-2.5-flash',
53
+ prompt,
54
+ response,
55
+ };
56
+ // Also save response to file if requested
57
+ const outIdx = args.indexOf('-o');
58
+ if (outIdx !== -1 && args[outIdx + 1]) {
59
+ await writeFile(args[outIdx + 1], JSON.stringify(report, null, 2));
60
+ console.error(`Saved: ${args[outIdx + 1]}`);
61
+ }
62
+ console.log(JSON.stringify(report, null, 2));
63
+ }
64
+ //# sourceMappingURL=query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"query.js","sourceRoot":"","sources":["../../cli/query.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * record command - Send a world JSON to orchestrator for recording, poll until done
3
+ *
4
+ * Usage:
5
+ * distark-check record <world.json> [--orc http://localhost:3000] [-o output.mp4] [--timeout 300]
6
+ *
7
+ * Flow:
8
+ * 1. POST world JSON to orchestrator /recording endpoint
9
+ * 2. Poll /recording/status/:key every 3 seconds
10
+ * 3. When done, download video to output path
11
+ */
12
+ export declare function runRecord(args: string[]): Promise<void>;
13
+ //# sourceMappingURL=record.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.d.ts","sourceRoot":"","sources":["../../cli/record.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAkBH,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgG7D"}
@@ -0,0 +1,97 @@
1
+ /**
2
+ * record command - Send a world JSON to orchestrator for recording, poll until done
3
+ *
4
+ * Usage:
5
+ * distark-check record <world.json> [--orc http://localhost:3000] [-o output.mp4] [--timeout 300]
6
+ *
7
+ * Flow:
8
+ * 1. POST world JSON to orchestrator /recording endpoint
9
+ * 2. Poll /recording/status/:key every 3 seconds
10
+ * 3. When done, download video to output path
11
+ */
12
+ import { readFile, writeFile } from 'fs/promises';
13
+ function sleep(ms) {
14
+ return new Promise(resolve => setTimeout(resolve, ms));
15
+ }
16
+ export async function runRecord(args) {
17
+ const inputFile = args.find(a => !a.startsWith('-')) || '';
18
+ if (!inputFile) {
19
+ console.error('Usage: distark-check record <world.json> [--orc http://localhost:3000] [-o output.mp4] [--timeout 300]');
20
+ process.exit(1);
21
+ }
22
+ const orcIdx = args.indexOf('--orc');
23
+ const orcBase = orcIdx !== -1 ? args[orcIdx + 1] : (process.env.ORC_URL || 'https://orchestrator.distark.com');
24
+ const outIdx = args.indexOf('-o');
25
+ const outputFile = outIdx !== -1 ? args[outIdx + 1] : inputFile.replace(/\.json$/, '.mp4');
26
+ const timeoutIdx = args.indexOf('--timeout');
27
+ const timeoutSec = timeoutIdx !== -1 ? parseInt(args[timeoutIdx + 1]) : 300;
28
+ // Read world JSON
29
+ const worldJson = await readFile(inputFile, 'utf-8');
30
+ // Validate it's valid JSON
31
+ JSON.parse(worldJson);
32
+ console.error(`Posting world JSON to ${orcBase}/recording ...`);
33
+ // POST to orchestrator
34
+ const postResp = await fetch(`${orcBase}/recording`, {
35
+ method: 'POST',
36
+ headers: { 'Content-Type': 'application/json' },
37
+ body: JSON.stringify({ world_json: worldJson }),
38
+ });
39
+ if (!postResp.ok) {
40
+ const errBody = await postResp.text();
41
+ throw new Error(`Orchestrator POST failed (${postResp.status}): ${errBody}`);
42
+ }
43
+ const postData = await postResp.json();
44
+ const key = postData.key;
45
+ if (!key) {
46
+ throw new Error(`No key returned from orchestrator: ${JSON.stringify(postData)}`);
47
+ }
48
+ console.error(`Recording started. Key: ${key}`);
49
+ console.error(`Polling ${orcBase}/recording/status/${key} ...`);
50
+ // Poll for completion
51
+ const startTime = Date.now();
52
+ const pollInterval = 3000;
53
+ let result = null;
54
+ while (true) {
55
+ const elapsed = (Date.now() - startTime) / 1000;
56
+ if (elapsed > timeoutSec) {
57
+ throw new Error(`Timed out after ${timeoutSec}s waiting for recording to complete`);
58
+ }
59
+ await sleep(pollInterval);
60
+ const statusResp = await fetch(`${orcBase}/recording/status/${key}`);
61
+ if (!statusResp.ok) {
62
+ console.error(` Status poll returned ${statusResp.status}, retrying...`);
63
+ continue;
64
+ }
65
+ result = await statusResp.json();
66
+ const elapsedRound = Math.round(elapsed);
67
+ console.error(` [${elapsedRound}s] status: ${result.status}`);
68
+ if (result.status === 'done')
69
+ break;
70
+ if (result.status === 'failed') {
71
+ throw new Error(`Recording failed: ${result.error || 'unknown error'}`);
72
+ }
73
+ }
74
+ const elapsedTotal = Math.round((Date.now() - startTime) / 1000);
75
+ // Download video if we have a URL
76
+ if (result.download_url) {
77
+ console.error(`Downloading video from ${result.download_url} ...`);
78
+ const videoResp = await fetch(result.download_url);
79
+ if (!videoResp.ok) {
80
+ throw new Error(`Failed to download video: ${videoResp.status}`);
81
+ }
82
+ const videoBuffer = Buffer.from(await videoResp.arrayBuffer());
83
+ await writeFile(outputFile, videoBuffer);
84
+ console.error(`Saved: ${outputFile} (${(videoBuffer.length / 1024 / 1024).toFixed(1)} MB)`);
85
+ }
86
+ const report = {
87
+ input: inputFile,
88
+ output: outputFile,
89
+ key,
90
+ status: result.status,
91
+ media_id: result.media_id,
92
+ download_url: result.download_url,
93
+ elapsed_seconds: elapsedTotal,
94
+ };
95
+ console.log(JSON.stringify(report, null, 2));
96
+ }
97
+ //# sourceMappingURL=record.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"record.js","sourceRoot":"","sources":["../../cli/record.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,40 @@
1
+ /**
2
+ * render command - Render a rig JSON to PNG and output a structured JSON report
3
+ *
4
+ * Usage:
5
+ * distark-check render <rig.json> [-o output.png] [--width 1000] [--height 1000] [--report]
6
+ */
7
+ export interface RenderReport {
8
+ input: string;
9
+ output: string;
10
+ dimensions: {
11
+ width: number;
12
+ height: number;
13
+ };
14
+ characters: Array<{
15
+ name: string;
16
+ type: string;
17
+ bounds: {
18
+ x: number;
19
+ y: number;
20
+ w: number;
21
+ h: number;
22
+ };
23
+ center: {
24
+ x: number;
25
+ y: number;
26
+ };
27
+ rotation: number;
28
+ zIndex: number;
29
+ hasImage: boolean;
30
+ }>;
31
+ visible_parts: string[];
32
+ missing_images: string[];
33
+ pivot_points: Array<{
34
+ name: string;
35
+ x: number;
36
+ y: number;
37
+ }>;
38
+ }
39
+ export declare function runRender(args: string[]): Promise<void>;
40
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../cli/render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAMH,MAAM,WAAW,YAAY;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC;IAC9C,UAAU,EAAE,KAAK,CAAC;QACd,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,MAAM,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACvD,MAAM,EAAE;YAAE,CAAC,EAAE,MAAM,CAAC;YAAC,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACjC,QAAQ,EAAE,MAAM,CAAC;QACjB,MAAM,EAAE,MAAM,CAAC;QACf,QAAQ,EAAE,OAAO,CAAC;KACrB,CAAC,CAAC;IACH,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,YAAY,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC/D;AAwCD,wBAAsB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CA8C7D"}
@@ -0,0 +1,75 @@
1
+ /**
2
+ * render command - Render a rig JSON to PNG and output a structured JSON report
3
+ *
4
+ * Usage:
5
+ * distark-check render <rig.json> [-o output.png] [--width 1000] [--height 1000] [--report]
6
+ */
7
+ import { SkiaRenderer } from '../modules/adapters/skiaRenderer.js';
8
+ import { readFile, writeFile } from 'fs/promises';
9
+ function computeBounds(obj) {
10
+ const drawX = obj.x - obj.width * obj.anchorX;
11
+ const drawY = obj.y - obj.height * obj.anchorY;
12
+ return { x: Math.round(drawX), y: Math.round(drawY), w: Math.round(obj.width), h: Math.round(obj.height) };
13
+ }
14
+ function buildReport(inputFile, outputFile, width, height, renderData) {
15
+ const characters = renderData.objects.map(obj => ({
16
+ name: obj.name,
17
+ type: obj.type,
18
+ bounds: computeBounds(obj),
19
+ center: { x: Math.round(obj.x), y: Math.round(obj.y) },
20
+ rotation: Math.round(obj.rotation * 180 / Math.PI * 100) / 100,
21
+ zIndex: obj.zIndex,
22
+ hasImage: !!obj.imageData,
23
+ }));
24
+ return {
25
+ input: inputFile,
26
+ output: outputFile,
27
+ dimensions: { width, height },
28
+ characters,
29
+ visible_parts: characters.filter(c => c.hasImage).map(c => c.name),
30
+ missing_images: characters.filter(c => !c.hasImage).map(c => c.name),
31
+ pivot_points: renderData.pivotPoints.map(p => ({
32
+ name: p.name,
33
+ x: Math.round(p.x),
34
+ y: Math.round(p.y),
35
+ })),
36
+ };
37
+ }
38
+ export async function runRender(args) {
39
+ const inputFile = args.find(a => !a.startsWith('-')) || '';
40
+ if (!inputFile) {
41
+ console.error('Usage: distark-check render <rig.json> [-o output.png] [--width N] [--height N] [--report]');
42
+ process.exit(1);
43
+ }
44
+ const outputIdx = args.indexOf('-o');
45
+ const outputFile = outputIdx !== -1 ? args[outputIdx + 1] : inputFile.replace(/\.json$/, '.png');
46
+ const width = parseInt(args[args.indexOf('--width') + 1]) || 1000;
47
+ const height = parseInt(args[args.indexOf('--height') + 1]) || 1000;
48
+ const wantReport = args.includes('--report');
49
+ const rigData = JSON.parse(await readFile(inputFile, 'utf-8'));
50
+ // Redirect console.log to stderr during image loading (SkiaImageLoader logs progress)
51
+ const origLog = console.log;
52
+ console.log = (...a) => console.error(...a);
53
+ const renderer = new SkiaRenderer();
54
+ await renderer.loadImages(rigData);
55
+ // Render to file
56
+ await renderer.renderToFile(outputFile, rigData, {
57
+ canvasWidth: width,
58
+ canvasHeight: height,
59
+ showPivots: false,
60
+ });
61
+ // Restore console.log
62
+ console.log = origLog;
63
+ console.error(`Rendered: ${outputFile}`);
64
+ // Compute report data
65
+ const renderData = renderer.compute(rigData, { canvasWidth: width, canvasHeight: height });
66
+ const report = buildReport(inputFile, outputFile, width, height, renderData);
67
+ if (wantReport) {
68
+ const reportFile = outputFile.replace(/\.png$/, '.report.json');
69
+ await writeFile(reportFile, JSON.stringify(report, null, 2));
70
+ console.error(`Report: ${reportFile}`);
71
+ }
72
+ // Always output report to stdout for LLM consumption (clean JSON only)
73
+ console.log(JSON.stringify(report, null, 2));
74
+ }
75
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../cli/render.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AACnE,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAqBlD,SAAS,aAAa,CAAC,GAAiB;IACpC,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC;IAC9C,MAAM,KAAK,GAAG,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,OAAO,CAAC;IAC/C,OAAO,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC;AAC/G,CAAC;AAED,SAAS,WAAW,CAChB,SAAiB,EACjB,UAAkB,EAClB,KAAa,EACb,MAAc,EACd,UAAyB;IAEzB,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC9C,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,MAAM,EAAE,aAAa,CAAC,GAAG,CAAC;QAC1B,MAAM,EAAE,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE;QACtD,QAAQ,EAAE,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,QAAQ,GAAG,GAAG,GAAG,IAAI,CAAC,EAAE,GAAG,GAAG,CAAC,GAAG,GAAG;QAC9D,MAAM,EAAE,GAAG,CAAC,MAAM;QAClB,QAAQ,EAAE,CAAC,CAAC,GAAG,CAAC,SAAS;KAC5B,CAAC,CAAC,CAAC;IAEJ,OAAO;QACH,KAAK,EAAE,SAAS;QAChB,MAAM,EAAE,UAAU;QAClB,UAAU,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE;QAC7B,UAAU;QACV,aAAa,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QAClE,cAAc,EAAE,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC;QACpE,YAAY,EAAE,UAAU,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YAC3C,IAAI,EAAE,CAAC,CAAC,IAAI;YACZ,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;YAClB,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;SACrB,CAAC,CAAC;KACN,CAAC;AACN,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,SAAS,CAAC,IAAc;IAC1C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAC3D,IAAI,CAAC,SAAS,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,4FAA4F,CAAC,CAAC;QAC5G,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpB,CAAC;IAED,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IACrC,MAAM,UAAU,GAAG,SAAS,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC;IACjG,MAAM,KAAK,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAClE,MAAM,MAAM,GAAG,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IACpE,MAAM,UAAU,GAAG,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC;IAE7C,MAAM,OAAO,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC,CAAC;IAExE,sFAAsF;IACtF,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC;IAC5B,OAAO,CAAC,GAAG,GAAG,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IAEvD,MAAM,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAC;IACpC,MAAM,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,CAAC;IAEnC,iBAAiB;IACjB,MAAM,QAAQ,CAAC,YAAY,CAAC,UAAU,EAAE,OAAO,EAAE;QAC7C,WAAW,EAAE,KAAK;QAClB,YAAY,EAAE,MAAM;QACpB,UAAU,EAAE,KAAK;KACpB,CAAC,CAAC;IAEH,sBAAsB;IACtB,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC;IAEtB,OAAO,CAAC,KAAK,CAAC,aAAa,UAAU,EAAE,CAAC,CAAC;IAEzC,sBAAsB;IACtB,MAAM,UAAU,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;IAC3F,MAAM,MAAM,GAAG,WAAW,CAAC,SAAS,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,CAAC,CAAC;IAE7E,IAAI,UAAU,EAAE,CAAC;QACb,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE,cAAc,CAAC,CAAC;QAChE,MAAM,SAAS,CAAC,UAAU,EAAE,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;QAC7D,OAAO,CAAC,KAAK,CAAC,WAAW,UAAU,EAAE,CAAC,CAAC;IAC3C,CAAC;IAED,uEAAuE;IACvE,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAC;AACjD,CAAC"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Shared helpers for CLI commands (query, test, etc.)
3
+ */
4
+ export declare function renderRigToBuffer(rigFile: string, width: number, height: number): Promise<Buffer>;
5
+ export declare function callGemini(base64Data: string, mimeType: string, prompt: string, apiKey: string): Promise<string>;
6
+ //# sourceMappingURL=shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../cli/shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAMH,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CASvG;AAED,wBAAsB,UAAU,CAAC,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyCtH"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Shared helpers for CLI commands (query, test, etc.)
3
+ */
4
+ import { readFile } from 'fs/promises';
5
+ import { SkiaRenderer } from '../modules/adapters/skiaRenderer.js';
6
+ export async function renderRigToBuffer(rigFile, width, height) {
7
+ const rigData = JSON.parse(await readFile(rigFile, 'utf-8'));
8
+ // Redirect console.log to stderr during image loading
9
+ const origLog = console.log;
10
+ console.log = (...a) => console.error(...a);
11
+ const renderer = new SkiaRenderer();
12
+ const buf = await renderer.renderToBuffer(rigData, { canvasWidth: width, canvasHeight: height });
13
+ console.log = origLog;
14
+ return buf;
15
+ }
16
+ export async function callGemini(base64Data, mimeType, prompt, apiKey) {
17
+ const model = 'gemini-2.5-flash';
18
+ const url = `https://generativelanguage.googleapis.com/v1beta/models/${model}:generateContent?key=${apiKey}`;
19
+ const body = {
20
+ contents: [{
21
+ parts: [
22
+ {
23
+ inline_data: {
24
+ mime_type: mimeType,
25
+ data: base64Data,
26
+ },
27
+ },
28
+ {
29
+ text: prompt,
30
+ },
31
+ ],
32
+ }],
33
+ generationConfig: {
34
+ temperature: 0.2,
35
+ maxOutputTokens: 2048,
36
+ },
37
+ };
38
+ const resp = await fetch(url, {
39
+ method: 'POST',
40
+ headers: { 'Content-Type': 'application/json' },
41
+ body: JSON.stringify(body),
42
+ });
43
+ if (!resp.ok) {
44
+ const errText = await resp.text();
45
+ throw new Error(`Gemini API error (${resp.status}): ${errText}`);
46
+ }
47
+ const data = await resp.json();
48
+ const text = data?.candidates?.[0]?.content?.parts?.[0]?.text;
49
+ if (!text) {
50
+ throw new Error(`Unexpected Gemini response: ${JSON.stringify(data)}`);
51
+ }
52
+ return text;
53
+ }
54
+ //# sourceMappingURL=shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shared.js","sourceRoot":"","sources":["../../cli/shared.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AACvC,OAAO,EAAE,YAAY,EAAE,MAAM,qCAAqC,CAAC;AAGnE,MAAM,CAAC,KAAK,UAAU,iBAAiB,CAAC,OAAe,EAAE,KAAa,EAAE,MAAc;IAClF,MAAM,OAAO,GAAY,IAAI,CAAC,KAAK,CAAC,MAAM,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC;IACtE,sDAAsD;IACtD,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC;IAC5B,OAAO,CAAC,GAAG,GAAG,CAAC,GAAG,CAAY,EAAE,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;IACvD,MAAM,QAAQ,GAAG,IAAI,YAAY,EAAE,CAAC;IACpC,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,cAAc,CAAC,OAAO,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,YAAY,EAAE,MAAM,EAAE,CAAC,CAAC;IACjG,OAAO,CAAC,GAAG,GAAG,OAAO,CAAC;IACtB,OAAO,GAAG,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,UAAkB,EAAE,QAAgB,EAAE,MAAc,EAAE,MAAc;IACjG,MAAM,KAAK,GAAG,kBAAkB,CAAC;IACjC,MAAM,GAAG,GAAG,2DAA2D,KAAK,wBAAwB,MAAM,EAAE,CAAC;IAE7G,MAAM,IAAI,GAAG;QACT,QAAQ,EAAE,CAAC;gBACP,KAAK,EAAE;oBACH;wBACI,WAAW,EAAE;4BACT,SAAS,EAAE,QAAQ;4BACnB,IAAI,EAAE,UAAU;yBACnB;qBACJ;oBACD;wBACI,IAAI,EAAE,MAAM;qBACf;iBACJ;aACJ,CAAC;QACF,gBAAgB,EAAE;YACd,WAAW,EAAE,GAAG;YAChB,eAAe,EAAE,IAAI;SACxB;KACJ,CAAC;IAEF,MAAM,IAAI,GAAG,MAAM,KAAK,CAAC,GAAG,EAAE;QAC1B,MAAM,EAAE,MAAM;QACd,OAAO,EAAE,EAAE,cAAc,EAAE,kBAAkB,EAAE;QAC/C,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;KAC7B,CAAC,CAAC;IAEH,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;QACX,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CAAC,qBAAqB,IAAI,CAAC,MAAM,MAAM,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC;IAED,MAAM,IAAI,GAAG,MAAM,IAAI,CAAC,IAAI,EAAE,CAAC;IAC/B,MAAM,IAAI,GAAG,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,CAAC;IAC9D,IAAI,CAAC,IAAI,EAAE,CAAC;QACR,MAAM,IAAI,KAAK,CAAC,+BAA+B,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IAC3E,CAAC;IACD,OAAO,IAAI,CAAC;AAChB,CAAC"}
@@ -0,0 +1,13 @@
1
+ /**
2
+ * test command - Run a pass/fail visual assertion against a rig, image, or video
3
+ *
4
+ * Usage:
5
+ * distark-check test <rig.json> --prompt "Does the character have both eyes visible?" [--tries 2]
6
+ * distark-check test <image.png> --prompt "Are both arms visible?"
7
+ * distark-check test <video.mp4> --prompt "Does the character wave its hand?"
8
+ *
9
+ * Exit code 0 = pass, 1 = fail
10
+ * Requires GEMINI_API_KEY environment variable.
11
+ */
12
+ export declare function runTest(args: string[]): Promise<void>;
13
+ //# sourceMappingURL=test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.d.ts","sourceRoot":"","sources":["../../cli/test.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAwDH,wBAAsB,OAAO,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAwF3D"}
@@ -0,0 +1,130 @@
1
+ /**
2
+ * test command - Run a pass/fail visual assertion against a rig, image, or video
3
+ *
4
+ * Usage:
5
+ * distark-check test <rig.json> --prompt "Does the character have both eyes visible?" [--tries 2]
6
+ * distark-check test <image.png> --prompt "Are both arms visible?"
7
+ * distark-check test <video.mp4> --prompt "Does the character wave its hand?"
8
+ *
9
+ * Exit code 0 = pass, 1 = fail
10
+ * Requires GEMINI_API_KEY environment variable.
11
+ */
12
+ import { readFile, writeFile } from 'fs/promises';
13
+ import { renderRigToBuffer, callGemini } from './shared.js';
14
+ const VIDEO_EXTENSIONS = ['.mp4', '.webm'];
15
+ const IMAGE_EXTENSIONS = ['.png', '.jpg', '.jpeg'];
16
+ function getMimeType(file) {
17
+ if (file.endsWith('.mp4'))
18
+ return 'video/mp4';
19
+ if (file.endsWith('.webm'))
20
+ return 'video/webm';
21
+ if (file.endsWith('.jpg') || file.endsWith('.jpeg'))
22
+ return 'image/jpeg';
23
+ return 'image/png';
24
+ }
25
+ function isVideo(file) {
26
+ return VIDEO_EXTENSIONS.some(ext => file.endsWith(ext));
27
+ }
28
+ function isImage(file) {
29
+ return IMAGE_EXTENSIONS.some(ext => file.endsWith(ext));
30
+ }
31
+ function parsePassFail(response) {
32
+ // Strip markdown code fences if present
33
+ let cleaned = response.trim();
34
+ if (cleaned.startsWith('```')) {
35
+ cleaned = cleaned.replace(/^```(?:json)?\s*\n?/, '').replace(/\n?```\s*$/, '');
36
+ }
37
+ try {
38
+ const parsed = JSON.parse(cleaned);
39
+ return {
40
+ pass: !!parsed.pass,
41
+ reason: parsed.reason || '',
42
+ };
43
+ }
44
+ catch {
45
+ // Fallback: look for pass/fail keywords
46
+ const lower = response.toLowerCase();
47
+ const pass = lower.includes('"pass": true') || lower.includes('"pass":true');
48
+ return {
49
+ pass,
50
+ reason: response.slice(0, 200),
51
+ };
52
+ }
53
+ }
54
+ export async function runTest(args) {
55
+ const positional = args.filter(a => !a.startsWith('-') && !a.startsWith('--'));
56
+ const inputFile = positional[0];
57
+ if (!inputFile) {
58
+ console.error('Usage: distark-check test <rig.json|image|video> --prompt "..." [--tries N]');
59
+ process.exit(1);
60
+ }
61
+ const promptIdx = args.indexOf('--prompt');
62
+ if (promptIdx === -1 || !args[promptIdx + 1]) {
63
+ console.error('--prompt is required');
64
+ process.exit(1);
65
+ }
66
+ const userPrompt = args[promptIdx + 1];
67
+ const triesIdx = args.indexOf('--tries');
68
+ const tries = triesIdx !== -1 ? parseInt(args[triesIdx + 1]) || 1 : 1;
69
+ const width = parseInt(args[args.indexOf('--width') + 1]) || 1000;
70
+ const height = parseInt(args[args.indexOf('--height') + 1]) || 1000;
71
+ const apiKey = process.env.GEMINI_API_KEY;
72
+ if (!apiKey) {
73
+ console.error('GEMINI_API_KEY environment variable is required');
74
+ process.exit(1);
75
+ }
76
+ // Load media
77
+ let mediaBase64;
78
+ let mimeType;
79
+ if (inputFile.endsWith('.json')) {
80
+ console.error(`Rendering ${inputFile} to image...`);
81
+ const buf = await renderRigToBuffer(inputFile, width, height);
82
+ mediaBase64 = Buffer.from(buf).toString('base64');
83
+ mimeType = 'image/png';
84
+ }
85
+ else if (isVideo(inputFile) || isImage(inputFile)) {
86
+ const buf = await readFile(inputFile);
87
+ mediaBase64 = Buffer.from(buf).toString('base64');
88
+ mimeType = getMimeType(inputFile);
89
+ }
90
+ else {
91
+ console.error(`Unsupported file type: ${inputFile}`);
92
+ process.exit(1);
93
+ }
94
+ const mediaLabel = isVideo(inputFile) ? 'video' : 'image';
95
+ const wrappedPrompt = [
96
+ `Answer the following yes/no question about this ${mediaLabel}.`,
97
+ `Respond ONLY with JSON: {"pass": true, "reason": "brief explanation"} or {"pass": false, "reason": "brief explanation"}`,
98
+ ``,
99
+ `Question: ${userPrompt}`,
100
+ ].join('\n');
101
+ let pass = false;
102
+ let reason = '';
103
+ let attempt = 0;
104
+ for (attempt = 1; attempt <= tries; attempt++) {
105
+ console.error(`Attempt ${attempt}/${tries}: querying Gemini...`);
106
+ const response = await callGemini(mediaBase64, mimeType, wrappedPrompt, apiKey);
107
+ const result = parsePassFail(response);
108
+ pass = result.pass;
109
+ reason = result.reason;
110
+ console.error(` Result: ${pass ? 'PASS' : 'FAIL'} - ${reason}`);
111
+ if (pass)
112
+ break;
113
+ }
114
+ const report = {
115
+ input: inputFile,
116
+ prompt: userPrompt,
117
+ pass,
118
+ reason,
119
+ attempts: attempt,
120
+ tries,
121
+ };
122
+ const outIdx = args.indexOf('-o');
123
+ if (outIdx !== -1 && args[outIdx + 1]) {
124
+ await writeFile(args[outIdx + 1], JSON.stringify(report, null, 2));
125
+ console.error(`Saved: ${args[outIdx + 1]}`);
126
+ }
127
+ console.log(JSON.stringify(report, null, 2));
128
+ process.exit(pass ? 0 : 1);
129
+ }
130
+ //# sourceMappingURL=test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"test.js","sourceRoot":"","sources":["../../cli/test.ts"],"names":[],"mappings":"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"}