channel-worker 2.2.0 → 2.2.1

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.
@@ -1169,6 +1169,7 @@ class CommandPoller {
1169
1169
  await this.api.resetRendererCommands(r.nst_profile_id);
1170
1170
  await this._launchRendererProfile(r);
1171
1171
  runningRenderers.push(r);
1172
+ this._profileLastActivity[r.nst_profile_id] = Date.now();
1172
1173
  console.log(`[scene-dispatch] ${r.name} recovered (${cmdCount} commands reset)`);
1173
1174
  } catch (err) {
1174
1175
  console.error(`[scene-dispatch] Failed to recover ${r.name}: ${err.message}`);
@@ -1179,19 +1180,24 @@ class CommandPoller {
1179
1180
 
1180
1181
  // 3b. Running profiles with stale heartbeat + PENDING commands → extension never started
1181
1182
  // Only restart if commands are still "pending" (not "running" = extension picked up but busy)
1182
- const HEARTBEAT_STALE = 30 * 1000; // 30s — extension heartbeats every 15s even when busy
1183
+ const HEARTBEAT_STALE = 60 * 1000; // 60s — extension heartbeats every 15s
1184
+ const LAUNCH_GRACE = 60 * 1000; // 60s grace period after launch for extension to init
1183
1185
  const now = Date.now();
1184
1186
  for (const r of [...runningRenderers]) {
1185
1187
  try {
1186
- // Only check profiles that have pending commands (not running = actively processing)
1187
1188
  const cmdCount = await this.api.rendererHasCommands(r.nst_profile_id);
1188
1189
  if (cmdCount === 0) continue;
1190
+ // Skip if recently launched (grace period for extension to init + first heartbeat)
1191
+ const lastLaunch = this._profileLastActivity[r.nst_profile_id] || 0;
1192
+ if (lastLaunch && (now - lastLaunch) < LAUNCH_GRACE) continue;
1189
1193
  const hb = r.last_heartbeat ? new Date(r.last_heartbeat).getTime() : 0;
1190
- if (hb && (now - hb) < HEARTBEAT_STALE) continue;
1194
+ if (hb && (now - hb) < HEARTBEAT_STALE) continue; // heartbeat fresh
1195
+ if (!hb && lastLaunch) continue; // never heartbeated but recently tracked — still in grace
1191
1196
  console.log(`[scene-dispatch] Stuck: ${r.name} running but heartbeat stale (${hb ? Math.round((now - hb) / 1000) + 's' : 'never'}) — restarting`);
1192
1197
  try {
1193
1198
  await this.api.resetRendererCommands(r.nst_profile_id);
1194
1199
  await this._launchRendererProfile(r, { forceRelaunch: true });
1200
+ this._profileLastActivity[r.nst_profile_id] = Date.now();
1195
1201
  console.log(`[scene-dispatch] ${r.name} restarted`);
1196
1202
  } catch (err) {
1197
1203
  console.error(`[scene-dispatch] Failed to restart ${r.name}: ${err.message}`);
@@ -1214,7 +1220,8 @@ class CommandPoller {
1214
1220
  console.log(`[scene-dispatch] Launching ${toLaunch.name} (running: ${runningRenderers.length + li}/${parallelLimit})`);
1215
1221
  try {
1216
1222
  await this._launchRendererProfile(toLaunch);
1217
- runningRenderers.push(toLaunch); // now considered running
1223
+ runningRenderers.push(toLaunch);
1224
+ this._profileLastActivity[toLaunch.nst_profile_id] = Date.now();
1218
1225
  console.log(`[scene-dispatch] ${toLaunch.name} launched`);
1219
1226
  } catch (err) {
1220
1227
  console.error(`[scene-dispatch] Failed to launch ${toLaunch.name}: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.2.0",
3
+ "version": "2.2.1",
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": {