storyboard-bridge 0.3.1 → 0.3.4
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/index.mjs +41 -7
- 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.
|
|
35
|
+
const VERSION = '0.3.4';
|
|
36
36
|
|
|
37
37
|
// ---- config ----
|
|
38
38
|
const arg = (name, fallback) => {
|
|
@@ -62,6 +62,18 @@ const JOB_TIMEOUT_MS = 300_000;
|
|
|
62
62
|
|
|
63
63
|
const log = (m) => console.log(`[bridge ${new Date().toISOString().slice(11, 19)}] ${m}`);
|
|
64
64
|
|
|
65
|
+
// Is version `a` strictly older than `b`? (numeric x.y.z compare; missing parts = 0.) Used so the
|
|
66
|
+
// update nag fires ONLY when this bridge is behind the backend's advertised latest, never when ahead.
|
|
67
|
+
function isOlderVersion(a, b) {
|
|
68
|
+
const pa = String(a).split('.').map((n) => parseInt(n, 10) || 0);
|
|
69
|
+
const pb = String(b).split('.').map((n) => parseInt(n, 10) || 0);
|
|
70
|
+
for (let i = 0; i < 3; i++) {
|
|
71
|
+
if ((pa[i] || 0) < (pb[i] || 0)) return true;
|
|
72
|
+
if ((pa[i] || 0) > (pb[i] || 0)) return false;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
}
|
|
76
|
+
|
|
65
77
|
function wsUrl(httpUrl) {
|
|
66
78
|
const u = new URL(httpUrl);
|
|
67
79
|
u.protocol = u.protocol === 'https:' ? 'wss:' : 'ws:';
|
|
@@ -71,11 +83,11 @@ function wsUrl(httpUrl) {
|
|
|
71
83
|
}
|
|
72
84
|
|
|
73
85
|
// ---- the same CLI contract the backend uses: `<bin> -p <prompt> --output-format json [--model m]` ----
|
|
74
|
-
function runCli(bin, args, stdinInput) {
|
|
86
|
+
function runCli(bin, args, stdinInput, cwd) {
|
|
75
87
|
return new Promise((resolve, reject) => {
|
|
76
88
|
// On Windows the CLI is usually a `.cmd` shim (claude.cmd) that spawn() can't launch without a
|
|
77
89
|
// 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 });
|
|
90
|
+
const child = spawn(bin, args, { timeout: JOB_TIMEOUT_MS, shell: IS_WIN, cwd });
|
|
79
91
|
let out = '';
|
|
80
92
|
let err = '';
|
|
81
93
|
child.stdout.on('data', (d) => (out += d));
|
|
@@ -98,7 +110,7 @@ function extractText(stdout) {
|
|
|
98
110
|
}
|
|
99
111
|
return stdout;
|
|
100
112
|
}
|
|
101
|
-
async function runJob(prompt) {
|
|
113
|
+
async function runJob(prompt, images) {
|
|
102
114
|
// Pass the prompt via STDIN, not argv. On Windows the bridge spawns through cmd.exe (shell:true, to
|
|
103
115
|
// resolve claude.cmd), and a big multi-line prompt as an arg gets mangled — cmd splits it on newlines,
|
|
104
116
|
// breaking the command ("'claude' is not recognized"). stdin sidesteps shell parsing entirely; `claude
|
|
@@ -106,6 +118,25 @@ async function runJob(prompt) {
|
|
|
106
118
|
const args = ['-p'];
|
|
107
119
|
if (USE_JSON) args.push('--output-format', 'json'); // claude wraps as JSON; gemini prints raw text
|
|
108
120
|
if (MODEL) args.push('--model', MODEL);
|
|
121
|
+
if (images?.length) {
|
|
122
|
+
// vision: write the attached image(s) into a temp dir, then point the prompt's `@name` refs at each
|
|
123
|
+
// file's ABSOLUTE path. Claude/Gemini resolve a bare `@name` against their own workspace/home (e.g.
|
|
124
|
+
// C:\Users\<you>), NOT our spawn cwd — so a relative ref silently misses and the model can't see the
|
|
125
|
+
// image (it goes looking for "image.png" and reports it can't find it). Absolute paths are cwd-independent.
|
|
126
|
+
const dir = await mkdtemp(join(tmpdir(), 'sbvis-'));
|
|
127
|
+
try {
|
|
128
|
+
let p = prompt;
|
|
129
|
+
for (const im of images) {
|
|
130
|
+
const name = String(im.name);
|
|
131
|
+
const abs = join(dir, name);
|
|
132
|
+
await writeFile(abs, Buffer.from(String(im.data), 'base64'));
|
|
133
|
+
p = p.split('@' + name).join('@' + abs);
|
|
134
|
+
}
|
|
135
|
+
return extractText(await runCli(BIN, args, p, dir));
|
|
136
|
+
} finally {
|
|
137
|
+
await rm(dir, { recursive: true, force: true });
|
|
138
|
+
}
|
|
139
|
+
}
|
|
109
140
|
return extractText(await runCli(BIN, args, prompt));
|
|
110
141
|
}
|
|
111
142
|
|
|
@@ -224,13 +255,16 @@ function connect() {
|
|
|
224
255
|
return;
|
|
225
256
|
}
|
|
226
257
|
if (msg.type === 'welcome') {
|
|
227
|
-
|
|
258
|
+
// Only nag when we're genuinely OLDER than what the backend advertises — a plain `!==` also fired
|
|
259
|
+
// when this bridge was NEWER than the backend's advertised version (e.g. mid-rollout, before the
|
|
260
|
+
// backend redeploys), wrongly telling the user to "update" to an older build.
|
|
261
|
+
if (msg.latest && isOlderVersion(VERSION, msg.latest)) {
|
|
228
262
|
log(`UPDATE AVAILABLE: bridge v${msg.latest} (you have v${VERSION}). Update with: npm i -g storyboard-bridge (or just re-run via npx).`);
|
|
229
263
|
}
|
|
230
264
|
} else if (msg.type === 'job' && msg.id) {
|
|
231
265
|
const t0 = Date.now();
|
|
232
266
|
try {
|
|
233
|
-
const text = await runJob(String(msg.prompt ?? ''));
|
|
267
|
+
const text = await runJob(String(msg.prompt ?? ''), Array.isArray(msg.images) ? msg.images : null);
|
|
234
268
|
ws.send(JSON.stringify({ type: 'result', id: msg.id, text }));
|
|
235
269
|
log(`job ${msg.id} ✓ (${Date.now() - t0}ms)`);
|
|
236
270
|
} catch (e) {
|
package/package.json
CHANGED