channel-worker 2.0.2 → 2.1.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.
@@ -334,6 +334,19 @@ class CommandPoller {
334
334
  else if (fs.statSync(src).isDirectory()) fs.cpSync(src, dest, { recursive: true });
335
335
  }
336
336
  console.log(`[commands] CC extension downloaded to ${extPath}`);
337
+
338
+ // Close running profiles so they reload with new extension on next launch
339
+ if (this.nst) {
340
+ try {
341
+ const running = await this.nst.getRunningBrowsers();
342
+ if (running.length > 0) {
343
+ console.log(`[commands] Closing ${running.length} running profiles to reload extension...`);
344
+ for (const b of running) {
345
+ try { await this.nst.stopProfile(b.profileId); } catch {}
346
+ }
347
+ }
348
+ } catch {}
349
+ }
337
350
  } finally {
338
351
  try { fs.unlinkSync(tmpArchive); } catch {}
339
352
  try { fs.rmSync(tmpExtract, { recursive: true, force: true }); } catch {}
@@ -1153,66 +1166,38 @@ class CommandPoller {
1153
1166
  });
1154
1167
  const offlineRenderers = renderers.filter(r => !runningRenderers.includes(r));
1155
1168
 
1156
- // 6. Pick target profile launch new if under limit, else assign to running
1157
- let target = null;
1169
+ // 6. Launch profiles up to parallel limit
1158
1170
  console.log(`[scene-dispatch] running=${runningRenderers.length}/${parallelLimit} offline=${offlineRenderers.length} queue=${queueCount} names=[${runningRenderers.map(r=>r.name)}]`);
1159
1171
 
1160
- if (runningRenderers.length < parallelLimit && offlineRenderers.length > 0) {
1161
- // Under limit launch a new profile first
1162
- target = offlineRenderers[0];
1163
- console.log(`[scene-dispatch] Launching ${target.name} (running: ${runningRenderers.length}/${parallelLimit})`);
1172
+ const neededLaunches = Math.min(parallelLimit - runningRenderers.length, offlineRenderers.length, queueCount);
1173
+ for (let li = 0; li < neededLaunches; li++) {
1174
+ const toLaunch = offlineRenderers[li];
1175
+ console.log(`[scene-dispatch] Launching ${toLaunch.name} (running: ${runningRenderers.length + li}/${parallelLimit})`);
1164
1176
  try {
1165
- await this.nst.ensureProfile(target.nst_profile_id, { os: target.os || 'windows', proxy: target.proxy || null });
1166
-
1167
- const path = require('path');
1168
- const os_mod = require('os');
1169
- const defaultCCExtPath = path.join(os_mod.homedir(), 'content-creator-ext');
1170
- const baseExtPath = this.config.content_creator_ext_path || defaultCCExtPath;
1171
- await this._ensureContentCreatorExt(baseExtPath);
1172
-
1173
- // Create unique ext dir per profile
1174
- let extensionPath = baseExtPath;
1175
- const fs = require('fs');
1176
- const uniqueExtPath = baseExtPath + '-' + target.nst_profile_id;
1177
- try {
1178
- if (fs.existsSync(uniqueExtPath)) fs.rmSync(uniqueExtPath, { recursive: true });
1179
- fs.mkdirSync(uniqueExtPath, { recursive: true });
1180
- fs.cpSync(baseExtPath, uniqueExtPath, { recursive: true });
1181
- fs.writeFileSync(path.join(uniqueExtPath, 'config.json'), JSON.stringify({
1182
- channelManagerApi: this.api.baseUrl,
1183
- profileId: target.nst_profile_id,
1184
- workerToken: this.config.worker_token || '',
1185
- workerType: 'veo3',
1186
- }));
1187
- extensionPath = uniqueExtPath;
1188
- } catch (e) {
1189
- console.warn(`[scene-dispatch] Ext dir failed: ${e.message}, using base`);
1190
- }
1191
-
1192
- await this.nst.launchProfile(target.nst_profile_id, { proxy: target.proxy || null, extensionPath });
1193
- console.log(`[scene-dispatch] ${target.name} launched`);
1177
+ await this._launchRendererProfile(toLaunch);
1178
+ runningRenderers.push(toLaunch); // now considered running
1179
+ console.log(`[scene-dispatch] ${toLaunch.name} launched`);
1194
1180
  } catch (err) {
1195
- console.error(`[scene-dispatch] Failed to launch ${target.name}: ${err.message}`);
1196
- target = runningRenderers[0] || null; // fallback to running
1181
+ console.error(`[scene-dispatch] Failed to launch ${toLaunch.name}: ${err.message}`);
1197
1182
  }
1198
1183
  }
1199
1184
 
1200
- // Fallback: assign to least loaded running renderer
1201
- if (!target && runningRenderers.length > 0) {
1202
- target = runningRenderers[0]; // extension processes commands sequentially per profile
1203
- }
1204
-
1205
- if (!target) {
1206
- // No renderers available at all — wait for next cycle
1185
+ if (runningRenderers.length === 0) {
1207
1186
  this._dispatching = false;
1208
1187
  return;
1209
1188
  }
1210
1189
 
1211
- // 7. Claim command and assign to profile
1212
- const cmd = await this.api.sceneDispatch(target.nst_profile_id);
1213
- if (cmd) {
1190
+ // 7. Assign queued commands round-robin across running renderers
1191
+ let assigned = 0;
1192
+ for (let qi = 0; qi < queueCount; qi++) {
1193
+ const target = runningRenderers[qi % runningRenderers.length];
1194
+ const cmd = await this.api.sceneDispatch(target.nst_profile_id);
1195
+ if (!cmd) break; // queue empty
1214
1196
  this._profileLastActivity[target.nst_profile_id] = Date.now();
1215
- console.log(`[scene-dispatch] Assigned ${cmd.type} → ${target.name} (queue: ${queueCount - 1})`);
1197
+ assigned++;
1198
+ }
1199
+ if (assigned > 0) {
1200
+ console.log(`[scene-dispatch] Assigned ${assigned} commands across ${runningRenderers.length} renderers`);
1216
1201
  }
1217
1202
  } catch (err) {
1218
1203
  console.error(`[scene-dispatch] Error: ${err.message}`);
@@ -1220,6 +1205,44 @@ class CommandPoller {
1220
1205
  this._dispatching = false;
1221
1206
  }
1222
1207
 
1208
+ async _launchRendererProfile(renderer) {
1209
+ await this.nst.ensureProfile(renderer.nst_profile_id, { os: renderer.os || 'windows', proxy: renderer.proxy || null });
1210
+
1211
+ const path = require('path');
1212
+ const os_mod = require('os');
1213
+ const fs = require('fs');
1214
+ const defaultCCExtPath = path.join(os_mod.homedir(), 'content-creator-ext');
1215
+ const baseExtPath = this.config.content_creator_ext_path || defaultCCExtPath;
1216
+ await this._ensureContentCreatorExt(baseExtPath);
1217
+
1218
+ let extensionPath = baseExtPath;
1219
+ const extVersion = (() => { try { return JSON.parse(fs.readFileSync(path.join(baseExtPath, 'manifest.json'), 'utf8')).version; } catch { return '0'; } })();
1220
+ const uniqueExtPath = baseExtPath + '-' + renderer.nst_profile_id + '-v' + extVersion;
1221
+ try {
1222
+ const parent = path.dirname(baseExtPath);
1223
+ const prefix = path.basename(baseExtPath) + '-' + renderer.nst_profile_id;
1224
+ try {
1225
+ fs.readdirSync(parent)
1226
+ .filter(d => d.startsWith(prefix) && d !== path.basename(uniqueExtPath))
1227
+ .forEach(d => { try { fs.rmSync(path.join(parent, d), { recursive: true }); } catch {} });
1228
+ } catch {}
1229
+ if (fs.existsSync(uniqueExtPath)) fs.rmSync(uniqueExtPath, { recursive: true });
1230
+ fs.mkdirSync(uniqueExtPath, { recursive: true });
1231
+ fs.cpSync(baseExtPath, uniqueExtPath, { recursive: true });
1232
+ fs.writeFileSync(path.join(uniqueExtPath, 'config.json'), JSON.stringify({
1233
+ channelManagerApi: this.api.baseUrl,
1234
+ profileId: renderer.nst_profile_id,
1235
+ workerToken: this.config.worker_token || '',
1236
+ workerType: 'veo3',
1237
+ }));
1238
+ extensionPath = uniqueExtPath;
1239
+ } catch (e) {
1240
+ console.warn(`[scene-dispatch] Ext dir failed: ${e.message}, using base`);
1241
+ }
1242
+
1243
+ await this.nst.launchProfile(renderer.nst_profile_id, { proxy: renderer.proxy || null, extensionPath });
1244
+ }
1245
+
1223
1246
  // ─── Profile Timeout ───────────────────────────────────────────────────────
1224
1247
  // Close profiles that have been idle (no commands assigned) for too long.
1225
1248
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "2.0.2",
3
+ "version": "2.1.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": {