channel-worker 2.0.3 → 2.1.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.
- package/lib/api-client.js +4 -0
- package/lib/command-poller.js +74 -56
- package/package.json +1 -1
package/lib/api-client.js
CHANGED
|
@@ -86,6 +86,10 @@ class ApiClient {
|
|
|
86
86
|
return this.request('POST', '/workers/scene-dispatch', { nst_profile_id: nstProfileId });
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
+
async rendererHasCommands(nstProfileId) {
|
|
90
|
+
return this.request('GET', `/workers/renderer-has-commands?nst_profile_id=${encodeURIComponent(nstProfileId)}`);
|
|
91
|
+
}
|
|
92
|
+
|
|
89
93
|
// Extension download
|
|
90
94
|
async getExtensionVersion() {
|
|
91
95
|
const data = await this.request('GET', '/extension-download/version');
|
package/lib/command-poller.js
CHANGED
|
@@ -1166,75 +1166,55 @@ class CommandPoller {
|
|
|
1166
1166
|
});
|
|
1167
1167
|
const offlineRenderers = renderers.filter(r => !runningRenderers.includes(r));
|
|
1168
1168
|
|
|
1169
|
-
//
|
|
1170
|
-
|
|
1171
|
-
console.log(`[scene-dispatch] running=${runningRenderers.length}/${parallelLimit} offline=${offlineRenderers.length} queue=${queueCount} names=[${runningRenderers.map(r=>r.name)}]`);
|
|
1172
|
-
|
|
1173
|
-
if (runningRenderers.length < parallelLimit && offlineRenderers.length > 0) {
|
|
1174
|
-
// Under limit — launch a new profile first
|
|
1175
|
-
target = offlineRenderers[0];
|
|
1176
|
-
console.log(`[scene-dispatch] Launching ${target.name} (running: ${runningRenderers.length}/${parallelLimit})`);
|
|
1169
|
+
// 5b. Crash recovery — re-launch offline profiles that have assigned commands
|
|
1170
|
+
for (const r of offlineRenderers) {
|
|
1177
1171
|
try {
|
|
1178
|
-
await this.
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
const os_mod = require('os');
|
|
1182
|
-
const defaultCCExtPath = path.join(os_mod.homedir(), 'content-creator-ext');
|
|
1183
|
-
const baseExtPath = this.config.content_creator_ext_path || defaultCCExtPath;
|
|
1184
|
-
await this._ensureContentCreatorExt(baseExtPath);
|
|
1185
|
-
|
|
1186
|
-
// Create unique ext dir per profile + version (forces Chromium to reload SW)
|
|
1187
|
-
let extensionPath = baseExtPath;
|
|
1188
|
-
const fs = require('fs');
|
|
1189
|
-
const extVersion = (() => { try { return JSON.parse(fs.readFileSync(path.join(baseExtPath, 'manifest.json'), 'utf8')).version; } catch { return '0'; } })();
|
|
1190
|
-
const uniqueExtPath = baseExtPath + '-' + target.nst_profile_id + '-v' + extVersion;
|
|
1191
|
-
try {
|
|
1192
|
-
// Clean old version dirs for this profile
|
|
1193
|
-
const parent = path.dirname(baseExtPath);
|
|
1194
|
-
const prefix = path.basename(baseExtPath) + '-' + target.nst_profile_id;
|
|
1172
|
+
const cmdCount = await this.api.rendererHasCommands(r.nst_profile_id);
|
|
1173
|
+
if (cmdCount > 0) {
|
|
1174
|
+
console.log(`[scene-dispatch] Crash recovery: ${r.name} has ${cmdCount} commands but not running — relaunching`);
|
|
1195
1175
|
try {
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
} catch {
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
fs.cpSync(baseExtPath, uniqueExtPath, { recursive: true });
|
|
1203
|
-
fs.writeFileSync(path.join(uniqueExtPath, 'config.json'), JSON.stringify({
|
|
1204
|
-
channelManagerApi: this.api.baseUrl,
|
|
1205
|
-
profileId: target.nst_profile_id,
|
|
1206
|
-
workerToken: this.config.worker_token || '',
|
|
1207
|
-
workerType: 'veo3',
|
|
1208
|
-
}));
|
|
1209
|
-
extensionPath = uniqueExtPath;
|
|
1210
|
-
} catch (e) {
|
|
1211
|
-
console.warn(`[scene-dispatch] Ext dir failed: ${e.message}, using base`);
|
|
1176
|
+
await this._launchRendererProfile(r);
|
|
1177
|
+
runningRenderers.push(r);
|
|
1178
|
+
console.log(`[scene-dispatch] ${r.name} recovered`);
|
|
1179
|
+
} catch (err) {
|
|
1180
|
+
console.error(`[scene-dispatch] Failed to recover ${r.name}: ${err.message}`);
|
|
1181
|
+
}
|
|
1212
1182
|
}
|
|
1183
|
+
} catch {}
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// 6. Launch profiles up to parallel limit
|
|
1187
|
+
console.log(`[scene-dispatch] running=${runningRenderers.length}/${parallelLimit} offline=${offlineRenderers.length} queue=${queueCount} names=[${runningRenderers.map(r=>r.name)}]`);
|
|
1213
1188
|
|
|
1214
|
-
|
|
1215
|
-
|
|
1189
|
+
const neededLaunches = Math.min(parallelLimit - runningRenderers.length, offlineRenderers.length, queueCount);
|
|
1190
|
+
for (let li = 0; li < neededLaunches; li++) {
|
|
1191
|
+
const toLaunch = offlineRenderers[li];
|
|
1192
|
+
console.log(`[scene-dispatch] Launching ${toLaunch.name} (running: ${runningRenderers.length + li}/${parallelLimit})`);
|
|
1193
|
+
try {
|
|
1194
|
+
await this._launchRendererProfile(toLaunch);
|
|
1195
|
+
runningRenderers.push(toLaunch); // now considered running
|
|
1196
|
+
console.log(`[scene-dispatch] ${toLaunch.name} launched`);
|
|
1216
1197
|
} catch (err) {
|
|
1217
|
-
console.error(`[scene-dispatch] Failed to launch ${
|
|
1218
|
-
target = runningRenderers[0] || null; // fallback to running
|
|
1198
|
+
console.error(`[scene-dispatch] Failed to launch ${toLaunch.name}: ${err.message}`);
|
|
1219
1199
|
}
|
|
1220
1200
|
}
|
|
1221
1201
|
|
|
1222
|
-
|
|
1223
|
-
if (!target && runningRenderers.length > 0) {
|
|
1224
|
-
target = runningRenderers[0]; // extension processes commands sequentially per profile
|
|
1225
|
-
}
|
|
1226
|
-
|
|
1227
|
-
if (!target) {
|
|
1228
|
-
// No renderers available at all — wait for next cycle
|
|
1202
|
+
if (runningRenderers.length === 0) {
|
|
1229
1203
|
this._dispatching = false;
|
|
1230
1204
|
return;
|
|
1231
1205
|
}
|
|
1232
1206
|
|
|
1233
|
-
// 7.
|
|
1234
|
-
|
|
1235
|
-
|
|
1207
|
+
// 7. Assign queued commands round-robin across running renderers
|
|
1208
|
+
let assigned = 0;
|
|
1209
|
+
for (let qi = 0; qi < queueCount; qi++) {
|
|
1210
|
+
const target = runningRenderers[qi % runningRenderers.length];
|
|
1211
|
+
const cmd = await this.api.sceneDispatch(target.nst_profile_id);
|
|
1212
|
+
if (!cmd) break; // queue empty
|
|
1236
1213
|
this._profileLastActivity[target.nst_profile_id] = Date.now();
|
|
1237
|
-
|
|
1214
|
+
assigned++;
|
|
1215
|
+
}
|
|
1216
|
+
if (assigned > 0) {
|
|
1217
|
+
console.log(`[scene-dispatch] Assigned ${assigned} commands across ${runningRenderers.length} renderers`);
|
|
1238
1218
|
}
|
|
1239
1219
|
} catch (err) {
|
|
1240
1220
|
console.error(`[scene-dispatch] Error: ${err.message}`);
|
|
@@ -1242,6 +1222,44 @@ class CommandPoller {
|
|
|
1242
1222
|
this._dispatching = false;
|
|
1243
1223
|
}
|
|
1244
1224
|
|
|
1225
|
+
async _launchRendererProfile(renderer) {
|
|
1226
|
+
await this.nst.ensureProfile(renderer.nst_profile_id, { os: renderer.os || 'windows', proxy: renderer.proxy || null });
|
|
1227
|
+
|
|
1228
|
+
const path = require('path');
|
|
1229
|
+
const os_mod = require('os');
|
|
1230
|
+
const fs = require('fs');
|
|
1231
|
+
const defaultCCExtPath = path.join(os_mod.homedir(), 'content-creator-ext');
|
|
1232
|
+
const baseExtPath = this.config.content_creator_ext_path || defaultCCExtPath;
|
|
1233
|
+
await this._ensureContentCreatorExt(baseExtPath);
|
|
1234
|
+
|
|
1235
|
+
let extensionPath = baseExtPath;
|
|
1236
|
+
const extVersion = (() => { try { return JSON.parse(fs.readFileSync(path.join(baseExtPath, 'manifest.json'), 'utf8')).version; } catch { return '0'; } })();
|
|
1237
|
+
const uniqueExtPath = baseExtPath + '-' + renderer.nst_profile_id + '-v' + extVersion;
|
|
1238
|
+
try {
|
|
1239
|
+
const parent = path.dirname(baseExtPath);
|
|
1240
|
+
const prefix = path.basename(baseExtPath) + '-' + renderer.nst_profile_id;
|
|
1241
|
+
try {
|
|
1242
|
+
fs.readdirSync(parent)
|
|
1243
|
+
.filter(d => d.startsWith(prefix) && d !== path.basename(uniqueExtPath))
|
|
1244
|
+
.forEach(d => { try { fs.rmSync(path.join(parent, d), { recursive: true }); } catch {} });
|
|
1245
|
+
} catch {}
|
|
1246
|
+
if (fs.existsSync(uniqueExtPath)) fs.rmSync(uniqueExtPath, { recursive: true });
|
|
1247
|
+
fs.mkdirSync(uniqueExtPath, { recursive: true });
|
|
1248
|
+
fs.cpSync(baseExtPath, uniqueExtPath, { recursive: true });
|
|
1249
|
+
fs.writeFileSync(path.join(uniqueExtPath, 'config.json'), JSON.stringify({
|
|
1250
|
+
channelManagerApi: this.api.baseUrl,
|
|
1251
|
+
profileId: renderer.nst_profile_id,
|
|
1252
|
+
workerToken: this.config.worker_token || '',
|
|
1253
|
+
workerType: 'veo3',
|
|
1254
|
+
}));
|
|
1255
|
+
extensionPath = uniqueExtPath;
|
|
1256
|
+
} catch (e) {
|
|
1257
|
+
console.warn(`[scene-dispatch] Ext dir failed: ${e.message}, using base`);
|
|
1258
|
+
}
|
|
1259
|
+
|
|
1260
|
+
await this.nst.launchProfile(renderer.nst_profile_id, { proxy: renderer.proxy || null, extensionPath });
|
|
1261
|
+
}
|
|
1262
|
+
|
|
1245
1263
|
// ─── Profile Timeout ───────────────────────────────────────────────────────
|
|
1246
1264
|
// Close profiles that have been idle (no commands assigned) for too long.
|
|
1247
1265
|
|