channel-worker 2.3.5 → 2.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/lib/api-client.js CHANGED
@@ -83,6 +83,13 @@ class ApiClient {
83
83
  return this.request('GET', '/workers/scene-queue-count');
84
84
  }
85
85
 
86
+ // ChatGPT subscription dispatch — returns { count, profile_id } so the
87
+ // daemon knows whether to launch a dedicated capture profile (distinct
88
+ // from the Veo3/FlowKit renderers pool).
89
+ async getChatgptQueue() {
90
+ return this.request('GET', '/workers/chatgpt-queue-count');
91
+ }
92
+
86
93
  async sceneDispatch(nstProfileId) {
87
94
  return this.request('POST', '/workers/scene-dispatch', { nst_profile_id: nstProfileId });
88
95
  }
@@ -31,14 +31,21 @@ class CommandPoller {
31
31
  this._dispatchTimer = setInterval(() => this._dispatchScenes(), 5000);
32
32
  this._dispatching = false;
33
33
 
34
- // Profile timeoutclose idle profiles after 3 minutes with no work
35
- this._profileTimeoutTimer = setInterval(() => this._checkProfileTimeouts(), 60000);
34
+ // ChatGPT-subscription dispatcherlaunches the configured profile when
35
+ // a thumbnail command is waiting, independent from the scene renderers pool.
36
+ console.log('[chatgpt-dispatch] Started (every 5s)');
37
+ this._chatgptDispatchTimer = setInterval(() => this._dispatchChatgpt(), 5000);
38
+ this._chatgptDispatching = false;
39
+
40
+ // Profile timeout — close idle profiles after 30s with no work
41
+ this._profileTimeoutTimer = setInterval(() => this._checkProfileTimeouts(), 10000);
36
42
  this._profileLastActivity = {}; // { nst_profile_id: timestamp }
37
43
  }
38
44
 
39
45
  stop() {
40
46
  if (this.timer) { clearInterval(this.timer); this.timer = null; }
41
47
  if (this._dispatchTimer) { clearInterval(this._dispatchTimer); this._dispatchTimer = null; }
48
+ if (this._chatgptDispatchTimer) { clearInterval(this._chatgptDispatchTimer); this._chatgptDispatchTimer = null; }
42
49
  if (this._profileTimeoutTimer) { clearInterval(this._profileTimeoutTimer); this._profileTimeoutTimer = null; }
43
50
  }
44
51
 
@@ -1234,6 +1241,8 @@ class CommandPoller {
1234
1241
  const excess = runningRenderers.length - parallelLimit;
1235
1242
  const idleRenderers = [];
1236
1243
  for (const r of runningRenderers) {
1244
+ // Only daemon-launched profiles are eligible — skip manual launches
1245
+ if (!this._profileLastActivity[r.nst_profile_id]) continue;
1237
1246
  try {
1238
1247
  const c = await this.api.rendererHasCommands(r.nst_profile_id);
1239
1248
  if (c === 0) idleRenderers.push(r);
@@ -1341,6 +1350,47 @@ class CommandPoller {
1341
1350
  await this.nst.launchProfile(renderer.nst_profile_id, { proxy: renderer.proxy || null, extensionPath, forceRelaunch: options.forceRelaunch });
1342
1351
  }
1343
1352
 
1353
+ // ─── ChatGPT Subscription Dispatch ─────────────────────────────────────────
1354
+ // Separate dispatcher for the dedicated ChatGPT profile (configured via
1355
+ // Setting: chatgpt_sub_profile_id). Launches the profile on-demand when a
1356
+ // generate_thumbnail command is pending, so Veo3 renderers stay untouched.
1357
+
1358
+ async _dispatchChatgpt() {
1359
+ if (this._chatgptDispatching) return;
1360
+ this._chatgptDispatching = true;
1361
+ try {
1362
+ if (!this.nst) return;
1363
+ const queue = await this.api.getChatgptQueue().catch(() => null);
1364
+ if (!queue || !queue.profile_id || queue.count <= 0) return;
1365
+
1366
+ const profileId = queue.profile_id;
1367
+ // Skip launch if already running (check via Nstbrowser running list).
1368
+ let alreadyRunning = false;
1369
+ try {
1370
+ const running = await this.nst.getRunningBrowsers();
1371
+ alreadyRunning = running.some(b => b.profileId === profileId || (b.name && b.name.toLowerCase() === profileId.toLowerCase()));
1372
+ } catch {}
1373
+ if (alreadyRunning) {
1374
+ this._profileLastActivity[profileId] = Date.now();
1375
+ return;
1376
+ }
1377
+
1378
+ console.log(`[chatgpt-dispatch] Launching ${profileId} (queue=${queue.count})`);
1379
+ try {
1380
+ // Reuse _launchRendererProfile contract — it just needs { nst_profile_id, os, proxy }.
1381
+ await this._launchRendererProfile({ nst_profile_id: profileId, os: 'windows', proxy: null });
1382
+ this._profileLastActivity[profileId] = Date.now();
1383
+ console.log(`[chatgpt-dispatch] ${profileId} launched`);
1384
+ } catch (err) {
1385
+ console.error(`[chatgpt-dispatch] Failed to launch ${profileId}: ${err.message}`);
1386
+ }
1387
+ } catch (err) {
1388
+ console.error(`[chatgpt-dispatch] Error: ${err.message}`);
1389
+ } finally {
1390
+ this._chatgptDispatching = false;
1391
+ }
1392
+ }
1393
+
1344
1394
  // ─── Profile Timeout ───────────────────────────────────────────────────────
1345
1395
  // Close profiles that have been idle (no commands assigned) for too long.
1346
1396
 
@@ -1348,7 +1398,7 @@ class CommandPoller {
1348
1398
  try {
1349
1399
  if (!this.nst) return;
1350
1400
 
1351
- const IDLE_TIMEOUT = 3 * 60 * 1000; // 3 minutes
1401
+ const IDLE_TIMEOUT = 30 * 1000; // 30s — matches old extension-side behavior
1352
1402
  const now = Date.now();
1353
1403
  const running = await this.nst.getRunningBrowsers();
1354
1404
  if (running.length === 0) return;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.3.5",
3
+ "version": "2.4.0",
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": {