claw-subagent-service 0.0.133 → 0.0.134

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.
@@ -252,8 +252,8 @@ start_docker() {
252
252
  log_info "尝试启动: openclaw gateway run --port $PORT"
253
253
  nohup openclaw gateway run --port "$PORT" > "$log_file" 2>&1 &
254
254
 
255
- # 等待 5 秒检查进程是否启动(给更多时间)
256
- sleep 5
255
+ # 等待 10 秒检查进程是否启动(给更多时间)
256
+ sleep 10
257
257
  local started_pid=$!
258
258
  log_info "启动的进程 PID: $started_pid"
259
259
 
@@ -272,7 +272,7 @@ start_docker() {
272
272
 
273
273
  # 检查进程监听的端口
274
274
  log_info "检查进程监听的端口..."
275
- sleep 2
275
+ sleep 5
276
276
  local listening_ports=$(netstat -tlnp 2>/dev/null | grep "$started_pid" || ss -tlnp 2>/dev/null | grep "$started_pid" || echo "")
277
277
  if [ -n "$listening_ports" ]; then
278
278
  log_info "进程 $started_pid 监听的端口:"
@@ -460,6 +460,12 @@ show_help() {
460
460
  main() {
461
461
  local force=""
462
462
 
463
+ # 检查 FORCE 环境变量(来自 script-executor 的强制停止模式)
464
+ if [ "${FORCE:-}" = "1" ]; then
465
+ force="1"
466
+ log_info "检测到 FORCE=1 环境变量,启用强制停止模式"
467
+ fi
468
+
463
469
  # 解析命令行参数
464
470
  while [ $# -gt 0 ]; do
465
471
  case $1 in
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.133",
3
+ "version": "0.0.134",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -112,14 +112,16 @@ async function manageWithServiceManager(command) {
112
112
 
113
113
  /**
114
114
  * 使用 ScriptExecutor 执行脚本(Docker 模式)
115
+ * @param {number} command - 命令代码
116
+ * @param {boolean} force - 是否强制停止
115
117
  * @returns {Object} { result, output }
116
118
  */
117
- async function executeWithScript(command) {
119
+ async function executeWithScript(command, force = false) {
118
120
  const executor = getExecutor();
119
121
  const scriptName = getScriptName(command);
120
122
 
121
123
  try {
122
- const result = await executor.executeWithStatus(command, scriptName);
124
+ const result = await executor.executeWithStatus(command, scriptName, force);
123
125
  // ScriptExecutor 现在返回 { status, message, output }
124
126
  return {
125
127
  result: { status: result.status, message: result.message },
@@ -200,10 +202,11 @@ async function verifyCommandResult(command, result, scriptOutput = '') {
200
202
  return result;
201
203
  }
202
204
 
203
- async function executeCommand(command, window, sendResponse) {
205
+ async function executeCommand(command, window, sendResponse, force = false) {
204
206
  const cmdName = getCommandName(command);
205
- console.log(`[OpenClawControl] ====== 开始执行 ${cmdName} 命令 ======`);
206
- console.log(`[OpenClawControl] 命令代码: ${command}`);
207
+ const actionName = force ? '强制' + cmdName : cmdName;
208
+ console.log(`[OpenClawControl] ====== 开始执行 ${actionName} 命令 ======`);
209
+ console.log(`[OpenClawControl] 命令代码: ${command}, force: ${force}`);
207
210
  console.log(`[OpenClawControl] 平台: ${process.platform}`);
208
211
 
209
212
  let result;
@@ -223,7 +226,7 @@ async function executeCommand(command, window, sendResponse) {
223
226
  let scriptOutput = '';
224
227
  if (!result) {
225
228
  console.log(`[OpenClawControl] 使用脚本方式执行...`);
226
- const scriptResult = await executeWithScript(command);
229
+ const scriptResult = await executeWithScript(command, force);
227
230
  result = scriptResult.result;
228
231
  scriptOutput = scriptResult.output;
229
232
  console.log(`[OpenClawControl] 脚本执行结果: ${result.status} - ${result.message}`);
@@ -242,13 +245,13 @@ async function executeCommand(command, window, sendResponse) {
242
245
  result.status === OpenClawServiceStatus.STOP_SUCCESS ||
243
246
  result.status === OpenClawServiceStatus.RESTART_SUCCESS ||
244
247
  result.status === OpenClawServiceStatus.CONFIG_FIX_SUCCESS) {
245
- console.log(`[OpenClawControl] ${cmdName} 成功: ${result.message}`);
248
+ console.log(`[OpenClawControl] ${actionName} 成功: ${result.message}`);
246
249
  } else if (result.status === OpenClawServiceStatus.ERROR) {
247
- console.log(`[OpenClawControl] ${cmdName} 失败: ${result.message}`);
250
+ console.log(`[OpenClawControl] ${actionName} 失败: ${result.message}`);
248
251
  } else if (result.status === OpenClawServiceStatus.NOT_INSTALL) {
249
- console.log(`[OpenClawControl] ${cmdName}: OpenClaw未安装`);
252
+ console.log(`[OpenClawControl] ${actionName}: OpenClaw未安装`);
250
253
  } else {
251
- console.log(`[OpenClawControl] ${cmdName} 状态: ${result.message}`);
254
+ console.log(`[OpenClawControl] ${actionName} 状态: ${result.message}`);
252
255
  }
253
256
 
254
257
  if (sendResponse) {
@@ -264,16 +267,19 @@ async function executeCommand(command, window, sendResponse) {
264
267
  const { getOpenClawStatus } = require('./port-checker');
265
268
  const portStatus = await getOpenClawStatus(18789);
266
269
  console.log(`[OpenClawControl] 端口状态: ${portStatus}`);
267
- // 更新真实状态
270
+ // 更新真实状态 - 始终以端口检测结果为准
268
271
  if (command === OpenClawCommandEnum.STOP) {
269
272
  realStatus = portStatus === 1 ? OpenClawServiceStatus.RUNNING : OpenClawServiceStatus.STOP_SUCCESS;
270
273
  realMessage = portStatus === 1 ? '停止失败: 服务仍在运行' : '服务已停止';
274
+ if (portStatus === 1) httpStatus = 'error';
271
275
  } else if (command === OpenClawCommandEnum.START) {
272
276
  realStatus = portStatus === 1 ? OpenClawServiceStatus.START_SUCCESS : OpenClawServiceStatus.ERROR;
273
277
  realMessage = portStatus === 1 ? '服务已启动' : '启动失败: 服务未运行';
278
+ if (portStatus !== 1) httpStatus = 'error';
274
279
  } else if (command === OpenClawCommandEnum.RESTART) {
275
280
  realStatus = portStatus === 1 ? OpenClawServiceStatus.RESTART_SUCCESS : OpenClawServiceStatus.ERROR;
276
281
  realMessage = portStatus === 1 ? '服务已重启' : '重启失败: 服务未运行';
282
+ if (portStatus !== 1) httpStatus = 'error';
277
283
  }
278
284
  console.log(`[OpenClawControl] 操作后真实状态: portStatus=${portStatus}, realStatus=${realStatus}, message=${realMessage}`);
279
285
  } catch (e) {
@@ -64,27 +64,40 @@ class RongyunMessageHandler {
64
64
  return;
65
65
  }
66
66
 
67
- const msgType = parsed.msg_type;
67
+ // 解析 content 字段(前端将业务数据放在 content 内)
68
+ let data = parsed;
69
+ if (parsed.content && typeof parsed.content === 'string') {
70
+ try {
71
+ const contentData = JSON.parse(parsed.content);
72
+ // 将 content 内的数据合并到顶层,方便处理器直接访问
73
+ // 保留原始 content 字段(用于聊天消息等场景)
74
+ data = { ...parsed, ...contentData, _raw_content: parsed.content };
75
+ } catch (e) {
76
+ this.logWarn(`解析 content 失败: ${e.message}`);
77
+ }
78
+ }
79
+
80
+ const msgType = data.msg_type;
68
81
  this.logInfo(`[RongyunMessageHandler] 处理消息类型: ${msgType}`);
69
82
 
70
83
  switch (msgType) {
71
84
  case RongyunMessageTypeEnum.COMMAND:
72
- await this.handleCommand(parsed);
85
+ await this.handleCommand(data);
73
86
  break;
74
87
  case RongyunMessageTypeEnum.CHAT_MESSAGE:
75
- await this.handleChatMessage(parsed);
88
+ await this.handleChatMessage(data);
76
89
  break;
77
90
  case RongyunMessageTypeEnum.CREATE_OPENCODE_SESSION:
78
- await this.handleCreateSession(parsed);
91
+ await this.handleCreateSession(data);
79
92
  break;
80
93
  case RongyunMessageTypeEnum.DELETE_OPENCODE_SESSION:
81
- await this.handleDeleteSession(parsed);
94
+ await this.handleDeleteSession(data);
82
95
  break;
83
96
  case RongyunMessageTypeEnum.DEVICE_CONTROL:
84
- await this.handleDeviceControl(parsed);
97
+ await this.handleDeviceControl(data);
85
98
  break;
86
99
  case RongyunMessageTypeEnum.DEVICE_STATUS_REQUEST:
87
- await this.handleDeviceStatusRequest(parsed);
100
+ await this.handleDeviceStatusRequest(data);
88
101
  break;
89
102
  default:
90
103
  this.logWarn(`未处理的消息类型: ${msgType}`);
@@ -100,8 +113,9 @@ class RongyunMessageHandler {
100
113
  const commandId = data.command_id;
101
114
  const requestId = data.request_id;
102
115
  const sourceId = data.source_im_id;
116
+ const force = data.force === true; // 是否强制停止
103
117
 
104
- this.logInfo(`[RongyunMessageHandler] 收到命令: command=${command}, command_id=${commandId}, from=${sourceId || 'guardserver'}`);
118
+ this.logInfo(`[RongyunMessageHandler] 收到命令: command=${command}, command_id=${commandId}, force=${force}, from=${sourceId || 'guardserver'}`);
105
119
 
106
120
  // 验证命令是否有效
107
121
  const validCommands = Object.values(OpenClawCommandEnum);
@@ -143,11 +157,12 @@ class RongyunMessageHandler {
143
157
 
144
158
  if (isAsyncCommand) {
145
159
  // 立即响应,告知前端命令已接收
160
+ const actionName = force ? '强制' + getCommandName(command) : getCommandName(command);
146
161
  await this.sendResponse(RongyunMessageTypeEnum.COMMAND_RESULT, {
147
162
  command,
148
163
  command_id: commandId,
149
164
  status: 'success',
150
- message: `${getCommandName(command)}命令已接收,正在执行...`
165
+ message: `${actionName}命令已接收,正在执行...`
151
166
  }, requestId, sourceId);
152
167
  }
153
168
 
@@ -163,8 +178,8 @@ class RongyunMessageHandler {
163
178
  }
164
179
  // 异步命令不在这里响应,因为已经提前响应了
165
180
  // 但我们可以在这里记录执行结果
166
- this.logInfo(`[RongyunMessageHandler] 命令执行完成: command=${command}, status=${response.status}, message=${response.message}`);
167
- }).then(() => {
181
+ this.logInfo(`[RongyunMessageHandler] 命令执行完成: command=${command}, force=${force}, status=${response.status}, message=${response.message}`);
182
+ }, force).then(() => {
168
183
  // 命令执行完成后,立即释放锁
169
184
  this.commandLock = false;
170
185
  if (this.commandLockTimer) {
@@ -206,7 +221,8 @@ class RongyunMessageHandler {
206
221
  async handleChatMessage(data) {
207
222
  const roomId = data.room_id;
208
223
  const sessionId = data.gateway_session_id || data.session_id;
209
- const content = data.content;
224
+ // 使用解析后的 content(聊天内容),如果没有则使用原始 content
225
+ const content = data.content || data._raw_content;
210
226
  const requestId = data.request_id;
211
227
 
212
228
  this.logInfo(`[RongyunMessageHandler] 收到聊天消息, roomId=${roomId}, sessionId=${sessionId}`);
@@ -79,7 +79,7 @@ class ScriptExecutor {
79
79
  }
80
80
  }
81
81
 
82
- async executeWithStatus(command, scriptName) {
82
+ async executeWithStatus(command, scriptName, force = false) {
83
83
  // 配置修复命令特殊处理:直接执行命令,不需要脚本
84
84
  if (command === OpenClawCommandEnum.CONFIG_FIX) {
85
85
  return await this.executeCommandDirect('openclaw doctor --fix', 120);
@@ -88,8 +88,8 @@ class ScriptExecutor {
88
88
  const scriptPath = path.join(this.scriptDir, scriptName);
89
89
 
90
90
  try {
91
- console.log(`[ScriptExecutor] 开始执行脚本: ${scriptPath}`);
92
- const { stdout, stderr, exitCode } = await this.runScript(scriptPath);
91
+ console.log(`[ScriptExecutor] 开始执行脚本: ${scriptPath}, force=${force}`);
92
+ const { stdout, stderr, exitCode } = await this.runScript(scriptPath, force);
93
93
  const fullOutput = stdout + stderr;
94
94
 
95
95
  // 调试日志:记录脚本实际输出
@@ -206,7 +206,7 @@ class ScriptExecutor {
206
206
  });
207
207
  }
208
208
 
209
- runScript(scriptPath) {
209
+ runScript(scriptPath, force = false) {
210
210
  // Linux/Mac 系统:自动修复 shell 脚本的 CRLF 换行符
211
211
  if (process.platform !== 'win32') {
212
212
  try {
@@ -233,6 +233,12 @@ class ScriptExecutor {
233
233
  cmd = 'bash';
234
234
  args = [scriptPath];
235
235
  }
236
+
237
+ // 如果是强制停止,添加 FORCE 环境变量
238
+ if (force) {
239
+ args.unshift('FORCE=1');
240
+ console.log(`[ScriptExecutor] 强制停止模式: FORCE=1`);
241
+ }
236
242
 
237
243
  // 调试日志:记录执行环境
238
244
  if (process.platform !== 'win32') {