channel-worker 1.0.1 → 1.0.3

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.
@@ -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; // check commands every 3s
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
- // TODO: Integrate with Nstbrowser SDK
56
- // const nstApi = require('./nst-manager');
57
- // await nstApi.launchProfile(profile_id);
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,123 @@
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?.docs || res?.data?.list || [];
24
+ if (!Array.isArray(profiles)) {
25
+ console.log(`[nst] getProfiles response:`, JSON.stringify(res?.data).slice(0, 200));
26
+ return null;
27
+ }
28
+ const match = profiles.find(p => p.name === name);
29
+ return match?.profileId || match?._id || null;
30
+ } catch (err) {
31
+ console.error(`[nst] Error finding profile "${name}":`, err.message);
32
+ return null;
33
+ }
34
+ }
35
+
36
+ // Create profile if not exists, return profileId
37
+ async ensureProfile(name) {
38
+ // Check existing
39
+ let profileId = await this.findProfile(name);
40
+ if (profileId) {
41
+ console.log(`[nst] Profile "${name}" exists: ${profileId}`);
42
+ return profileId;
43
+ }
44
+
45
+ // Create new
46
+ console.log(`[nst] Creating profile "${name}"...`);
47
+ const res = await this.client.profiles().createProfile({
48
+ name,
49
+ platform: 'Windows',
50
+ kernelMilestone: '132',
51
+ fingerprint: {
52
+ flags: {
53
+ audio: 'Noise',
54
+ canvas: 'Noise',
55
+ fonts: 'Masked',
56
+ gpu: 'Allow',
57
+ webgl: 'Noise',
58
+ },
59
+ hardwareConcurrency: 8,
60
+ deviceMemory: 8,
61
+ },
62
+ });
63
+
64
+ profileId = res?.data?.profileId || res?.data?.id;
65
+ if (!profileId) throw new Error('Failed to create profile — no ID returned');
66
+
67
+ console.log(`[nst] Profile "${name}" created: ${profileId}`);
68
+ return profileId;
69
+ }
70
+
71
+ // Launch browser for profile (visible, not headless)
72
+ async launchProfile(profileIdOrName) {
73
+ // If it looks like a name (not UUID), find/create by name
74
+ let profileId = profileIdOrName;
75
+ if (!this.isUUID(profileIdOrName)) {
76
+ profileId = await this.ensureProfile(profileIdOrName);
77
+ }
78
+
79
+ console.log(`[nst] Starting browser for profile: ${profileId}`);
80
+ const res = await this.client.browsers().startBrowser(profileId);
81
+ console.log(`[nst] Browser started`);
82
+ return { profileId, response: res };
83
+ }
84
+
85
+ // Launch and get CDP WebSocket URL (for automation)
86
+ async connectProfile(profileIdOrName) {
87
+ let profileId = profileIdOrName;
88
+ if (!this.isUUID(profileIdOrName)) {
89
+ profileId = await this.ensureProfile(profileIdOrName);
90
+ }
91
+
92
+ console.log(`[nst] Connecting to profile: ${profileId}`);
93
+ const res = await this.client.cdpEndpoints().connectBrowser(profileId, {
94
+ headless: false,
95
+ autoClose: false,
96
+ });
97
+
98
+ const wsEndpoint = res?.data?.webSocketDebuggerUrl;
99
+ if (!wsEndpoint) throw new Error('No WebSocket endpoint returned');
100
+
101
+ console.log(`[nst] Connected — WS: ${wsEndpoint}`);
102
+ return { profileId, wsEndpoint };
103
+ }
104
+
105
+ // Stop browser for profile
106
+ async stopProfile(profileIdOrName) {
107
+ let profileId = profileIdOrName;
108
+ if (!this.isUUID(profileIdOrName)) {
109
+ profileId = await this.findProfile(profileIdOrName);
110
+ if (!profileId) return;
111
+ }
112
+
113
+ console.log(`[nst] Stopping browser for profile: ${profileId}`);
114
+ await this.client.browsers().stopBrowser(profileId);
115
+ console.log(`[nst] Browser stopped`);
116
+ }
117
+
118
+ isUUID(str) {
119
+ return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(str);
120
+ }
121
+ }
122
+
123
+ module.exports = { NstManager };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "1.0.1",
3
+ "version": "1.0.3",
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"