channel-worker 2.1.4 → 2.1.6

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.
@@ -1177,21 +1177,21 @@ class CommandPoller {
1177
1177
  } catch {}
1178
1178
  }
1179
1179
 
1180
- // 3b. Running profiles with stale heartbeat + commands → extension stuck, close + reset + relaunch
1181
- const HEARTBEAT_STALE = 90 * 1000; // 90s
1180
+ // 3b. Running profiles with stale heartbeat + PENDING commands → extension never started
1181
+ // Only restart if commands are still "pending" (not "running" = extension picked up but busy)
1182
+ const HEARTBEAT_STALE = 3 * 60 * 1000; // 3 minutes (extension can be busy rendering for 5+ min)
1182
1183
  const now = Date.now();
1183
1184
  for (const r of [...runningRenderers]) {
1184
1185
  try {
1186
+ // Only check profiles that have pending commands (not running = actively processing)
1185
1187
  const cmdCount = await this.api.rendererHasCommands(r.nst_profile_id);
1186
1188
  if (cmdCount === 0) continue;
1187
1189
  const hb = r.last_heartbeat ? new Date(r.last_heartbeat).getTime() : 0;
1188
- if (hb && (now - hb) < HEARTBEAT_STALE) continue; // heartbeat fresh, extension alive
1189
- // Stale heartbeat + has commands → extension stuck
1190
+ if (hb && (now - hb) < HEARTBEAT_STALE) continue;
1190
1191
  console.log(`[scene-dispatch] Stuck: ${r.name} running but heartbeat stale (${hb ? Math.round((now - hb) / 1000) + 's' : 'never'}) — restarting`);
1191
1192
  try {
1192
- await this.nst.stopProfile(r.nst_profile_id).catch(() => {});
1193
1193
  await this.api.resetRendererCommands(r.nst_profile_id);
1194
- await this._launchRendererProfile(r);
1194
+ await this._launchRendererProfile(r, { forceRelaunch: true });
1195
1195
  console.log(`[scene-dispatch] ${r.name} restarted`);
1196
1196
  } catch (err) {
1197
1197
  console.error(`[scene-dispatch] Failed to restart ${r.name}: ${err.message}`);
@@ -1203,13 +1203,14 @@ class CommandPoller {
1203
1203
  const queueCount = await this.api.getSceneQueueCount();
1204
1204
  if (!queueCount) { this._dispatching = false; return; }
1205
1205
 
1206
- // 5. Launch new profiles up to parallel limit
1206
+ // 5. Launch new profiles up to parallel limit (recompute offline after recovery)
1207
1207
  const parallelLimit = parseInt(await this.api.getSetting('veo3_parallel_limit')) || 1;
1208
- console.log(`[scene-dispatch] running=${runningRenderers.length}/${parallelLimit} offline=${offlineRenderers.length} queue=${queueCount} names=[${runningRenderers.map(r=>r.name)}]`);
1208
+ const stillOffline = renderers.filter(r => !runningRenderers.includes(r));
1209
+ console.log(`[scene-dispatch] running=${runningRenderers.length}/${parallelLimit} offline=${stillOffline.length} queue=${queueCount} names=[${runningRenderers.map(r=>r.name)}]`);
1209
1210
 
1210
- const neededLaunches = Math.min(parallelLimit - runningRenderers.length, offlineRenderers.length, queueCount);
1211
+ const neededLaunches = Math.min(parallelLimit - runningRenderers.length, stillOffline.length, queueCount);
1211
1212
  for (let li = 0; li < neededLaunches; li++) {
1212
- const toLaunch = offlineRenderers[li];
1213
+ const toLaunch = stillOffline[li];
1213
1214
  console.log(`[scene-dispatch] Launching ${toLaunch.name} (running: ${runningRenderers.length + li}/${parallelLimit})`);
1214
1215
  try {
1215
1216
  await this._launchRendererProfile(toLaunch);
@@ -1243,7 +1244,7 @@ class CommandPoller {
1243
1244
  this._dispatching = false;
1244
1245
  }
1245
1246
 
1246
- async _launchRendererProfile(renderer) {
1247
+ async _launchRendererProfile(renderer, options = {}) {
1247
1248
  await this.nst.ensureProfile(renderer.nst_profile_id, { os: renderer.os || 'windows', proxy: renderer.proxy || null });
1248
1249
 
1249
1250
  const path = require('path');
@@ -1278,7 +1279,7 @@ class CommandPoller {
1278
1279
  console.warn(`[scene-dispatch] Ext dir failed: ${e.message}, using base`);
1279
1280
  }
1280
1281
 
1281
- await this.nst.launchProfile(renderer.nst_profile_id, { proxy: renderer.proxy || null, extensionPath });
1282
+ await this.nst.launchProfile(renderer.nst_profile_id, { proxy: renderer.proxy || null, extensionPath, forceRelaunch: options.forceRelaunch });
1282
1283
  }
1283
1284
 
1284
1285
  // ─── Profile Timeout ───────────────────────────────────────────────────────
@@ -153,8 +153,18 @@ class NstManager {
153
153
  console.log(`[nst] Running browsers: ${running.map(b => b.name || b.profileId).join(', ') || 'none'}`);
154
154
  const isRunning = running.some(b => b.profileId === profileId);
155
155
  if (isRunning) {
156
- console.log(`[nst] Profile ${profileId} already running — skipping launch`);
157
- return { profileId, alreadyRunning: true };
156
+ if (options.forceRelaunch) {
157
+ console.log(`[nst] Profile ${profileId} already running — force stopping for relaunch`);
158
+ try {
159
+ await this.stopProfile(profileId);
160
+ await new Promise(r => setTimeout(r, 3000));
161
+ } catch (e) {
162
+ console.warn(`[nst] Stop failed: ${e.message}`);
163
+ }
164
+ } else {
165
+ console.log(`[nst] Profile ${profileId} already running — skipping launch`);
166
+ return { profileId, alreadyRunning: true };
167
+ }
158
168
  }
159
169
 
160
170
  // Update profile language to en-US (Custom) before launch
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.1.4",
3
+ "version": "2.1.6",
4
4
  "description": "Channel Manager worker daemon — runs on remote machines to execute video pipeline jobs",
5
5
  "main": "lib/daemon.js",
6
6
  "bin": {