linco-connect 1.0.9 → 1.1.1

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.
@@ -16,6 +16,10 @@ const {
16
16
  } = require('../src/config');
17
17
  const { ensureLocalToken, localUrlWithToken } = require('../src/localAuth');
18
18
  const { checkGatewayHealth, resolveHermesGatewayOptions } = require('../src/hermesGateway');
19
+ const {
20
+ checkGatewayHealth: checkOpenClawGatewayHealth,
21
+ resolveOpenClawGatewayOptions,
22
+ } = require('../src/openclawGateway');
19
23
  const pkg = require('../package.json');
20
24
 
21
25
  const rootDir = path.resolve(__dirname, '..');
@@ -147,6 +151,8 @@ function mergeInitConfig(current, values) {
147
151
  appSecret: values.appSecret,
148
152
  enabled: true,
149
153
  };
154
+ if (values.agentType === 'openclaw') accountEntry.openclawAgentId = 'main';
155
+ const agentType = values.agentType || 'claude';
150
156
 
151
157
  return {
152
158
  ...current,
@@ -157,13 +163,13 @@ function mergeInitConfig(current, values) {
157
163
  ...(current.channels?.linco || {}),
158
164
  agents: {
159
165
  ...(current.channels?.linco?.agents || {}),
160
- [values.agentType]: {
161
- ...(current.channels?.linco?.agents?.[values.agentType] || {}),
166
+ [agentType]: {
167
+ ...(current.channels?.linco?.agents?.[agentType] || {}),
162
168
  defaultAccount: values.account,
163
169
  accounts: {
164
- ...(current.channels?.linco?.agents?.[values.agentType]?.accounts || {}),
170
+ ...(current.channels?.linco?.agents?.[agentType]?.accounts || {}),
165
171
  [values.account]: {
166
- ...(current.channels?.linco?.agents?.[values.agentType]?.accounts?.[values.account] || {}),
172
+ ...(current.channels?.linco?.agents?.[agentType]?.accounts?.[values.account] || {}),
167
173
  ...accountEntry,
168
174
  },
169
175
  },
@@ -425,6 +431,19 @@ async function doctorCommand() {
425
431
  } else {
426
432
  checks.push([`${agentType} Gateway`, false, `未运行,且 autoStartGateway=false ${options.gatewayUrl}`]);
427
433
  }
434
+ } else if (agentType === 'openclaw') {
435
+ const options = resolveOpenClawGatewayOptions(agent);
436
+ const health = await checkOpenClawGatewayHealth(options.gatewayUrl, agent, { timeoutMs: 3000 });
437
+ if (health.ok) {
438
+ checks.push([`${agentType} Gateway`, true, options.gatewayUrl]);
439
+ } else if (agent.autoStartGateway !== false) {
440
+ const hasCli = !agent.enabled || (path.isAbsolute(options.openclawBin) ? fs.existsSync(options.openclawBin) : commandExists(options.openclawBin));
441
+ checks.push([`${agentType} Gateway`, true, `not running; OpenClaw messages will auto-start ${options.gatewayUrl}`]);
442
+ checks.push([`${agentType} CLI`, hasCli, options.openclawBin]);
443
+ } else {
444
+ checks.push([`${agentType} Gateway`, false, `not running and autoStartGateway=false ${options.gatewayUrl}`]);
445
+ }
446
+ checks.push([`${agentType} Agent ID`, true, agent.openclawAgentId || 'main']);
428
447
  } else {
429
448
  checks.push([`${agentType} CLI`, !agent.enabled || (path.isAbsolute(agent.bin) ? fs.existsSync(agent.bin) : commandExists(agent.bin)), agent.bin]);
430
449
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "linco-connect",
3
- "version": "1.0.9",
3
+ "version": "1.1.1",
4
4
  "description": "自研 IM 桥接多 Agent 服务",
5
5
  "main": "server.js",
6
6
  "bin": {
package/public/index.html CHANGED
@@ -577,6 +577,7 @@
577
577
  <option value="claude">Claude Code</option>
578
578
  <option value="codex">Codex</option>
579
579
  <option value="hermes">Hermes</option>
580
+ <option value="openclaw">OpenClaw</option>
580
581
  </select>
581
582
  </div>
582
583
  <div id="messages"></div>
@@ -1180,11 +1181,11 @@
1180
1181
 
1181
1182
  function normalizeAgentType(value) {
1182
1183
  const type = String(value || 'claude').trim().toLowerCase();
1183
- return ['claude', 'codex', 'hermes'].includes(type) ? type : 'claude';
1184
+ return ['claude', 'codex', 'hermes', 'openclaw'].includes(type) ? type : 'claude';
1184
1185
  }
1185
1186
 
1186
1187
  function agentLabel(type) {
1187
- return type === 'codex' ? 'Codex' : type === 'hermes' ? 'Hermes' : 'Claude Code';
1188
+ return type === 'codex' ? 'Codex' : type === 'hermes' ? 'Hermes' : type === 'openclaw' ? 'OpenClaw' : 'Claude Code';
1188
1189
  }
1189
1190
 
1190
1191
  function setAgentSelection(agentType, options = {}) {
@@ -1,11 +1,13 @@
1
1
  const claude = require('./agents/claude');
2
2
  const codex = require('./agents/codex');
3
3
  const hermes = require('./agents/hermes');
4
+ const openclaw = require('./agents/openclaw');
4
5
 
5
6
  const providers = {
6
7
  claude,
7
8
  codex,
8
9
  hermes,
10
+ openclaw,
9
11
  };
10
12
 
11
13
  function providerFor(session) {
@@ -1,6 +1,6 @@
1
1
  const { spawn } = require('child_process');
2
2
  const { isDangerousCommand } = require('../danger');
3
- const { send, sendError, sendSystem } = require('../protocol');
3
+ const { send, sendError, sendSystem, sendTurnEnd } = require('../protocol');
4
4
  const { persistAgentSessionId, stopAgentProcess: stopSessionProcess, updateAgentSessionHistory } = require('../session');
5
5
  const { getOutboxDir } = require('../outgoingAttachmentHandler');
6
6
  const { createTextStreamBuffer, appendTextStream, flushTextStream, resetTextStream } = require('../streamBuffer');
@@ -603,6 +603,7 @@ function handleAppServerMessage(message, session) {
603
603
  } else {
604
604
  sendSystem(ws, 'Codex 本次执行没有输出。');
605
605
  }
606
+ sendTurnEnd(ws, session, 'timeout');
606
607
  const cfg = session._lastConfig;
607
608
  if (cfg) drainQueue(ws, session, cfg);
608
609
  }
@@ -622,6 +623,7 @@ function handleAppServerMessage(message, session) {
622
623
  } else {
623
624
  sendSystem(ws, 'Codex 本次执行没有输出。');
624
625
  }
626
+ sendTurnEnd(ws, session);
625
627
  const cfg = session._lastConfig;
626
628
  if (cfg) {
627
629
  drainQueue(ws, session, cfg);
@@ -631,6 +633,7 @@ function handleAppServerMessage(message, session) {
631
633
 
632
634
  if (method === 'error' || method.includes('error')) {
633
635
  sendError(ws, params.message || JSON.stringify(params));
636
+ sendTurnEnd(ws, session, 'error', { error: params.message || JSON.stringify(params) });
634
637
  clearTurnState(session);
635
638
  return;
636
639
  }
@@ -861,6 +864,7 @@ function runExecTurn(input, ws, session, config) {
861
864
  if (code === 0) {
862
865
  updateCodexSessionStats(session);
863
866
  }
867
+ sendTurnEnd(ws, session, code === 0 ? 'completed' : 'error', code === 0 ? {} : { error: stderr.trim() || `Codex 退出,状态码: ${code}` });
864
868
  drainQueue(ws, session, config);
865
869
  });
866
870
 
@@ -1,5 +1,5 @@
1
1
  const { isDangerousCommand } = require('../danger');
2
- const { send, sendError, sendSystem } = require('../protocol');
2
+ const { send, sendError, sendSystem, sendTurnEnd } = require('../protocol');
3
3
  const { persistAgentSessionId, stopAgentProcess: stopSessionProcess, updateAgentSessionHistory, createAgentSessionEntry, saveSessionMetadata } = require('../session');
4
4
  const { getOutboxDir } = require('../outgoingAttachmentHandler');
5
5
  const { createTextStreamBuffer, appendTextStream, flushTextStream, resetTextStream } = require('../streamBuffer');
@@ -177,10 +177,12 @@ function handleHermesEvent(event, ws, session, config) {
177
177
  return;
178
178
  case 'run.failed':
179
179
  sendError(ws, event.error || 'Hermes run 执行失败');
180
+ sendTurnEnd(ws, session, 'error', { error: event.error || 'Hermes run 执行失败' });
180
181
  finishTurn(ws, session, config);
181
182
  return;
182
183
  case 'run.cancelled':
183
184
  sendSystem(ws, 'Hermes run 已停止。');
185
+ sendTurnEnd(ws, session, 'cancelled');
184
186
  finishTurn(ws, session, config);
185
187
  return;
186
188
  default:
@@ -240,6 +242,7 @@ function completeRun(event, ws, session, config) {
240
242
  sendSystem(ws, 'Hermes 本次执行没有输出。');
241
243
  }
242
244
  updateHermesSessionStats(session, event.usage);
245
+ sendTurnEnd(ws, session);
243
246
  finishTurn(ws, session, config);
244
247
  }
245
248