channel-worker 1.0.1 → 1.0.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/command-poller.js +25 -5
- package/lib/nst-manager.js +119 -0
- package/package.json +3 -2
package/lib/command-poller.js
CHANGED
|
@@ -1,9 +1,22 @@
|
|
|
1
|
+
const { NstManager } = require('./nst-manager');
|
|
2
|
+
|
|
1
3
|
class CommandPoller {
|
|
2
4
|
constructor(api, config) {
|
|
3
5
|
this.api = api;
|
|
4
6
|
this.config = config;
|
|
5
7
|
this.timer = null;
|
|
6
|
-
this.pollIntervalMs = 3000;
|
|
8
|
+
this.pollIntervalMs = 3000;
|
|
9
|
+
this.nst = null;
|
|
10
|
+
|
|
11
|
+
// Init Nstbrowser if API key available
|
|
12
|
+
if (config.nst_api_key) {
|
|
13
|
+
try {
|
|
14
|
+
this.nst = new NstManager(config.nst_api_key);
|
|
15
|
+
console.log('[commands] Nstbrowser SDK initialized');
|
|
16
|
+
} catch (err) {
|
|
17
|
+
console.warn(`[commands] Nstbrowser SDK not available: ${err.message}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
7
20
|
}
|
|
8
21
|
|
|
9
22
|
start() {
|
|
@@ -52,15 +65,22 @@ class CommandPoller {
|
|
|
52
65
|
console.log(`[commands] Launching Nstbrowser profile: ${profile_id}`);
|
|
53
66
|
|
|
54
67
|
try {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
68
|
+
if (!this.nst) {
|
|
69
|
+
// No Nstbrowser SDK — try to get API key from dashboard settings
|
|
70
|
+
const apiKey = await this.api.getSetting('nst_api_key');
|
|
71
|
+
if (apiKey) {
|
|
72
|
+
this.nst = new NstManager(apiKey);
|
|
73
|
+
} else {
|
|
74
|
+
throw new Error('Nstbrowser API key not configured. Set it in Dashboard → Settings.');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
58
77
|
|
|
78
|
+
const result = await this.nst.launchProfile(profile_id);
|
|
59
79
|
console.log(`[commands] Profile ${profile_id} launched — user can now login via RDP`);
|
|
60
80
|
|
|
61
81
|
await this.api.updateCommand(command._id, {
|
|
62
82
|
status: 'done',
|
|
63
|
-
result: { profile_id, launched_at: new Date().toISOString() },
|
|
83
|
+
result: { profile_id: result.profileId, launched_at: new Date().toISOString() },
|
|
64
84
|
});
|
|
65
85
|
} catch (err) {
|
|
66
86
|
console.error(`[commands] Failed to launch profile: ${err.message}`);
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
let NstBrowserV2;
|
|
2
|
+
try {
|
|
3
|
+
NstBrowserV2 = require('nstbrowser-sdk-node').NstBrowserV2;
|
|
4
|
+
} catch {
|
|
5
|
+
// SDK not installed — will fail gracefully
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
class NstManager {
|
|
9
|
+
constructor(apiKey, options = {}) {
|
|
10
|
+
if (!NstBrowserV2) {
|
|
11
|
+
throw new Error('nstbrowser-sdk-node not installed. Run: npm install -g nstbrowser-sdk-node');
|
|
12
|
+
}
|
|
13
|
+
this.client = new NstBrowserV2(apiKey, {
|
|
14
|
+
timeout: options.timeout || 60000,
|
|
15
|
+
apiAddress: options.apiAddress || 'http://localhost:8848/api/v2',
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
// Check if profile exists by name, return profileId
|
|
20
|
+
async findProfile(name) {
|
|
21
|
+
try {
|
|
22
|
+
const res = await this.client.profiles().getProfiles({ keyword: name });
|
|
23
|
+
const profiles = res?.data?.list || res?.data || [];
|
|
24
|
+
const match = profiles.find(p => p.name === name);
|
|
25
|
+
return match?.profileId || match?.id || null;
|
|
26
|
+
} catch (err) {
|
|
27
|
+
console.error(`[nst] Error finding profile "${name}":`, err.message);
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Create profile if not exists, return profileId
|
|
33
|
+
async ensureProfile(name) {
|
|
34
|
+
// Check existing
|
|
35
|
+
let profileId = await this.findProfile(name);
|
|
36
|
+
if (profileId) {
|
|
37
|
+
console.log(`[nst] Profile "${name}" exists: ${profileId}`);
|
|
38
|
+
return profileId;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Create new
|
|
42
|
+
console.log(`[nst] Creating profile "${name}"...`);
|
|
43
|
+
const res = await this.client.profiles().createProfile({
|
|
44
|
+
name,
|
|
45
|
+
platform: 'Windows',
|
|
46
|
+
kernelMilestone: '132',
|
|
47
|
+
fingerprint: {
|
|
48
|
+
flags: {
|
|
49
|
+
audio: 'Noise',
|
|
50
|
+
canvas: 'Noise',
|
|
51
|
+
fonts: 'Masked',
|
|
52
|
+
gpu: 'Allow',
|
|
53
|
+
webgl: 'Noise',
|
|
54
|
+
},
|
|
55
|
+
hardwareConcurrency: 8,
|
|
56
|
+
deviceMemory: 8,
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
profileId = res?.data?.profileId || res?.data?.id;
|
|
61
|
+
if (!profileId) throw new Error('Failed to create profile — no ID returned');
|
|
62
|
+
|
|
63
|
+
console.log(`[nst] Profile "${name}" created: ${profileId}`);
|
|
64
|
+
return profileId;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
// Launch browser for profile (visible, not headless)
|
|
68
|
+
async launchProfile(profileIdOrName) {
|
|
69
|
+
// If it looks like a name (not UUID), find/create by name
|
|
70
|
+
let profileId = profileIdOrName;
|
|
71
|
+
if (!this.isUUID(profileIdOrName)) {
|
|
72
|
+
profileId = await this.ensureProfile(profileIdOrName);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
console.log(`[nst] Starting browser for profile: ${profileId}`);
|
|
76
|
+
const res = await this.client.browsers().startBrowser(profileId);
|
|
77
|
+
console.log(`[nst] Browser started`);
|
|
78
|
+
return { profileId, response: res };
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// Launch and get CDP WebSocket URL (for automation)
|
|
82
|
+
async connectProfile(profileIdOrName) {
|
|
83
|
+
let profileId = profileIdOrName;
|
|
84
|
+
if (!this.isUUID(profileIdOrName)) {
|
|
85
|
+
profileId = await this.ensureProfile(profileIdOrName);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
console.log(`[nst] Connecting to profile: ${profileId}`);
|
|
89
|
+
const res = await this.client.cdpEndpoints().connectBrowser(profileId, {
|
|
90
|
+
headless: false,
|
|
91
|
+
autoClose: false,
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
const wsEndpoint = res?.data?.webSocketDebuggerUrl;
|
|
95
|
+
if (!wsEndpoint) throw new Error('No WebSocket endpoint returned');
|
|
96
|
+
|
|
97
|
+
console.log(`[nst] Connected — WS: ${wsEndpoint}`);
|
|
98
|
+
return { profileId, wsEndpoint };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Stop browser for profile
|
|
102
|
+
async stopProfile(profileIdOrName) {
|
|
103
|
+
let profileId = profileIdOrName;
|
|
104
|
+
if (!this.isUUID(profileIdOrName)) {
|
|
105
|
+
profileId = await this.findProfile(profileIdOrName);
|
|
106
|
+
if (!profileId) return;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
console.log(`[nst] Stopping browser for profile: ${profileId}`);
|
|
110
|
+
await this.client.browsers().stopBrowser(profileId);
|
|
111
|
+
console.log(`[nst] Browser stopped`);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
isUUID(str) {
|
|
115
|
+
return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
module.exports = { NstManager };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "channel-worker",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.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": {
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"dev": "node bin/cli.js start --verbose"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"node-fetch": "^3.3.2"
|
|
18
|
+
"node-fetch": "^3.3.2",
|
|
19
|
+
"nstbrowser-sdk-node": "^0.1.1"
|
|
19
20
|
},
|
|
20
21
|
"engines": {
|
|
21
22
|
"node": ">=18"
|