claw-subagent-service 0.0.166 → 0.0.170

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/cli.js CHANGED
@@ -24,6 +24,17 @@ const SERVICE_NAME = 'claw-subagent-service';
24
24
  const args = process.argv.slice(2);
25
25
  const command = args[0] || '--run';
26
26
 
27
+ // --version 支持:直接显示版本并退出,不启动 daemon
28
+ if (command === '--version' || command === '-v') {
29
+ try {
30
+ const version = require('./package.json').version;
31
+ console.log(version);
32
+ } catch {
33
+ console.log('unknown');
34
+ }
35
+ process.exit(0);
36
+ }
37
+
27
38
  function runDaemon() {
28
39
  console.log('[CLI] 启动 Daemon...');
29
40
  console.log(`[CLI] CLI 路径: ${__filename}`);
@@ -254,15 +265,38 @@ function controlService(action) {
254
265
  function checkStatus() {
255
266
  const platform = process.platform;
256
267
  let cmd;
257
-
268
+
258
269
  if (platform === 'win32') {
259
- cmd = `sc.exe query ${SERVICE_NAME}`;
270
+ // Windows: 先尝试 sc.exe query,若服务未安装则回退到进程检查
271
+ cmd = `sc.exe query "${SERVICE_NAME}"`;
272
+ exec(cmd, (err, stdout, stderr) => {
273
+ if (err) {
274
+ // 1060 = 服务未安装,给出更友好的提示
275
+ if (stderr && stderr.includes('1060')) {
276
+ console.log(`[CLI] 服务 "${SERVICE_NAME}" 未安装`);
277
+ console.log(`[CLI] 请运行: claw-subagent-service --install`);
278
+ } else {
279
+ console.error(`[CLI] 查询状态失败: ${err.message}`);
280
+ }
281
+ // 回退:检查 daemon 进程是否在前台运行
282
+ try {
283
+ const list = execSync('tasklist /FI "IMAGENAME eq node.exe" /FO CSV /NH', { encoding: 'utf8', windowsHide: true });
284
+ const hasDaemon = list.includes('daemon.js');
285
+ if (hasDaemon) {
286
+ console.log('[CLI] 检测到 Daemon 进程正在前台运行');
287
+ }
288
+ } catch { /* 忽略 */ }
289
+ return;
290
+ }
291
+ console.log(stdout);
292
+ });
293
+ return;
260
294
  } else if (platform === 'linux') {
261
295
  cmd = `systemctl status ${SERVICE_NAME}`;
262
296
  } else if (platform === 'darwin') {
263
297
  cmd = `launchctl list | grep ${SERVICE_NAME}`;
264
298
  }
265
-
299
+
266
300
  exec(cmd, (err, stdout) => {
267
301
  if (err) {
268
302
  console.error(`[CLI] 查询状态失败: ${err.message}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "claw-subagent-service",
3
- "version": "0.0.166",
3
+ "version": "0.0.170",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -130,25 +130,40 @@ function installAndStartService() {
130
130
  svc.on('install', () => {
131
131
  console.log('[postinstall] 服务注册成功,正在启动...');
132
132
 
133
- // 延迟 2 秒确保服务注册到 SCM,再设置开机自启 + 崩溃恢复
133
+ // 延迟 3 秒确保服务注册到 SCM,再设置开机自启 + 崩溃恢复
134
134
  setTimeout(() => {
135
- const cmdFailure = `sc.exe failure "${SERVICE_NAME}" reset= 0 actions= restart/0/restart/0/restart/0`;
136
- exec(cmdFailure, (err) => {
137
- if (err) console.error(`[postinstall] 设置恢复策略失败: ${err.message}`);
138
- else console.log('[postinstall] 恢复策略已设置:崩溃后自动重启');
139
- });
140
-
141
- exec(`sc.exe config "${SERVICE_NAME}" start= auto`, (err) => {
142
- if (err) console.error(`[postinstall] 设置自动启动失败: ${err.message}`);
143
- else console.log('[postinstall] 启动类型已设为:自动');
144
- });
145
- }, 2000);
135
+ // 使用 execSync 确保配置在启动前生效
136
+ try {
137
+ execSync(`sc.exe failure "${SERVICE_NAME}" reset= 0 actions= restart/0/restart/0/restart/0`, { stdio: 'ignore', timeout: 10000 });
138
+ console.log('[postinstall] 恢复策略已设置:崩溃后自动重启');
139
+ } catch (err) {
140
+ console.error(`[postinstall] 设置恢复策略失败: ${err.message}`);
141
+ }
146
142
 
147
- svc.start();
143
+ try {
144
+ execSync(`sc.exe config "${SERVICE_NAME}" start= auto`, { stdio: 'ignore', timeout: 10000 });
145
+ console.log('[postinstall] 启动类型已设为:自动');
146
+ } catch (err) {
147
+ console.error(`[postinstall] 设置自动启动失败: ${err.message}`);
148
+ }
149
+
150
+ // 配置完成后启动服务
151
+ svc.start();
152
+ }, 3000);
148
153
  });
149
154
 
150
155
  svc.on('start', () => {
151
156
  console.log('[postinstall] 服务已启动');
157
+ // 启动后验证服务状态
158
+ setTimeout(() => {
159
+ try {
160
+ const status = execSync(`sc.exe query "${SERVICE_NAME}"`, { encoding: 'utf8', timeout: 5000 });
161
+ console.log('[postinstall] 服务状态验证:');
162
+ console.log(status);
163
+ } catch (err) {
164
+ console.warn(`[postinstall] 服务状态验证失败: ${err.message}`);
165
+ }
166
+ }, 2000);
152
167
  });
153
168
 
154
169
  svc.on('error', (err) => {
@@ -610,9 +610,29 @@ class RongyunMessageHandler {
610
610
  const requestId = data.request_id;
611
611
  const userId = data.userId || data.source_im_id;
612
612
  const sourceId = data.source_im_id;
613
- const content = data.content || data._raw_content;
613
+ let content = data.content || data._raw_content;
614
+ const voiceUrl = data.voiceUrl;
615
+ const voiceDuration = data.voiceDuration;
616
+
617
+ this.logInfo(`[RongyunMessageHandler] 收到客服消息, userId=${userId}, content=${content?.substring(0, 50)}, voiceUrl=${voiceUrl ? '有' : '无'}`);
614
618
 
615
- this.logInfo(`[RongyunMessageHandler] 收到客服消息, userId=${userId}, content=${content?.substring(0, 50)}`);
619
+ // 处理语音消息:如果有语音URL,先进行语音识别
620
+ if (voiceUrl && (!content || content === '[语音]')) {
621
+ try {
622
+ this.logInfo(`[RongyunMessageHandler] 检测到语音消息,开始语音识别: ${voiceUrl}`);
623
+ const recognizedText = await this.recognizeVoice(voiceUrl);
624
+ if (recognizedText) {
625
+ content = recognizedText;
626
+ this.logInfo(`[RongyunMessageHandler] 语音识别成功: ${content.substring(0, 50)}`);
627
+ } else {
628
+ content = '[语音消息识别失败]';
629
+ this.logWarn(`[RongyunMessageHandler] 语音识别失败,使用占位文本`);
630
+ }
631
+ } catch (e) {
632
+ this.logError(`[RongyunMessageHandler] 语音识别异常: ${e.message}`);
633
+ content = '[语音消息识别失败]';
634
+ }
635
+ }
616
636
 
617
637
  if (!content) {
618
638
  this.logWarn('客服消息内容为空');
@@ -691,6 +711,42 @@ class RongyunMessageHandler {
691
711
  }
692
712
  }
693
713
 
714
+ /**
715
+ * 语音识别 - 调用后端 Python 服务
716
+ * @param {string} voiceUrl - 语音文件 URL
717
+ * @returns {Promise<string>} 识别文本
718
+ */
719
+ async recognizeVoice(voiceUrl) {
720
+ try {
721
+ const axios = require('axios');
722
+
723
+ // 从配置中获取后端 API 地址
724
+ const apiBaseUrl = this.config.apiBaseUrl || process.env.API_BASE_URL || 'http://localhost:5000';
725
+ const recognizeUrl = `${apiBaseUrl}/im/api/voice/recognize`;
726
+
727
+ this.logInfo(`[RongyunMessageHandler] 调用语音识别 API: ${recognizeUrl}`);
728
+
729
+ const response = await axios.post(recognizeUrl, {
730
+ audioUrl: voiceUrl,
731
+ format: 'mp3',
732
+ sampleRate: 16000
733
+ }, {
734
+ timeout: 30000,
735
+ headers: { 'Content-Type': 'application/json' }
736
+ });
737
+
738
+ if (response.data && response.data.code === 200) {
739
+ return response.data.data.text;
740
+ } else {
741
+ this.logError(`[RongyunMessageHandler] 语音识别 API 返回错误: ${JSON.stringify(response.data)}`);
742
+ return null;
743
+ }
744
+ } catch (e) {
745
+ this.logError(`[RongyunMessageHandler] 语音识别请求失败: ${e.message}`);
746
+ return null;
747
+ }
748
+ }
749
+
694
750
  async sendResponse(msgType, content, requestId, targetId) {
695
751
  if (!this.messageSender) {
696
752
  this.logError('MessageSender 未设置,无法发送响应');