channel-worker 2.4.2 → 2.4.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.
@@ -1176,18 +1176,30 @@ class CommandPoller {
1176
1176
  const offlineRenderers = renderers.filter(r => !runningRenderers.includes(r));
1177
1177
 
1178
1178
  // 3. Crash recovery
1179
- // 3a. Offline profiles with assigned commands → reset + relaunch
1179
+ // 3a. Offline profiles with assigned commands → reset + relaunch.
1180
+ // BUT: Nstbrowser API can briefly drop a profile from its running list
1181
+ // during heavy ops (image gen, captcha solving). The reset endpoint
1182
+ // returns { skipped: 'heartbeat-fresh' } if the extension is still
1183
+ // sending heartbeats — meaning the profile is alive even though
1184
+ // Nstbrowser doesn't list it. Skip relaunch in that case to avoid
1185
+ // a tabs-close + tabs-open loop.
1180
1186
  for (const r of offlineRenderers) {
1181
1187
  try {
1182
1188
  const cmdCount = await this.api.rendererHasCommands(r.nst_profile_id);
1183
1189
  if (cmdCount > 0) {
1184
- console.log(`[scene-dispatch] Crash recovery: ${r.name} has ${cmdCount} commands but not running — relaunching`);
1190
+ console.log(`[scene-dispatch] Crash recovery: ${r.name} has ${cmdCount} commands but not running — checking heartbeat`);
1185
1191
  try {
1186
- await this.api.resetRendererCommands(r.nst_profile_id);
1192
+ const resetRes = await this.api.resetRendererCommands(r.nst_profile_id);
1193
+ if (resetRes && resetRes.skipped === 'heartbeat-fresh') {
1194
+ console.log(`[scene-dispatch] ${r.name} heartbeat fresh — extension is alive, skipping relaunch`);
1195
+ runningRenderers.push(r);
1196
+ this._profileLastActivity[r.nst_profile_id] = Date.now();
1197
+ continue;
1198
+ }
1187
1199
  await this._launchRendererProfile(r);
1188
1200
  runningRenderers.push(r);
1189
1201
  this._profileLastActivity[r.nst_profile_id] = Date.now();
1190
- console.log(`[scene-dispatch] ${r.name} recovered (${cmdCount} commands reset)`);
1202
+ console.log(`[scene-dispatch] ${r.name} recovered (${(resetRes && resetRes.modified) || cmdCount} commands reset)`);
1191
1203
  } catch (err) {
1192
1204
  console.error(`[scene-dispatch] Failed to recover ${r.name}: ${err.message}`);
1193
1205
  }
@@ -1367,7 +1379,15 @@ class CommandPoller {
1367
1379
  if (this._chatgptDispatching) return;
1368
1380
  this._chatgptDispatching = true;
1369
1381
  try {
1370
- if (!this.nst) return;
1382
+ // Lazy-init NstManager — the daemon config often ships with an empty
1383
+ // nst_api_key and relies on fetching it from the user's settings.
1384
+ if (!this.nst) {
1385
+ try {
1386
+ const apiKey = await this.api.getSetting('nst_api_key');
1387
+ if (apiKey) this.nst = new NstManager(apiKey);
1388
+ } catch {}
1389
+ if (!this.nst) return;
1390
+ }
1371
1391
  const queue = await this.api.getChatgptQueue().catch(() => null);
1372
1392
  if (!queue || !Array.isArray(queue.profiles) || queue.profiles.length === 0) return;
1373
1393
 
@@ -1401,7 +1421,13 @@ class CommandPoller {
1401
1421
  // ─── Profile sync — report local Nstbrowser profile list to API ──────────
1402
1422
  async _syncProfiles() {
1403
1423
  try {
1404
- if (!this.nst) return;
1424
+ if (!this.nst) {
1425
+ try {
1426
+ const apiKey = await this.api.getSetting('nst_api_key');
1427
+ if (apiKey) this.nst = new NstManager(apiKey);
1428
+ } catch {}
1429
+ if (!this.nst) return;
1430
+ }
1405
1431
  const profiles = await this.nst.listAllProfiles();
1406
1432
  if (!profiles || profiles.length === 0) return;
1407
1433
  await this.api.reportNstProfiles(this.config.worker_id, profiles).catch((e) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.4.2",
3
+ "version": "2.4.4",
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": {