claw-subagent-service 0.0.51 → 0.0.53

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": "claw-subagent-service",
3
- "version": "0.0.51",
3
+ "version": "0.0.53",
4
4
  "description": "虾说智能助手",
5
5
  "main": "cli.js",
6
6
  "bin": {
@@ -196,6 +196,30 @@ function startOpenClawGateway(log) {
196
196
  }
197
197
 
198
198
  class OpenClawClient {
199
+ // 全局并发限制:同时最多运行 N 个 openclaw agent 进程
200
+ static maxConcurrency = 2;
201
+ static runningCount = 0;
202
+ static waitQueue = [];
203
+ // Session 级串行锁:确保同一 session 不会并发 spawn 多个进程
204
+ static sessionLocks = new Map();
205
+
206
+ static async acquireSlot() {
207
+ if (OpenClawClient.runningCount < OpenClawClient.maxConcurrency) {
208
+ OpenClawClient.runningCount++;
209
+ return;
210
+ }
211
+ return new Promise(resolve => OpenClawClient.waitQueue.push(resolve));
212
+ }
213
+
214
+ static releaseSlot() {
215
+ OpenClawClient.runningCount--;
216
+ if (OpenClawClient.waitQueue.length > 0) {
217
+ const next = OpenClawClient.waitQueue.shift();
218
+ OpenClawClient.runningCount++;
219
+ next();
220
+ }
221
+ }
222
+
199
223
  constructor(log) {
200
224
  this.log = log;
201
225
  this.gatewayStarting = false;
@@ -260,6 +284,35 @@ class OpenClawClient {
260
284
 
261
285
  async chatViaCLI(message, fromUser) {
262
286
  const sessionId = `clawmessenger-${fromUser}`;
287
+
288
+ // 1. Session 级串行锁:同一用户的消息排队执行,避免多个进程竞争同一 session 文件
289
+ const previousLock = OpenClawClient.sessionLocks.get(sessionId);
290
+ let resolveLock;
291
+ const currentLock = new Promise(r => { resolveLock = r; });
292
+ OpenClawClient.sessionLocks.set(sessionId, currentLock);
293
+ if (previousLock) {
294
+ this.log?.info(`[OpenClawClient] session ${sessionId} 正在处理中,排队等待...`);
295
+ await previousLock;
296
+ }
297
+
298
+ // 2. 全局并发槽位限制:所有实例共享,防止服务器资源耗尽
299
+ await OpenClawClient.acquireSlot();
300
+ this.log?.info(`[OpenClawClient] 获得执行槽位 (当前运行: ${OpenClawClient.runningCount}/${OpenClawClient.maxConcurrency})`);
301
+
302
+ try {
303
+ return await this._runAgentCLI(message, fromUser, sessionId);
304
+ } finally {
305
+ OpenClawClient.releaseSlot();
306
+ this.log?.info(`[OpenClawClient] 释放执行槽位 (当前运行: ${OpenClawClient.runningCount}/${OpenClawClient.maxConcurrency})`);
307
+ // 释放 session 锁
308
+ resolveLock();
309
+ if (OpenClawClient.sessionLocks.get(sessionId) === currentLock) {
310
+ OpenClawClient.sessionLocks.delete(sessionId);
311
+ }
312
+ }
313
+ }
314
+
315
+ _runAgentCLI(message, fromUser, sessionId) {
263
316
  const escapedMessage = message
264
317
  .replace(/\\/g, '\\\\')
265
318
  .replace(/"/g, '\\"')
@@ -278,9 +331,7 @@ class OpenClawClient {
278
331
 
279
332
  const quoteArg = (s) => `"${s}"`;
280
333
  const cmdParts = ['openclaw', 'agent', '-m', quoteArg(escapedMessage), '--session-id', quoteArg(sessionId)];
281
- if (gatewayToken) {
282
- cmdParts.push('--token', quoteArg(gatewayToken));
283
- }
334
+ // 注意:openclaw agent CLI 不支持 --token 参数,token 通过环境变量传递
284
335
  const command = cmdParts.join(' ');
285
336
 
286
337
  this.log?.info(`[OpenClawClient] 执行: ${command}`);
@@ -292,10 +343,16 @@ class OpenClawClient {
292
343
 
293
344
  // 关键:不设置 OPENCLAW_GATEWAY_URL,避免触发 "gateway url override requires explicit credentials"
294
345
  // 让 openclaw agent 通过默认方式自动发现本地 gateway
346
+ const env = getOpenClawEnv();
347
+ // 将 gateway token 通过环境变量传递(openclaw agent 不支持 --token CLI 参数)
348
+ if (gatewayToken) {
349
+ env.OPENCLAW_API_KEY = gatewayToken;
350
+ env.OPENCLAW_TOKEN = gatewayToken;
351
+ }
295
352
  const child = spawn(command, {
296
353
  shell: true,
297
354
  windowsHide: true,
298
- env: getOpenClawEnv(),
355
+ env,
299
356
  });
300
357
 
301
358
  this.log?.info(`[OpenClawClient] CLI 子进程 PID=${child.pid}`);