claw-subagent-service 0.0.88 → 0.0.89

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.
@@ -208,6 +208,7 @@ stop_docker() {
208
208
  log_info "OpenClaw 服务已停止。"
209
209
  log_info "服务已成功停止。"
210
210
  log_info "Success"
211
+ exit 0
211
212
  elif check_port "$PORT"; then
212
213
  log_error "OpenClaw 服务停止失败!端口 $PORT 仍在监听。"
213
214
  exit 1
@@ -215,6 +216,7 @@ stop_docker() {
215
216
  log_info "OpenClaw 服务已停止(端口已关闭)。"
216
217
  log_info "服务已成功停止。"
217
218
  log_info "Success"
219
+ exit 0
218
220
  fi
219
221
  }
220
222
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.88",
3
+ "version": "0.0.89",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -4,11 +4,10 @@
4
4
  * 执行 start/stop/restart/status 命令
5
5
  * 基于 nodejs_client/src/main/openclaw-control.ts
6
6
  */
7
- const { spawn } = require('child_process');
8
7
  const path = require('path');
9
- const os = require('os');
10
8
  const { OpenClawCommandEnum, OpenClawServiceStatus, getServiceStatusMessage } = require('./openclaw-enum');
11
9
  const { ScriptExecutor } = require('./script-executor');
10
+ const { ServiceManager } = require('./service-manager');
12
11
  const { getOpenClawStatus } = require('./port-checker');
13
12
 
14
13
  let globalExecutor = null;
@@ -54,71 +53,148 @@ function getCommandName(command) {
54
53
  return names[command] || '未知命令';
55
54
  }
56
55
 
57
- async function executeCommand(command, window, sendResponse) {
58
- const cmdName = getCommandName(command);
59
- console.log(`[OpenClawControl] ${cmdName} OpenClaw...`);
56
+ /**
57
+ * 使用 ServiceManager 管理服务(systemd 模式)
58
+ */
59
+ async function manageWithServiceManager(command) {
60
+ const serviceMgr = new ServiceManager('openclaw-gateway', 'OpenClaw Gateway');
61
+
62
+ try {
63
+ switch (command) {
64
+ case OpenClawCommandEnum.STOP:
65
+ await serviceMgr.stop();
66
+ return { status: OpenClawServiceStatus.STOP_SUCCESS, message: '服务已停止' };
67
+ case OpenClawCommandEnum.START:
68
+ await serviceMgr.start();
69
+ return { status: OpenClawServiceStatus.START_SUCCESS, message: '服务已启动' };
70
+ case OpenClawCommandEnum.RESTART:
71
+ await serviceMgr.restart();
72
+ return { status: OpenClawServiceStatus.RESTART_SUCCESS, message: '服务已重启' };
73
+ case OpenClawCommandEnum.STATUS:
74
+ const status = await serviceMgr.status();
75
+ return { status: OpenClawServiceStatus.RUNNING, message: status };
76
+ default:
77
+ return { status: OpenClawServiceStatus.ERROR, message: '未知命令' };
78
+ }
79
+ } catch (err) {
80
+ // ServiceManager 失败,返回 null 让上层回退到脚本方式
81
+ console.log(`[OpenClawControl] ServiceManager 失败: ${err.message}`);
82
+ return null;
83
+ }
84
+ }
60
85
 
86
+ /**
87
+ * 使用 ScriptExecutor 执行脚本(Docker 模式)
88
+ */
89
+ async function executeWithScript(command) {
61
90
  const executor = getExecutor();
62
91
  const scriptName = getScriptName(command);
63
-
92
+
64
93
  try {
65
- const result = await executor.executeWithStatus(command, scriptName);
66
-
67
- if (
68
- result.status === OpenClawServiceStatus.START_SUCCESS ||
69
- result.status === OpenClawServiceStatus.STOP_SUCCESS ||
70
- result.status === OpenClawServiceStatus.RESTART_SUCCESS
71
- ) {
72
- console.log(`[OpenClawControl] ${cmdName} 成功: ${result.message}`);
73
- // 延迟更新状态
74
- setTimeout(async () => {
75
- const portStatus = await getOpenClawStatus(18789);
76
- console.log(`[OpenClawControl] 状态已更新: ${portStatus === 1 ? '运行中' : '未运行'}`);
77
- }, 2000);
78
- } else if (result.status === OpenClawServiceStatus.ERROR) {
79
- console.log(`[OpenClawControl] ${cmdName} 失败: ${result.message}`);
80
- } else if (result.status === OpenClawServiceStatus.NOT_INSTALL) {
81
- console.log(`[OpenClawControl] ${cmdName}: OpenClaw未安装`);
82
- } else {
83
- console.log(`[OpenClawControl] ${cmdName} 状态: ${result.message}`);
84
- setTimeout(async () => {
85
- const portStatus = await getOpenClawStatus(18789);
86
- console.log(`[OpenClawControl] 状态: ${portStatus}`);
87
- }, 2000);
88
- }
89
-
90
- if (sendResponse) {
91
- let httpStatus = 'success';
92
- if (result.status === OpenClawServiceStatus.ERROR) httpStatus = 'error';
93
- sendResponse({
94
- type: 'command_result',
95
- command,
96
- status: httpStatus,
97
- message: result.message,
98
- service_status: result.status
99
- });
100
- }
101
-
102
- return result;
94
+ return await executor.executeWithStatus(command, scriptName);
103
95
  } catch (e) {
104
96
  const msg = e instanceof Error ? e.message : String(e);
105
- console.log(`[OpenClawControl] ${cmdName} 执行异常: ${msg}`);
106
- if (sendResponse) {
107
- sendResponse({
108
- type: 'command_result',
109
- command,
110
- status: 'error',
111
- message: msg,
112
- service_status: OpenClawServiceStatus.ERROR
113
- });
114
- }
97
+ console.log(`[OpenClawControl] 脚本执行异常: ${msg}`);
115
98
  return {
116
99
  status: OpenClawServiceStatus.ERROR,
117
- message: msg
100
+ message: `执行异常: ${msg}`
118
101
  };
119
102
  }
120
103
  }
121
104
 
105
+ /**
106
+ * 验证命令执行结果
107
+ */
108
+ async function verifyCommandResult(command, result) {
109
+ if (result.status === OpenClawServiceStatus.ERROR) {
110
+ return result;
111
+ }
112
+
113
+ if (command === OpenClawCommandEnum.STOP) {
114
+ // 等待 3 秒后验证端口
115
+ await new Promise(resolve => setTimeout(resolve, 3000));
116
+ const portStatus = await getOpenClawStatus(18789);
117
+ console.log(`[OpenClawControl] 停止后端口状态: ${portStatus === 1 ? '运行中' : '未运行'}`);
118
+
119
+ if (portStatus === 1) {
120
+ return {
121
+ status: OpenClawServiceStatus.ERROR,
122
+ message: '停止失败: 服务仍在运行'
123
+ };
124
+ }
125
+ } else if (command === OpenClawCommandEnum.START || command === OpenClawCommandEnum.RESTART) {
126
+ // 等待服务启动
127
+ const maxWait = command === OpenClawCommandEnum.START ? 30 : 60;
128
+ let attempts = 0;
129
+ let portStatus = 0;
130
+
131
+ while (attempts < maxWait) {
132
+ await new Promise(resolve => setTimeout(resolve, 2000));
133
+ portStatus = await getOpenClawStatus(18789);
134
+ if (portStatus === 1) break;
135
+ attempts++;
136
+ }
137
+
138
+ console.log(`[OpenClawControl] ${getCommandName(command)}后端口状态: ${portStatus === 1 ? '运行中' : '未运行'}`);
139
+
140
+ if (portStatus === 0) {
141
+ return {
142
+ status: OpenClawServiceStatus.ERROR,
143
+ message: `${getCommandName(command)}失败: 服务未运行`
144
+ };
145
+ }
146
+ }
147
+
148
+ return result;
149
+ }
150
+
151
+ async function executeCommand(command, window, sendResponse) {
152
+ const cmdName = getCommandName(command);
153
+ console.log(`[OpenClawControl] ${cmdName} OpenClaw...`);
154
+
155
+ let result;
156
+
157
+ // 优先尝试 ServiceManager(systemd 模式)
158
+ if (process.platform === 'linux' || process.platform === 'darwin') {
159
+ result = await manageWithServiceManager(command);
160
+ }
161
+
162
+ // 如果 ServiceManager 失败或不是 Linux/macOS,使用脚本方式
163
+ if (!result) {
164
+ result = await executeWithScript(command);
165
+ }
166
+
167
+ // 验证结果
168
+ result = await verifyCommandResult(command, result);
169
+
170
+ // 输出日志
171
+ if (result.status === OpenClawServiceStatus.START_SUCCESS ||
172
+ result.status === OpenClawServiceStatus.STOP_SUCCESS ||
173
+ result.status === OpenClawServiceStatus.RESTART_SUCCESS) {
174
+ console.log(`[OpenClawControl] ${cmdName} 成功: ${result.message}`);
175
+ } else if (result.status === OpenClawServiceStatus.ERROR) {
176
+ console.log(`[OpenClawControl] ${cmdName} 失败: ${result.message}`);
177
+ } else if (result.status === OpenClawServiceStatus.NOT_INSTALL) {
178
+ console.log(`[OpenClawControl] ${cmdName}: OpenClaw未安装`);
179
+ } else {
180
+ console.log(`[OpenClawControl] ${cmdName} 状态: ${result.message}`);
181
+ }
182
+
183
+ if (sendResponse) {
184
+ let httpStatus = 'success';
185
+ if (result.status === OpenClawServiceStatus.ERROR) httpStatus = 'error';
186
+ sendResponse({
187
+ type: 'command_result',
188
+ command,
189
+ status: httpStatus,
190
+ message: result.message,
191
+ service_status: result.status
192
+ });
193
+ }
194
+
195
+ return result;
196
+ }
197
+
122
198
  module.exports = {
123
199
  executeCommand,
124
200
  getScriptName,
@@ -88,14 +88,26 @@ class ScriptExecutor {
88
88
  const scriptPath = path.join(this.scriptDir, scriptName);
89
89
 
90
90
  try {
91
- const { stdout, stderr } = await this.runScript(scriptPath);
91
+ const { stdout, stderr, exitCode } = await this.runScript(scriptPath);
92
92
  const fullOutput = stdout + stderr;
93
93
 
94
94
  // 调试日志:记录脚本实际输出
95
95
  console.log(`[ScriptExecutor] 执行脚本: ${scriptPath}`);
96
96
  console.log(`[ScriptExecutor] ${scriptName} 输出:\n${fullOutput}`);
97
+ console.log(`[ScriptExecutor] ${scriptName} 退出码: ${exitCode}`);
98
+
99
+ const result = this.parseStatus(command, fullOutput);
100
+
101
+ // 对于 STOP 命令,如果脚本退出码非零,强制返回错误
102
+ if (command === OpenClawCommandEnum.STOP && exitCode !== 0) {
103
+ console.log(`[ScriptExecutor] 停止脚本退出码非零(${exitCode}),返回错误`);
104
+ return {
105
+ status: OpenClawServiceStatus.ERROR,
106
+ message: `停止失败: 脚本退出码 ${exitCode}`
107
+ };
108
+ }
97
109
 
98
- return this.parseStatus(command, fullOutput);
110
+ return result;
99
111
  } catch (e) {
100
112
  const msg = e instanceof Error ? e.message : String(e);
101
113
  return { status: OpenClawServiceStatus.ERROR, message: `执行异常: ${msg}` };
@@ -239,7 +251,7 @@ class ScriptExecutor {
239
251
  killed = true;
240
252
  clearTimeout(timer);
241
253
  this.killProcessTree(child);
242
- resolve({ stdout, stderr });
254
+ resolve({ stdout, stderr, exitCode: 0 });
243
255
  }
244
256
  }
245
257
  });
@@ -252,7 +264,7 @@ class ScriptExecutor {
252
264
  killed = true;
253
265
  clearTimeout(timer);
254
266
  this.killProcessTree(child);
255
- resolve({ stdout, stderr });
267
+ resolve({ stdout, stderr, exitCode: 0 });
256
268
  }
257
269
  }
258
270
  });
@@ -271,7 +283,7 @@ class ScriptExecutor {
271
283
  killed = true;
272
284
  clearTimeout(timer);
273
285
  console.log(`[ScriptExecutor] 脚本退出,code=${code}, stdout长度=${stdout.length}`);
274
- resolve({ stdout, stderr });
286
+ resolve({ stdout, stderr, exitCode: code });
275
287
  }
276
288
  });
277
289
 
@@ -281,7 +293,7 @@ class ScriptExecutor {
281
293
  killed = true;
282
294
  clearTimeout(timer);
283
295
  console.log(`[ScriptExecutor] 脚本关闭,code=${code}, stdout长度=${stdout.length}`);
284
- resolve({ stdout, stderr });
296
+ resolve({ stdout, stderr, exitCode: code });
285
297
  }
286
298
  });
287
299