channel-worker 2.5.2 → 2.5.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/lib/command-poller.js +66 -35
- package/package.json +1 -1
package/lib/command-poller.js
CHANGED
|
@@ -1336,26 +1336,81 @@ class CommandPoller {
|
|
|
1336
1336
|
} catch (e) {
|
|
1337
1337
|
if (this.config.verbose) console.warn('[scene-dispatch] getMyWorker failed, using default:', e.message);
|
|
1338
1338
|
}
|
|
1339
|
-
|
|
1340
|
-
|
|
1339
|
+
// Treat 'paused' renderers as NOT eligible to launch — operator clicked
|
|
1340
|
+
// Pause on the Renderers tab; profile should stay closed until Resume.
|
|
1341
|
+
const isPaused = (r) => r && r.health_state === 'paused'
|
|
1342
|
+
&& (!r.pause_until || new Date(r.pause_until).getTime() > Date.now());
|
|
1343
|
+
const stillOffline = renderers.filter(r => !runningRenderers.includes(r) && !isPaused(r));
|
|
1344
|
+
const pausedCount = renderers.filter(isPaused).length;
|
|
1345
|
+
console.log(`[scene-dispatch] running=${runningRenderers.length} cap=${parallelLimit} (flowkit=${flowkitQ} dom=${domQ}) offline=${stillOffline.length} paused=${pausedCount} queue=${queueCount} names=[${runningRenderers.map(r=>r.name)}]`);
|
|
1346
|
+
|
|
1347
|
+
// Launch policy: STRICT parallel_limit cap on physical launches.
|
|
1348
|
+
// With pc2.parallel_limit=1 + 2 renderers (veo03/veo04 share same Veo
|
|
1349
|
+
// Google account), only ONE of them is launched at any time. The
|
|
1350
|
+
// second stays closed until the first finishes / is paused / cap is
|
|
1351
|
+
// bumped. This prevents Google flagging "2 concurrent sessions on
|
|
1352
|
+
// same account" → captcha.
|
|
1353
|
+
//
|
|
1354
|
+
// Earlier iteration tried "launch all, cap only claims" — that left
|
|
1355
|
+
// both browsers open, both pinging Flow (token refresh, telemetry),
|
|
1356
|
+
// and Veo's per-account session monitor counted them as 2 active
|
|
1357
|
+
// sessions → tripped captcha on the supposedly-idle one.
|
|
1358
|
+
const needNew = Math.max(0, queueCount);
|
|
1359
|
+
const neededLaunches = Math.min(
|
|
1360
|
+
parallelLimit - runningRenderers.length,
|
|
1361
|
+
stillOffline.length,
|
|
1362
|
+
needNew,
|
|
1363
|
+
);
|
|
1364
|
+
for (let li = 0; li < Math.max(0, neededLaunches); li++) {
|
|
1365
|
+
const toLaunch = stillOffline[li];
|
|
1366
|
+
console.log(`[scene-dispatch] Launching ${toLaunch.name} (${runningRenderers.length + li + 1}/${parallelLimit})`);
|
|
1367
|
+
try {
|
|
1368
|
+
await this._launchRendererProfile(toLaunch);
|
|
1369
|
+
runningRenderers.push(toLaunch);
|
|
1370
|
+
this._profileLastActivity[toLaunch.nst_profile_id] = Date.now();
|
|
1371
|
+
console.log(`[scene-dispatch] ${toLaunch.name} launched`);
|
|
1372
|
+
} catch (err) {
|
|
1373
|
+
console.error(`[scene-dispatch] Failed to launch ${toLaunch.name}: ${err.message}`);
|
|
1374
|
+
}
|
|
1375
|
+
}
|
|
1376
|
+
|
|
1377
|
+
// Close any currently-running renderers that just became paused —
|
|
1378
|
+
// operator clicked Pause while a profile was open. Skip if it has an
|
|
1379
|
+
// in-flight command (let it finish naturally); cleanup happens on the
|
|
1380
|
+
// next poll cycle.
|
|
1381
|
+
for (const r of [...runningRenderers]) {
|
|
1382
|
+
if (!isPaused(r)) continue;
|
|
1383
|
+
try {
|
|
1384
|
+
const c = await this.api.rendererHasCommands(r.nst_profile_id);
|
|
1385
|
+
if (c > 0) continue;
|
|
1386
|
+
console.log(`[scene-dispatch] Closing paused renderer ${r.name}`);
|
|
1387
|
+
await this.nst.stopProfile(r.nst_profile_id);
|
|
1388
|
+
delete this._profileLastActivity[r.nst_profile_id];
|
|
1389
|
+
if (r.name) delete this._profileLastActivity[r.name.toLowerCase()];
|
|
1390
|
+
const idx = runningRenderers.indexOf(r);
|
|
1391
|
+
if (idx >= 0) runningRenderers.splice(idx, 1);
|
|
1392
|
+
} catch (e) {
|
|
1393
|
+
console.warn(`[scene-dispatch] Failed to close paused ${r.name}: ${e.message}`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1341
1396
|
|
|
1342
|
-
//
|
|
1343
|
-
//
|
|
1344
|
-
//
|
|
1397
|
+
// Close EXCESS running profiles when count > parallel_limit. Happens
|
|
1398
|
+
// when operator drops the cap (eg 2→1 to throttle captcha) — the
|
|
1399
|
+
// already-launched 2nd profile should retire so it doesn't keep
|
|
1400
|
+
// refreshing tokens against the shared Google account. Only close
|
|
1401
|
+
// if it has no in-flight command (let work finish first).
|
|
1345
1402
|
if (runningRenderers.length > parallelLimit) {
|
|
1346
1403
|
const excess = runningRenderers.length - parallelLimit;
|
|
1347
|
-
const
|
|
1404
|
+
const idleRunning = [];
|
|
1348
1405
|
for (const r of runningRenderers) {
|
|
1349
|
-
// Only daemon-launched profiles are eligible — skip manual launches
|
|
1350
1406
|
if (!this._profileLastActivity[r.nst_profile_id]) continue;
|
|
1351
1407
|
try {
|
|
1352
1408
|
const c = await this.api.rendererHasCommands(r.nst_profile_id);
|
|
1353
|
-
if (c === 0)
|
|
1409
|
+
if (c === 0) idleRunning.push(r);
|
|
1354
1410
|
} catch {}
|
|
1355
1411
|
}
|
|
1356
|
-
const
|
|
1357
|
-
|
|
1358
|
-
console.log(`[scene-dispatch] Closing excess profile ${r.name} (over cap ${parallelLimit})`);
|
|
1412
|
+
for (const r of idleRunning.slice(0, excess)) {
|
|
1413
|
+
console.log(`[scene-dispatch] Closing excess ${r.name} (cap=${parallelLimit})`);
|
|
1359
1414
|
try {
|
|
1360
1415
|
await this.nst.stopProfile(r.nst_profile_id);
|
|
1361
1416
|
delete this._profileLastActivity[r.nst_profile_id];
|
|
@@ -1368,30 +1423,6 @@ class CommandPoller {
|
|
|
1368
1423
|
}
|
|
1369
1424
|
}
|
|
1370
1425
|
|
|
1371
|
-
// Count how many running profiles are actually free (no commands)
|
|
1372
|
-
let freeRunning = 0;
|
|
1373
|
-
for (const r of runningRenderers) {
|
|
1374
|
-
try {
|
|
1375
|
-
const c = await this.api.rendererHasCommands(r.nst_profile_id);
|
|
1376
|
-
if (c === 0) freeRunning++;
|
|
1377
|
-
} catch {}
|
|
1378
|
-
}
|
|
1379
|
-
// Only launch new profiles if queue has more work than free profiles
|
|
1380
|
-
const needNew = Math.max(0, queueCount - freeRunning);
|
|
1381
|
-
const neededLaunches = Math.min(parallelLimit - runningRenderers.length, stillOffline.length, needNew);
|
|
1382
|
-
for (let li = 0; li < neededLaunches; li++) {
|
|
1383
|
-
const toLaunch = stillOffline[li];
|
|
1384
|
-
console.log(`[scene-dispatch] Launching ${toLaunch.name} (running: ${runningRenderers.length + li}/${parallelLimit})`);
|
|
1385
|
-
try {
|
|
1386
|
-
await this._launchRendererProfile(toLaunch);
|
|
1387
|
-
runningRenderers.push(toLaunch);
|
|
1388
|
-
this._profileLastActivity[toLaunch.nst_profile_id] = Date.now();
|
|
1389
|
-
console.log(`[scene-dispatch] ${toLaunch.name} launched`);
|
|
1390
|
-
} catch (err) {
|
|
1391
|
-
console.error(`[scene-dispatch] Failed to launch ${toLaunch.name}: ${err.message}`);
|
|
1392
|
-
}
|
|
1393
|
-
}
|
|
1394
|
-
|
|
1395
1426
|
if (runningRenderers.length === 0) {
|
|
1396
1427
|
this._dispatching = false;
|
|
1397
1428
|
return;
|