@yeaft/webchat-agent 0.1.212 → 0.1.213

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yeaft/webchat-agent",
3
- "version": "0.1.212",
3
+ "version": "0.1.213",
4
4
  "description": "Remote agent for Yeaft WebChat — connects worker machines to the central server",
5
5
  "main": "index.js",
6
6
  "type": "module",
package/service/doctor.js CHANGED
@@ -1,30 +1,16 @@
1
1
  /**
2
2
  * Service — doctor command
3
3
  * Diagnoses service configuration issues: checks paths, service status, version consistency.
4
+ *
5
+ * Reuses path and status functions from platform modules to avoid duplication.
4
6
  */
5
7
  import { execSync } from 'child_process';
6
8
  import { existsSync, readFileSync, statSync } from 'fs';
7
- import { join, dirname } from 'path';
8
9
  import { platform, homedir } from 'os';
9
- import { fileURLToPath } from 'url';
10
- import { SERVICE_NAME } from './config.js';
11
-
12
- const __dirname = dirname(fileURLToPath(import.meta.url));
13
-
14
- // ── Platform-specific service config paths ─────────────────────────────────
15
-
16
- function getMacPlistPath() {
17
- return join(homedir(), 'Library', 'LaunchAgents', 'com.yeaft.agent.plist');
18
- }
19
-
20
- function getLinuxServicePath() {
21
- return join(homedir(), '.config', 'systemd', 'user', `${SERVICE_NAME}.service`);
22
- }
23
-
24
- function getWindowsEcosystemPath() {
25
- const appData = process.env.APPDATA || join(homedir(), 'AppData', 'Roaming');
26
- return join(appData, SERVICE_NAME, 'ecosystem.config.cjs');
27
- }
10
+ import { getNodePath } from './config.js';
11
+ import { getLaunchdPlistPath, getMacServiceStatus } from './macos.js';
12
+ import { getSystemdServicePath, getLinuxServiceStatus } from './linux.js';
13
+ import { getEcosystemPath, getWinServiceStatus } from './windows.js';
28
14
 
29
15
  // ── Parse paths from service config files ──────────────────────────────────
30
16
 
@@ -107,78 +93,13 @@ function getCliVersion(nodePath, cliPath) {
107
93
  }
108
94
 
109
95
  function getCurrentNodePath() {
110
- return process.execPath;
96
+ return getNodePath();
111
97
  }
112
98
 
113
99
  function getCurrentNodeVersion() {
114
100
  return process.version;
115
101
  }
116
102
 
117
- // ── Service status checks ──────────────────────────────────────────────────
118
-
119
- function getMacServiceStatus() {
120
- try {
121
- const output = execSync('launchctl list | grep com.yeaft.agent', {
122
- encoding: 'utf-8',
123
- stdio: ['pipe', 'pipe', 'pipe'],
124
- });
125
- if (output.trim()) {
126
- const parts = output.trim().split(/\s+/);
127
- const pid = parts[0];
128
- if (pid !== '-') {
129
- return { running: true, pid };
130
- }
131
- return { running: false, pid: null };
132
- }
133
- return { running: false, pid: null };
134
- } catch {
135
- return { running: false, pid: null };
136
- }
137
- }
138
-
139
- function getLinuxServiceStatus() {
140
- try {
141
- const output = execSync(`systemctl --user is-active ${SERVICE_NAME}`, {
142
- encoding: 'utf-8',
143
- stdio: ['pipe', 'pipe', 'pipe'],
144
- }).trim();
145
- if (output === 'active') {
146
- // Try to get PID
147
- let pid = null;
148
- try {
149
- pid = execSync(`systemctl --user show ${SERVICE_NAME} --property=MainPID --value`, {
150
- encoding: 'utf-8',
151
- stdio: ['pipe', 'pipe', 'pipe'],
152
- }).trim();
153
- if (pid === '0') pid = null;
154
- } catch {}
155
- return { running: true, pid };
156
- }
157
- return { running: false, pid: null };
158
- } catch {
159
- return { running: false, pid: null };
160
- }
161
- }
162
-
163
- function getWindowsServiceStatus() {
164
- try {
165
- const output = execSync('pm2 jlist', {
166
- encoding: 'utf-8',
167
- stdio: ['pipe', 'pipe', 'pipe'],
168
- });
169
- const apps = JSON.parse(output);
170
- const app = Array.isArray(apps) && apps.find(a => a.name === 'yeaft-agent');
171
- if (app) {
172
- const running = app.pm2_env && app.pm2_env.status === 'online';
173
- const pid = running ? app.pid : null;
174
- return { running, pid: pid ? String(pid) : null };
175
- }
176
- return { running: false, pid: null };
177
- } catch {
178
- return { running: false, pid: null };
179
- }
180
- }
181
-
182
103
  // ── Tilde shorthand for display ────────────────────────────────────────────
183
104
 
184
105
  function tildeify(filePath) {
@@ -201,17 +122,17 @@ export function doctor() {
201
122
 
202
123
  // 1. Determine platform and config path
203
124
  if (os === 'darwin') {
204
- configPath = getMacPlistPath();
125
+ configPath = getLaunchdPlistPath();
205
126
  parsePaths = parseMacPaths;
206
127
  getServiceStatus = getMacServiceStatus;
207
128
  } else if (os === 'linux') {
208
- configPath = getLinuxServicePath();
129
+ configPath = getSystemdServicePath();
209
130
  parsePaths = parseLinuxPaths;
210
131
  getServiceStatus = getLinuxServiceStatus;
211
132
  } else if (os === 'win32') {
212
- configPath = getWindowsEcosystemPath();
133
+ configPath = getEcosystemPath();
213
134
  parsePaths = parseWindowsPaths;
214
- getServiceStatus = getWindowsServiceStatus;
135
+ getServiceStatus = getWinServiceStatus;
215
136
  } else {
216
137
  console.log(`\u26a0\ufe0f Unsupported platform: ${os}`);
217
138
  console.log(` The doctor command supports macOS, Linux, and Windows.`);
package/service/linux.js CHANGED
@@ -7,10 +7,9 @@ import { join, dirname } from 'path';
7
7
  import { homedir } from 'os';
8
8
  import { SERVICE_NAME, getLogDir, getNodePath, getCliPath } from './config.js';
9
9
 
10
+ /** Pure path getter — no side effects (no directory creation). */
10
11
  export function getSystemdServicePath() {
11
- const dir = join(homedir(), '.config', 'systemd', 'user');
12
- mkdirSync(dir, { recursive: true });
13
- return join(dir, `${SERVICE_NAME}.service`);
12
+ return join(homedir(), '.config', 'systemd', 'user', `${SERVICE_NAME}.service`);
14
13
  }
15
14
 
16
15
  function generateSystemdUnit(config) {
@@ -50,6 +49,8 @@ WantedBy=default.target
50
49
 
51
50
  export function linuxInstall(config) {
52
51
  const servicePath = getSystemdServicePath();
52
+ // Ensure systemd user directory exists before writing
53
+ mkdirSync(join(homedir(), '.config', 'systemd', 'user'), { recursive: true });
53
54
  writeFileSync(servicePath, generateSystemdUnit(config));
54
55
  execSync('systemctl --user daemon-reload');
55
56
  execSync(`systemctl --user enable ${SERVICE_NAME}`);
@@ -88,6 +89,33 @@ export function linuxRestart() {
88
89
  console.log('Service restarted.');
89
90
  }
90
91
 
92
+ /**
93
+ * Query systemd for the current service status.
94
+ * Returns { running: boolean, pid: string|null }.
95
+ */
96
+ export function getLinuxServiceStatus() {
97
+ try {
98
+ const output = execSync(`systemctl --user is-active ${SERVICE_NAME}`, {
99
+ encoding: 'utf-8',
100
+ stdio: ['pipe', 'pipe', 'pipe'],
101
+ }).trim();
102
+ if (output === 'active') {
103
+ let pid = null;
104
+ try {
105
+ pid = execSync(`systemctl --user show ${SERVICE_NAME} --property=MainPID --value`, {
106
+ encoding: 'utf-8',
107
+ stdio: ['pipe', 'pipe', 'pipe'],
108
+ }).trim();
109
+ if (pid === '0') pid = null;
110
+ } catch {}
111
+ return { running: true, pid };
112
+ }
113
+ return { running: false, pid: null };
114
+ } catch {
115
+ return { running: false, pid: null };
116
+ }
117
+ }
118
+
91
119
  export function linuxStatus() {
92
120
  try {
93
121
  execSync(`systemctl --user status ${SERVICE_NAME}`, { stdio: 'inherit' });
package/service/macos.js CHANGED
@@ -7,10 +7,9 @@ import { join } from 'path';
7
7
  import { homedir } from 'os';
8
8
  import { getLogDir, getNodePath, getCliPath } from './config.js';
9
9
 
10
+ /** Pure path getter — no side effects (no directory creation). */
10
11
  export function getLaunchdPlistPath() {
11
- const dir = join(homedir(), 'Library', 'LaunchAgents');
12
- mkdirSync(dir, { recursive: true });
13
- return join(dir, 'com.yeaft.agent.plist');
12
+ return join(homedir(), 'Library', 'LaunchAgents', 'com.yeaft.agent.plist');
14
13
  }
15
14
 
16
15
  function generateLaunchdPlist(config) {
@@ -61,6 +60,8 @@ ${envDict.join('\n')}
61
60
 
62
61
  export function macInstall(config) {
63
62
  const plistPath = getLaunchdPlistPath();
63
+ // Ensure LaunchAgents directory exists before writing
64
+ mkdirSync(join(homedir(), 'Library', 'LaunchAgents'), { recursive: true });
64
65
  // Unload first if exists
65
66
  if (existsSync(plistPath)) {
66
67
  try { execSync(`launchctl unload ${plistPath} 2>/dev/null`); } catch {}
@@ -107,22 +108,38 @@ export function macRestart() {
107
108
  macStart();
108
109
  }
109
110
 
110
- export function macStatus() {
111
+ /**
112
+ * Query launchd for the current service status.
113
+ * Returns { running: boolean, pid: string|null, exitCode: string|null }.
114
+ */
115
+ export function getMacServiceStatus() {
111
116
  try {
112
- const output = execSync(`launchctl list | grep com.yeaft.agent`, { encoding: 'utf-8' });
117
+ const output = execSync('launchctl list | grep com.yeaft.agent', {
118
+ encoding: 'utf-8',
119
+ stdio: ['pipe', 'pipe', 'pipe'],
120
+ });
113
121
  if (output.trim()) {
114
122
  const parts = output.trim().split(/\s+/);
115
123
  const pid = parts[0];
116
124
  const exitCode = parts[1];
117
125
  if (pid !== '-') {
118
- console.log(`Service is running (PID: ${pid})`);
119
- } else {
120
- console.log(`Service is stopped (last exit code: ${exitCode})`);
126
+ return { running: true, pid, exitCode };
121
127
  }
122
- } else {
123
- console.log('Service is not installed.');
128
+ return { running: false, pid: null, exitCode };
124
129
  }
130
+ return { running: false, pid: null, exitCode: null };
125
131
  } catch {
132
+ return { running: false, pid: null, exitCode: null };
133
+ }
134
+ }
135
+
136
+ export function macStatus() {
137
+ const status = getMacServiceStatus();
138
+ if (status.running) {
139
+ console.log(`Service is running (PID: ${status.pid})`);
140
+ } else if (status.exitCode !== null) {
141
+ console.log(`Service is stopped (last exit code: ${status.exitCode})`);
142
+ } else {
126
143
  console.log('Service is not installed.');
127
144
  }
128
145
  }
@@ -22,7 +22,7 @@ function ensurePm2() {
22
22
  }
23
23
  }
24
24
 
25
- function getEcosystemPath() {
25
+ export function getEcosystemPath() {
26
26
  return join(getConfigDir(), 'ecosystem.config.cjs');
27
27
  }
28
28
 
@@ -156,6 +156,29 @@ export function winRestart() {
156
156
  }
157
157
  }
158
158
 
159
+ /**
160
+ * Query pm2 for the current service status.
161
+ * Returns { running: boolean, pid: string|null }.
162
+ */
163
+ export function getWinServiceStatus() {
164
+ try {
165
+ const output = execSync('pm2 jlist', {
166
+ encoding: 'utf-8',
167
+ stdio: ['pipe', 'pipe', 'pipe'],
168
+ });
169
+ const apps = JSON.parse(output);
170
+ const app = Array.isArray(apps) && apps.find(a => a.name === 'yeaft-agent');
171
+ if (app) {
172
+ const running = app.pm2_env && app.pm2_env.status === 'online';
173
+ const pid = running ? app.pid : null;
174
+ return { running, pid: pid ? String(pid) : null };
175
+ }
176
+ return { running: false, pid: null };
177
+ } catch {
178
+ return { running: false, pid: null };
179
+ }
180
+ }
181
+
159
182
  export function winStatus() {
160
183
  try {
161
184
  execSync(`pm2 describe ${PM2_APP_NAME}`, { stdio: 'inherit' });