coding-tool-x 3.5.2 → 3.5.4

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/src/index.js CHANGED
@@ -6,30 +6,24 @@
6
6
  */
7
7
 
8
8
  const { loadConfig } = require('./config/loader');
9
- const { showMainMenu } = require('./ui/menu');
10
- const { handleList } = require('./commands/list');
11
- const { handleSearch } = require('./commands/search');
12
- const { switchProject } = require('./commands/switch');
13
9
  const { resetConfig } = require('./reset-config');
14
- const { handleChannelManagement, handleAddChannel, handleChannelStatus } = require('./commands/channels');
15
- const { handleToggleProxy } = require('./commands/toggle-proxy');
16
- const { handlePortConfig } = require('./commands/port-config');
17
- const { handleSwitchCliType } = require('./commands/cli-type');
18
10
  const { handleStart, handleStop, handleRestart, handleStatus } = require('./commands/daemon');
19
11
  const { handleProxyStart: proxyStart, handleProxyStop: proxyStop, handleProxyRestart, handleProxyStatus: proxyStatus } = require('./commands/proxy-control');
20
12
  const { handleLogs } = require('./commands/logs');
21
13
  const { handleStats, handleStatsExport } = require('./commands/stats');
22
14
  const { handleDoctor } = require('./commands/doctor');
23
15
  const { handleUpdate } = require('./commands/update');
24
- const { workspaceMenu } = require('./commands/workspace');
25
16
  const { ensureStorageDirMigrated } = require('./config/paths');
26
17
  const PluginManager = require('./plugins/plugin-manager');
27
18
  const eventBus = require('./plugins/event-bus');
28
19
  const chalk = require('chalk');
29
- const inquirer = require('inquirer');
30
20
  const path = require('path');
31
21
  const fs = require('fs');
32
22
 
23
+ function getInquirer() {
24
+ return require('inquirer');
25
+ }
26
+
33
27
  // 读取版本号
34
28
  function getVersion() {
35
29
  const packagePath = path.join(__dirname, '../package.json');
@@ -338,6 +332,7 @@ async function main() {
338
332
 
339
333
  // port 命令 - 配置端口
340
334
  if (args[0] === 'port') {
335
+ const { handlePortConfig } = require('./commands/port-config');
341
336
  await handlePortConfig();
342
337
  return;
343
338
  }
@@ -400,6 +395,7 @@ async function main() {
400
395
 
401
396
  while (true) {
402
397
  // 显示主菜单
398
+ const { showMainMenu } = require('./ui/menu');
403
399
  const action = await showMainMenu(config);
404
400
 
405
401
  // 发送命令开始事件
@@ -409,7 +405,9 @@ async function main() {
409
405
 
410
406
  switch (action) {
411
407
  case 'list':
408
+ const { handleList } = require('./commands/list');
412
409
  await handleList(config, async () => {
410
+ const { switchProject } = require('./commands/switch');
413
411
  const switched = await switchProject(config);
414
412
  if (switched) {
415
413
  // 重新加载配置以获取最新的项目设置
@@ -420,7 +418,9 @@ async function main() {
420
418
  break;
421
419
 
422
420
  case 'search':
421
+ const { handleSearch } = require('./commands/search');
423
422
  await handleSearch(config, async () => {
423
+ const { switchProject } = require('./commands/switch');
424
424
  const switched = await switchProject(config);
425
425
  if (switched) {
426
426
  config = loadConfig();
@@ -430,11 +430,14 @@ async function main() {
430
430
  break;
431
431
 
432
432
  case 'switch':
433
+ const { switchProject } = require('./commands/switch');
433
434
  const switched = await switchProject(config);
434
435
  if (switched) {
435
436
  config = loadConfig();
436
437
  // 切换成功后自动进入会话列表
438
+ const { handleList } = require('./commands/list');
437
439
  await handleList(config, async () => {
440
+ const { switchProject } = require('./commands/switch');
438
441
  const switched = await switchProject(config);
439
442
  if (switched) {
440
443
  config = loadConfig();
@@ -445,26 +448,32 @@ async function main() {
445
448
  break;
446
449
 
447
450
  case 'workspace':
451
+ const { workspaceMenu } = require('./commands/workspace');
448
452
  await workspaceMenu();
449
453
  break;
450
454
 
451
455
  case 'switch-cli-type':
456
+ const { handleSwitchCliType } = require('./commands/cli-type');
452
457
  await handleSwitchCliType();
453
458
  config = loadConfig(); // 重新加载配置以获取新的类型
454
459
  break;
455
460
 
456
461
  case 'switch-channel':
462
+ const { handleChannelManagement } = require('./commands/channels');
457
463
  await handleChannelManagement();
458
464
  break;
459
465
  case 'channel-status':
466
+ const { handleChannelStatus } = require('./commands/channels');
460
467
  await handleChannelStatus();
461
468
  break;
462
469
 
463
470
  case 'toggle-proxy':
471
+ const { handleToggleProxy } = require('./commands/toggle-proxy');
464
472
  await handleToggleProxy();
465
473
  break;
466
474
 
467
475
  case 'add-channel':
476
+ const { handleAddChannel } = require('./commands/channels');
468
477
  await handleAddChannel();
469
478
  break;
470
479
 
@@ -475,6 +484,7 @@ async function main() {
475
484
  }
476
485
 
477
486
  case 'port-config':
487
+ const { handlePortConfig } = require('./commands/port-config');
478
488
  await handlePortConfig();
479
489
  break;
480
490
 
@@ -484,6 +494,7 @@ async function main() {
484
494
 
485
495
  case 'plugin-menu': {
486
496
  const { handlePluginCommand } = require('./commands/plugin');
497
+ const inquirer = getInquirer();
487
498
 
488
499
  // Show plugin management submenu
489
500
  const pluginAction = await inquirer.prompt([{
@@ -1,7 +1,6 @@
1
1
  const express = require('express');
2
2
  const path = require('path');
3
3
  const chalk = require('chalk');
4
- const inquirer = require('inquirer');
5
4
  const { loadConfig } = require('../config/loader');
6
5
  const { PATHS, ensureStorageDirMigrated } = require('../config/paths');
7
6
  const { startWebSocketServer: attachWebSocketServer } = require('./websocket-server');
@@ -25,6 +24,10 @@ const { startOpenCodeProxyServer, collectProxyModelList } = require('./opencode-
25
24
  const { createRemoteMutationGuard } = require('./services/network-access');
26
25
  const { createApiRequestLogger } = require('./services/request-logger');
27
26
 
27
+ function getInquirer() {
28
+ return require('inquirer');
29
+ }
30
+
28
31
  function isInteractivePortConflictMode(options = {}) {
29
32
  if (options.interactive === false) {
30
33
  return false;
@@ -73,6 +76,7 @@ async function startServer(port, host = '127.0.0.1', options = {}) {
73
76
  shouldKill = true;
74
77
  } else if (interactiveMode) {
75
78
  // 询问用户是否关闭占用端口的进程
79
+ const inquirer = getInquirer();
76
80
  const answer = await inquirer.prompt([
77
81
  {
78
82
  type: 'list',
@@ -204,6 +204,8 @@ class McpClient extends EventEmitter {
204
204
  // HTTP/SSE transport state
205
205
  this._sseAbortController = null;
206
206
  this._httpSessionUrl = null;
207
+ this._httpSessionId = null;
208
+ this._negotiatedProtocolVersion = MCP_PROTOCOL_VERSION;
207
209
  }
208
210
 
209
211
  // --------------------------------------------------------------------------
@@ -249,11 +251,12 @@ class McpClient extends EventEmitter {
249
251
  }
250
252
  });
251
253
 
254
+ this._negotiatedProtocolVersion = result.protocolVersion || MCP_PROTOCOL_VERSION;
252
255
  this._serverCapabilities = result.capabilities || {};
253
256
  this._serverInfo = result.serverInfo || {};
254
257
 
255
258
  // Send initialized notification (no response expected)
256
- this._notify('notifications/initialized', {});
259
+ await this._notify('notifications/initialized', {});
257
260
 
258
261
  this._initialized = true;
259
262
  this.emit('initialized', result);
@@ -654,11 +657,18 @@ class McpClient extends EventEmitter {
654
657
  'Content-Type': 'application/json',
655
658
  'Content-Length': Buffer.byteLength(body),
656
659
  'Accept': 'application/json, text/event-stream',
660
+ ...(msg.method !== 'initialize'
661
+ ? {
662
+ 'MCP-Protocol-Version': this._negotiatedProtocolVersion || MCP_PROTOCOL_VERSION,
663
+ ...(this._httpSessionId ? { 'Mcp-Session-Id': this._httpSessionId } : {})
664
+ }
665
+ : {}),
657
666
  ...this._spec.headers
658
667
  }
659
668
  };
660
669
 
661
670
  const req = client.request(options, (res) => {
671
+ this._captureHttpResponseMetadata(res);
662
672
  let data = '';
663
673
  res.on('data', (chunk) => { data += chunk.toString(); });
664
674
  res.on('end', () => {
@@ -669,7 +679,19 @@ class McpClient extends EventEmitter {
669
679
  return;
670
680
  }
671
681
 
682
+ const isNotification = msg.id === undefined || msg.id === null;
672
683
  const contentType = res.headers['content-type'] || '';
684
+ const trimmedData = data.trim();
685
+
686
+ if (!trimmedData) {
687
+ if (isNotification || res.statusCode === 202 || res.statusCode === 204) {
688
+ resolve();
689
+ return;
690
+ }
691
+
692
+ reject(new McpClientError('Empty HTTP response for request'));
693
+ return;
694
+ }
673
695
 
674
696
  // JSON response (direct response to JSON-RPC)
675
697
  if (contentType.includes('application/json')) {
@@ -690,12 +712,6 @@ class McpClient extends EventEmitter {
690
712
  return;
691
713
  }
692
714
 
693
- // Accepted with no body (202, notifications)
694
- if (res.statusCode === 202 || !data.trim()) {
695
- resolve();
696
- return;
697
- }
698
-
699
715
  // Try parsing as JSON anyway
700
716
  try {
701
717
  const parsed = JSON.parse(data);
@@ -761,6 +777,8 @@ class McpClient extends EventEmitter {
761
777
  /** @private */
762
778
  _disconnectHttp() {
763
779
  this._httpSessionUrl = null;
780
+ this._httpSessionId = null;
781
+ this._negotiatedProtocolVersion = MCP_PROTOCOL_VERSION;
764
782
  }
765
783
 
766
784
  // --------------------------------------------------------------------------
@@ -807,7 +825,7 @@ class McpClient extends EventEmitter {
807
825
  }
808
826
 
809
827
  /** @private */
810
- _notify(method, params) {
828
+ async _notify(method, params) {
811
829
  const msg = {
812
830
  jsonrpc: JSONRPC_VERSION,
813
831
  method,
@@ -817,14 +835,20 @@ class McpClient extends EventEmitter {
817
835
  try {
818
836
  if (this._type === 'stdio') {
819
837
  this._sendStdio(msg);
838
+ return;
820
839
  } else {
821
- // Fire-and-forget for HTTP notifications
822
- this._sendHttp(msg).catch((err) => {
823
- this.emit('error', new McpClientError(`Notification send failed: ${err.message}`));
824
- });
840
+ await this._sendHttp(msg);
825
841
  }
826
842
  } catch (err) {
827
- this.emit('error', new McpClientError(`Notification send failed: ${err.message}`));
843
+ throw new McpClientError(`Notification send failed: ${err.message}`);
844
+ }
845
+ }
846
+
847
+ /** @private */
848
+ _captureHttpResponseMetadata(res) {
849
+ const sessionId = res && res.headers ? res.headers['mcp-session-id'] : null;
850
+ if (typeof sessionId === 'string' && sessionId.trim()) {
851
+ this._httpSessionId = sessionId.trim();
828
852
  }
829
853
  }
830
854