channel-worker 2.4.0 → 2.4.2

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
@@ -90,6 +90,11 @@ class ApiClient {
90
90
  return this.request('GET', '/workers/chatgpt-queue-count');
91
91
  }
92
92
 
93
+ // Report local Nstbrowser profile list to API so the UI can show a picker.
94
+ async reportNstProfiles(workerId, profiles) {
95
+ return this.request('POST', '/workers/nst-profiles', { worker_id: workerId, profiles });
96
+ }
97
+
93
98
  async sceneDispatch(nstProfileId) {
94
99
  return this.request('POST', '/workers/scene-dispatch', { nst_profile_id: nstProfileId });
95
100
  }
@@ -37,6 +37,13 @@ class CommandPoller {
37
37
  this._chatgptDispatchTimer = setInterval(() => this._dispatchChatgpt(), 5000);
38
38
  this._chatgptDispatching = false;
39
39
 
40
+ // Profile syncer — report local Nstbrowser profile list to API every 60s
41
+ // so the web UI can render a multi-select picker (e.g. for choosing
42
+ // chatgpt_sub profiles).
43
+ console.log('[profile-sync] Started (every 60s)');
44
+ this._syncProfiles().catch(() => {});
45
+ this._profileSyncTimer = setInterval(() => this._syncProfiles().catch(() => {}), 60000);
46
+
40
47
  // Profile timeout — close idle profiles after 30s with no work
41
48
  this._profileTimeoutTimer = setInterval(() => this._checkProfileTimeouts(), 10000);
42
49
  this._profileLastActivity = {}; // { nst_profile_id: timestamp }
@@ -46,6 +53,7 @@ class CommandPoller {
46
53
  if (this.timer) { clearInterval(this.timer); this.timer = null; }
47
54
  if (this._dispatchTimer) { clearInterval(this._dispatchTimer); this._dispatchTimer = null; }
48
55
  if (this._chatgptDispatchTimer) { clearInterval(this._chatgptDispatchTimer); this._chatgptDispatchTimer = null; }
56
+ if (this._profileSyncTimer) { clearInterval(this._profileSyncTimer); this._profileSyncTimer = null; }
49
57
  if (this._profileTimeoutTimer) { clearInterval(this._profileTimeoutTimer); this._profileTimeoutTimer = null; }
50
58
  }
51
59
 
@@ -1361,28 +1369,27 @@ class CommandPoller {
1361
1369
  try {
1362
1370
  if (!this.nst) return;
1363
1371
  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}`);
1372
+ if (!queue || !Array.isArray(queue.profiles) || queue.profiles.length === 0) return;
1373
+
1374
+ let running = [];
1375
+ try { running = await this.nst.getRunningBrowsers(); } catch {}
1376
+
1377
+ for (const item of queue.profiles) {
1378
+ const profileId = item.id;
1379
+ const alreadyRunning = running.some(b =>
1380
+ b.profileId === profileId || (b.name && b.name.toLowerCase() === profileId.toLowerCase()));
1381
+ if (alreadyRunning) {
1382
+ this._profileLastActivity[profileId] = Date.now();
1383
+ continue;
1384
+ }
1385
+ console.log(`[chatgpt-dispatch] Launching ${profileId} (pending=${item.pending})`);
1386
+ try {
1387
+ await this._launchRendererProfile({ nst_profile_id: profileId, os: 'windows', proxy: null });
1388
+ this._profileLastActivity[profileId] = Date.now();
1389
+ console.log(`[chatgpt-dispatch] ${profileId} launched`);
1390
+ } catch (err) {
1391
+ console.error(`[chatgpt-dispatch] Failed to launch ${profileId}: ${err.message}`);
1392
+ }
1386
1393
  }
1387
1394
  } catch (err) {
1388
1395
  console.error(`[chatgpt-dispatch] Error: ${err.message}`);
@@ -1391,6 +1398,20 @@ class CommandPoller {
1391
1398
  }
1392
1399
  }
1393
1400
 
1401
+ // ─── Profile sync — report local Nstbrowser profile list to API ──────────
1402
+ async _syncProfiles() {
1403
+ try {
1404
+ if (!this.nst) return;
1405
+ const profiles = await this.nst.listAllProfiles();
1406
+ if (!profiles || profiles.length === 0) return;
1407
+ await this.api.reportNstProfiles(this.config.worker_id, profiles).catch((e) => {
1408
+ if (this.config.verbose) console.warn('[profile-sync] report failed:', e.message);
1409
+ });
1410
+ } catch (err) {
1411
+ if (this.config.verbose) console.warn('[profile-sync]', err.message);
1412
+ }
1413
+ }
1414
+
1394
1415
  // ─── Profile Timeout ───────────────────────────────────────────────────────
1395
1416
  // Close profiles that have been idle (no commands assigned) for too long.
1396
1417
 
@@ -17,6 +17,31 @@ class NstManager {
17
17
  return res.json();
18
18
  }
19
19
 
20
+ // List ALL profiles (not just running). Used by daemon heartbeat to report
21
+ // available profiles up to the API so the dashboard can show a picker.
22
+ async listAllProfiles() {
23
+ try {
24
+ let all = [];
25
+ let page = 1;
26
+ while (true) {
27
+ const res = await this.api(`/profiles/?page=${page}&limit=100`);
28
+ const docs = res?.data?.docs || res?.data || [];
29
+ all = all.concat(docs);
30
+ const totalPages = res?.data?.totalPage || res?.data?.totalPages || 1;
31
+ if (page >= totalPages || docs.length === 0) break;
32
+ page++;
33
+ }
34
+ return all.map(p => ({
35
+ profile_id: p.profileId || p._id,
36
+ name: p.name || '',
37
+ platform: p.platform || '',
38
+ }));
39
+ } catch (err) {
40
+ console.warn(`[nst] listAllProfiles: ${err.message}`);
41
+ return [];
42
+ }
43
+ }
44
+
20
45
  // Get all running browsers
21
46
  async getRunningBrowsers() {
22
47
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.4.0",
3
+ "version": "2.4.2",
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": {