channel-worker 1.3.8 → 1.4.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.
package/bin/cli.js CHANGED
@@ -128,9 +128,32 @@ if (cmd === 'pair') {
128
128
  // Save merged config for next time
129
129
  saveConfig(config);
130
130
 
131
- const { Daemon } = require('../lib/daemon');
132
- const daemon = new Daemon(config);
133
- daemon.start();
131
+ if (args._daemon) {
132
+ // Actually run the daemon (spawned by ourselves)
133
+ const { Daemon } = require('../lib/daemon');
134
+ const daemon = new Daemon(config);
135
+ daemon.start();
136
+ } else {
137
+ // Spawn detached background process and exit
138
+ const { spawn } = require('child_process');
139
+ const LOG_FILE = path.join(CONFIG_DIR, 'daemon.log');
140
+ const logFd = fs.openSync(LOG_FILE, 'a');
141
+
142
+ const child = spawn(process.execPath, [__filename, 'start', '--_daemon'], {
143
+ detached: true,
144
+ stdio: ['ignore', logFd, logFd],
145
+ cwd: os.homedir(),
146
+ });
147
+ child.unref();
148
+
149
+ // Save PID for stop command
150
+ const pidFile = path.join(CONFIG_DIR, 'daemon.pid');
151
+ fs.writeFileSync(pidFile, String(child.pid));
152
+
153
+ console.log(`[channel-worker] Daemon started (PID: ${child.pid})`);
154
+ console.log(`[channel-worker] Logs: ${LOG_FILE}`);
155
+ process.exit(0);
156
+ }
134
157
 
135
158
  } else if (cmd === 'update') {
136
159
  const { checkAndUpdate, getLocalVersion } = require('../lib/updater');
@@ -150,6 +173,55 @@ if (cmd === 'pair') {
150
173
  }
151
174
  })();
152
175
 
176
+ } else if (cmd === 'stop') {
177
+ const pidFile = path.join(CONFIG_DIR, 'daemon.pid');
178
+ if (!fs.existsSync(pidFile)) {
179
+ console.log('[channel-worker] No daemon running (no PID file).');
180
+ process.exit(0);
181
+ }
182
+ const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
183
+ try {
184
+ process.kill(pid, 'SIGTERM');
185
+ fs.unlinkSync(pidFile);
186
+ console.log(`[channel-worker] Daemon stopped (PID: ${pid})`);
187
+ } catch (err) {
188
+ if (err.code === 'ESRCH') {
189
+ fs.unlinkSync(pidFile);
190
+ console.log(`[channel-worker] Daemon was not running (stale PID: ${pid}). Cleaned up.`);
191
+ } else {
192
+ console.error(`[channel-worker] Failed to stop: ${err.message}`);
193
+ process.exit(1);
194
+ }
195
+ }
196
+
197
+ } else if (cmd === 'logs') {
198
+ const LOG_FILE = path.join(CONFIG_DIR, 'daemon.log');
199
+ if (!fs.existsSync(LOG_FILE)) {
200
+ console.log('[channel-worker] No log file found.');
201
+ process.exit(0);
202
+ }
203
+ const lines = parseInt(args.lines || '50', 10);
204
+ const content = fs.readFileSync(LOG_FILE, 'utf-8');
205
+ const tail = content.split('\n').slice(-lines).join('\n');
206
+ console.log(tail);
207
+
208
+ } else if (cmd === 'restart') {
209
+ // Stop existing daemon, then start new one
210
+ const pidFile = path.join(CONFIG_DIR, 'daemon.pid');
211
+ if (fs.existsSync(pidFile)) {
212
+ const pid = parseInt(fs.readFileSync(pidFile, 'utf-8').trim(), 10);
213
+ try { process.kill(pid, 'SIGTERM'); } catch { /* already dead */ }
214
+ fs.unlinkSync(pidFile);
215
+ console.log(`[channel-worker] Stopped old daemon (PID: ${pid})`);
216
+ }
217
+ // Re-invoke start
218
+ const { spawn } = require('child_process');
219
+ const child = spawn(process.execPath, [__filename, 'start'], {
220
+ stdio: 'inherit',
221
+ cwd: process.cwd(),
222
+ });
223
+ child.on('exit', (code) => process.exit(code));
224
+
153
225
  } else if (cmd === 'config') {
154
226
  const config = loadConfig();
155
227
  // Hide token in display
@@ -164,7 +236,10 @@ channel-worker — Channel Manager worker daemon
164
236
  Commands:
165
237
  pair Pair with dashboard using a one-time code (recommended)
166
238
  init Configure worker manually
167
- start Start the daemon (auto-checks for updates every 5min)
239
+ start Start the daemon in background
240
+ stop Stop the daemon
241
+ restart Restart the daemon
242
+ logs Show recent daemon logs (--lines <n>, default 50)
168
243
  update Check and install updates manually
169
244
  config Show current config
170
245
 
@@ -182,6 +257,8 @@ Options:
182
257
  Examples:
183
258
  channel-worker pair --code A3F1B2 --api https://api.channel.tunasm.art
184
259
  channel-worker start
260
+ channel-worker logs --lines 100
261
+ channel-worker stop
185
262
  `);
186
263
  }
187
264
 
package/lib/api-client.js CHANGED
@@ -29,8 +29,11 @@ class ApiClient {
29
29
  return this.request('POST', '/workers/register', workerData);
30
30
  }
31
31
 
32
- async heartbeat(workerId) {
33
- return this.request('POST', '/workers/heartbeat', { worker_id: workerId });
32
+ async heartbeat(workerId, version, extensionVersion) {
33
+ const body = { worker_id: workerId };
34
+ if (version) body.version = version;
35
+ if (extensionVersion) body.extension_version = extensionVersion;
36
+ return this.request('POST', '/workers/heartbeat', body);
34
37
  }
35
38
 
36
39
  // Jobs
package/lib/daemon.js CHANGED
@@ -9,7 +9,7 @@ class Daemon {
9
9
  constructor(config) {
10
10
  this.config = config;
11
11
  this.api = new ApiClient(config.api_url, config.worker_token);
12
- this.heartbeat = new Heartbeat(this.api, config.worker_id);
12
+ this.heartbeat = new Heartbeat(this.api, config.worker_id, 30000, config);
13
13
  this.poller = new JobPoller(this.api, config);
14
14
  this.commandPoller = new CommandPoller(this.api, config);
15
15
  this.updateChecker = new UpdateChecker(5 * 60 * 1000); // check every 5min
package/lib/heartbeat.js CHANGED
@@ -1,8 +1,12 @@
1
+ const { getLocalVersion } = require('./updater');
2
+ const { getLocalExtensionVersion } = require('./extension-updater');
3
+
1
4
  class Heartbeat {
2
- constructor(api, workerId, intervalMs = 30000) {
5
+ constructor(api, workerId, intervalMs = 30000, config = {}) {
3
6
  this.api = api;
4
7
  this.workerId = workerId;
5
8
  this.intervalMs = intervalMs;
9
+ this.config = config;
6
10
  this.timer = null;
7
11
  }
8
12
 
@@ -20,7 +24,9 @@ class Heartbeat {
20
24
 
21
25
  async send() {
22
26
  try {
23
- await this.api.heartbeat(this.workerId);
27
+ const version = getLocalVersion();
28
+ const extVersion = getLocalExtensionVersion(this.config.extension_path);
29
+ await this.api.heartbeat(this.workerId, version, extVersion);
24
30
  } catch (err) {
25
31
  console.error(`[heartbeat] Failed: ${err.message}`);
26
32
  }
package/lib/updater.js CHANGED
@@ -43,15 +43,31 @@ async function checkAndUpdate({ autoRestart = false } = {}) {
43
43
  installUpdate(latest);
44
44
 
45
45
  if (autoRestart) {
46
- console.log('[updater] Restarting daemon...');
47
- // Spawn detached process with same args, then exit current
46
+ console.log('[updater] Restarting daemon with new version...');
48
47
  const { spawn } = require('child_process');
49
- const child = spawn(process.argv[0], process.argv.slice(1), {
48
+ const fs = require('fs');
49
+ const path = require('path');
50
+ const os = require('os');
51
+
52
+ const CONFIG_DIR = path.join(os.homedir(), '.channel-worker');
53
+ const pidFile = path.join(CONFIG_DIR, 'daemon.pid');
54
+ const LOG_FILE = path.join(CONFIG_DIR, 'daemon.log');
55
+ const logFd = fs.openSync(LOG_FILE, 'a');
56
+
57
+ // Spawn new daemon using global binary (picks up new version)
58
+ const isWindows = process.platform === 'win32';
59
+ const binName = isWindows ? 'channel-worker.cmd' : 'channel-worker';
60
+ const child = spawn(binName, ['start'], {
50
61
  detached: true,
51
- stdio: 'ignore',
52
- cwd: process.cwd(),
62
+ stdio: ['ignore', logFd, logFd],
63
+ cwd: os.homedir(),
64
+ shell: isWindows,
53
65
  });
54
66
  child.unref();
67
+
68
+ // Update PID file
69
+ fs.writeFileSync(pidFile, String(child.pid));
70
+
55
71
  process.exit(0);
56
72
  }
57
73
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "channel-worker",
3
- "version": "1.3.8",
3
+ "version": "1.4.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": {