storyboard-bridge 0.3.1 → 0.3.3

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 (2) hide show
  1. package/index.mjs +25 -6
  2. package/package.json +1 -1
package/index.mjs CHANGED
@@ -26,13 +26,13 @@
26
26
  // --cdp <url> CDP_HTTP the debug-Chrome endpoint (default http://localhost:9222)
27
27
  // -- CLAUDE_BIN/GEMINI_BIN path to the CLI binary if not on PATH
28
28
  import { spawn, execFile } from 'node:child_process';
29
- import { writeFile, readFile, unlink, stat, mkdir } from 'node:fs/promises';
29
+ import { writeFile, readFile, unlink, stat, mkdir, mkdtemp, rm } from 'node:fs/promises';
30
30
  import { join, resolve, dirname, sep } from 'node:path';
31
31
  import { fileURLToPath } from 'node:url';
32
32
  import { tmpdir } from 'node:os';
33
33
  import { WebSocket } from 'ws';
34
34
 
35
- const VERSION = '0.3.1';
35
+ const VERSION = '0.3.3';
36
36
 
37
37
  // ---- config ----
38
38
  const arg = (name, fallback) => {
@@ -71,11 +71,11 @@ function wsUrl(httpUrl) {
71
71
  }
72
72
 
73
73
  // ---- the same CLI contract the backend uses: `<bin> -p <prompt> --output-format json [--model m]` ----
74
- function runCli(bin, args, stdinInput) {
74
+ function runCli(bin, args, stdinInput, cwd) {
75
75
  return new Promise((resolve, reject) => {
76
76
  // On Windows the CLI is usually a `.cmd` shim (claude.cmd) that spawn() can't launch without a
77
77
  // shell; shell:true uses PATHEXT to resolve it. POSIX (Linux/WSL/macOS) spawns the binary directly.
78
- const child = spawn(bin, args, { timeout: JOB_TIMEOUT_MS, shell: IS_WIN });
78
+ const child = spawn(bin, args, { timeout: JOB_TIMEOUT_MS, shell: IS_WIN, cwd });
79
79
  let out = '';
80
80
  let err = '';
81
81
  child.stdout.on('data', (d) => (out += d));
@@ -98,7 +98,7 @@ function extractText(stdout) {
98
98
  }
99
99
  return stdout;
100
100
  }
101
- async function runJob(prompt) {
101
+ async function runJob(prompt, images) {
102
102
  // Pass the prompt via STDIN, not argv. On Windows the bridge spawns through cmd.exe (shell:true, to
103
103
  // resolve claude.cmd), and a big multi-line prompt as an arg gets mangled — cmd splits it on newlines,
104
104
  // breaking the command ("'claude' is not recognized"). stdin sidesteps shell parsing entirely; `claude
@@ -106,6 +106,25 @@ async function runJob(prompt) {
106
106
  const args = ['-p'];
107
107
  if (USE_JSON) args.push('--output-format', 'json'); // claude wraps as JSON; gemini prints raw text
108
108
  if (MODEL) args.push('--model', MODEL);
109
+ if (images?.length) {
110
+ // vision: write the attached image(s) into a temp dir, then point the prompt's `@name` refs at each
111
+ // file's ABSOLUTE path. Claude/Gemini resolve a bare `@name` against their own workspace/home (e.g.
112
+ // C:\Users\<you>), NOT our spawn cwd — so a relative ref silently misses and the model can't see the
113
+ // image (it goes looking for "image.png" and reports it can't find it). Absolute paths are cwd-independent.
114
+ const dir = await mkdtemp(join(tmpdir(), 'sbvis-'));
115
+ try {
116
+ let p = prompt;
117
+ for (const im of images) {
118
+ const name = String(im.name);
119
+ const abs = join(dir, name);
120
+ await writeFile(abs, Buffer.from(String(im.data), 'base64'));
121
+ p = p.split('@' + name).join('@' + abs);
122
+ }
123
+ return extractText(await runCli(BIN, args, p, dir));
124
+ } finally {
125
+ await rm(dir, { recursive: true, force: true });
126
+ }
127
+ }
109
128
  return extractText(await runCli(BIN, args, prompt));
110
129
  }
111
130
 
@@ -230,7 +249,7 @@ function connect() {
230
249
  } else if (msg.type === 'job' && msg.id) {
231
250
  const t0 = Date.now();
232
251
  try {
233
- const text = await runJob(String(msg.prompt ?? ''));
252
+ const text = await runJob(String(msg.prompt ?? ''), Array.isArray(msg.images) ? msg.images : null);
234
253
  ws.send(JSON.stringify({ type: 'result', id: msg.id, text }));
235
254
  log(`job ${msg.id} ✓ (${Date.now() - t0}ms)`);
236
255
  } catch (e) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "storyboard-bridge",
3
- "version": "0.3.1",
3
+ "version": "0.3.3",
4
4
  "description": "Desktop bridge that powers a hosted Storyboard AI webapp with your own local Claude Code / Gemini CLI login (no API key).",
5
5
  "type": "module",
6
6
  "bin": {