storyboard-bridge 0.3.8 → 0.4.0
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 +30 -7
- package/package.json +1 -1
package/index.mjs
CHANGED
|
@@ -304,8 +304,9 @@ async function handleHiggsfieldCli(msg, ws) {
|
|
|
304
304
|
if (!o?.id) throw new Error('higgsfield upload returned no id');
|
|
305
305
|
medias.push({ role: 'image', data: { id: o.id, type: 'media_input' } }); // 'media_input' = uploaded file ('image' is rejected)
|
|
306
306
|
}
|
|
307
|
+
const promptStr = String(p.prompt ?? '');
|
|
307
308
|
const args = ['generate', 'create', String(p.slug || 'seedance_2_0'),
|
|
308
|
-
'--prompt',
|
|
309
|
+
'--prompt', promptStr.startsWith('@') ? ' ' + promptStr : promptStr, // leading '@' = "read from file" to the CLI; a space neutralizes it
|
|
309
310
|
'--duration', String(Math.max(1, Math.round(p.durationSec || 5))),
|
|
310
311
|
'--resolution', String(p.resolution || '720p'),
|
|
311
312
|
'--aspect_ratio', String(p.aspectRatio || '16:9'),
|
|
@@ -325,6 +326,24 @@ async function handleHiggsfieldCli(msg, ws) {
|
|
|
325
326
|
}
|
|
326
327
|
}
|
|
327
328
|
|
|
329
|
+
// ---- disconnect-proof result delivery ----------------------------------------
|
|
330
|
+
// claude -p runs independently of the socket, so a WS drop mid-job must NOT lose the answer. Job results go
|
|
331
|
+
// through deliver(): sent on the live socket if open, else QUEUED in an outbox and flushed when we reconnect.
|
|
332
|
+
// The backend keeps the job pending across the drop and resolves it by id on whichever socket the result
|
|
333
|
+
// arrives on — so a deploy/network blip no longer turns a real answer into a mock fallback.
|
|
334
|
+
let activeWs = null;
|
|
335
|
+
const outbox = []; // JSON frame strings that couldn't be sent because the socket was down
|
|
336
|
+
function deliver(frame) {
|
|
337
|
+
const s = JSON.stringify(frame);
|
|
338
|
+
if (activeWs && activeWs.readyState === 1 /* WebSocket.OPEN */) { try { activeWs.send(s); return; } catch { /* fall through → queue */ } }
|
|
339
|
+
outbox.push(s);
|
|
340
|
+
}
|
|
341
|
+
function flushOutbox() {
|
|
342
|
+
if (!activeWs || activeWs.readyState !== 1) return;
|
|
343
|
+
while (outbox.length) { try { activeWs.send(outbox.shift()); } catch { break; } }
|
|
344
|
+
if (outbox.length === 0) return;
|
|
345
|
+
}
|
|
346
|
+
|
|
328
347
|
// ---- connection (reconnects forever with backoff) ----
|
|
329
348
|
let backoff = 1000;
|
|
330
349
|
function connect() {
|
|
@@ -333,6 +352,8 @@ function connect() {
|
|
|
333
352
|
|
|
334
353
|
ws.on('open', async () => {
|
|
335
354
|
backoff = 1000;
|
|
355
|
+
activeWs = ws; // this socket is now the delivery channel
|
|
356
|
+
flushOutbox(); // re-send any results that completed while we were disconnected
|
|
336
357
|
const providers = {
|
|
337
358
|
claude: await cliAvailable(BIN_FOR.claude),
|
|
338
359
|
gemini: await cliAvailable(BIN_FOR.gemini),
|
|
@@ -363,18 +384,18 @@ function connect() {
|
|
|
363
384
|
const t0 = Date.now();
|
|
364
385
|
try {
|
|
365
386
|
const text = await runJob(String(msg.prompt ?? ''), Array.isArray(msg.images) ? msg.images : null);
|
|
366
|
-
|
|
367
|
-
log(`job ${msg.id} ✓ (${Date.now() - t0}ms)`);
|
|
387
|
+
deliver({ type: 'result', id: msg.id, text }); // survives a mid-job reconnect (queued + flushed)
|
|
388
|
+
log(`job ${msg.id} ✓ (${Date.now() - t0}ms)${activeWs?.readyState === 1 ? '' : ' [queued — offline]'}`);
|
|
368
389
|
} catch (e) {
|
|
369
|
-
|
|
390
|
+
deliver({ type: 'error', id: msg.id, message: String(e?.message ?? e) });
|
|
370
391
|
log(`job ${msg.id} ✗ ${e?.message ?? e}`);
|
|
371
392
|
}
|
|
372
393
|
} else if (msg.type === 'file' && msg.id) {
|
|
373
394
|
try {
|
|
374
395
|
const r = await handleFile(msg);
|
|
375
|
-
|
|
396
|
+
deliver({ type: 'fileResult', id: msg.id, ...r });
|
|
376
397
|
} catch (e) {
|
|
377
|
-
|
|
398
|
+
deliver({ type: 'fileError', id: msg.id, message: String(e?.message ?? e) });
|
|
378
399
|
log(`file ${msg.op} ${msg.id} ✗ ${e?.message ?? e}`);
|
|
379
400
|
}
|
|
380
401
|
} else if (msg.type === 'higgsfield' && msg.id) {
|
|
@@ -393,7 +414,9 @@ function connect() {
|
|
|
393
414
|
});
|
|
394
415
|
|
|
395
416
|
ws.on('close', () => {
|
|
396
|
-
|
|
417
|
+
if (activeWs === ws) activeWs = null; // stop sending on the dead socket; results queue until we reconnect
|
|
418
|
+
const queued = outbox.length ? ` (${outbox.length} result(s) queued for re-delivery)` : '';
|
|
419
|
+
log(`disconnected — retrying in ${Math.round(backoff / 1000)}s${queued}`);
|
|
397
420
|
setTimeout(connect, backoff);
|
|
398
421
|
backoff = Math.min(backoff * 2, 30000);
|
|
399
422
|
});
|
package/package.json
CHANGED