coding-tool-x 3.6.0 → 3.6.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-tool-x",
3
- "version": "3.6.0",
3
+ "version": "3.6.1",
4
4
  "description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -18,6 +18,7 @@ const channelHealth = new Map(); // `${source}:${channelId}` → health info
18
18
 
19
19
  // 冻结回调(用于通知调度器解绑会话)
20
20
  let onChannelFrozenCallback = null;
21
+ let channelListProvider = null;
21
22
 
22
23
  /**
23
24
  * 设置渠道冻结时的回调
@@ -26,6 +27,10 @@ function setOnChannelFrozen(callback) {
26
27
  onChannelFrozenCallback = callback;
27
28
  }
28
29
 
30
+ function setChannelListProvider(provider) {
31
+ channelListProvider = typeof provider === 'function' ? provider : null;
32
+ }
33
+
29
34
  /**
30
35
  * 初始化渠道健康信息
31
36
  */
@@ -69,6 +74,40 @@ function transitionFrozenChannelIfExpired(channelId, source = 'claude') {
69
74
  return health;
70
75
  }
71
76
 
77
+ function getEnabledChannels(source = 'claude') {
78
+ if (!channelListProvider) {
79
+ return null;
80
+ }
81
+
82
+ try {
83
+ const channels = channelListProvider(source || 'claude');
84
+ if (!Array.isArray(channels)) {
85
+ return null;
86
+ }
87
+ return channels.filter(channel => channel && channel.enabled !== false);
88
+ } catch (err) {
89
+ console.warn(`[ChannelHealth] Failed to inspect ${source || 'claude'} channels before freezing: ${err.message}`);
90
+ return null;
91
+ }
92
+ }
93
+
94
+ function isLastAvailableChannel(channelId, source = 'claude') {
95
+ const enabledChannels = getEnabledChannels(source);
96
+ if (!enabledChannels) {
97
+ return false;
98
+ }
99
+
100
+ const currentExists = enabledChannels.some(channel => channel.id === channelId);
101
+ if (!currentExists) {
102
+ return false;
103
+ }
104
+
105
+ const availableChannels = enabledChannels.filter(channel =>
106
+ channel.id === channelId || isChannelAvailable(channel.id, source)
107
+ );
108
+ return availableChannels.length <= 1;
109
+ }
110
+
72
111
  /**
73
112
  * 记录成功请求
74
113
  */
@@ -107,6 +146,11 @@ function recordFailure(channelId, source = 'claude', error) {
107
146
  // 如果当前是健康状态或检测中状态,检查是否需要冻结
108
147
  if (health.status === 'healthy' || health.status === 'checking') {
109
148
  if (health.consecutiveFailures >= healthConfig.failureThreshold) {
149
+ if (isLastAvailableChannel(channelId, source)) {
150
+ console.warn(`[ChannelHealth] Channel ${channelId} reached failure threshold but remains active because it is the last available ${source || 'claude'} channel`);
151
+ return;
152
+ }
153
+
110
154
  // 触发冻结
111
155
  const previousStatus = health.status;
112
156
  health.status = 'frozen';
@@ -241,5 +285,6 @@ module.exports = {
241
285
  getAllChannelHealthStatus,
242
286
  resetChannelHealth,
243
287
  setOnChannelFrozen,
288
+ setChannelListProvider,
244
289
  healthConfig,
245
290
  };
@@ -2,7 +2,7 @@ const { getAllChannels } = require('./channels');
2
2
  const { getChannels: getCodexChannels } = require('./codex-channels');
3
3
  const { getChannels: getGeminiChannels } = require('./gemini-channels');
4
4
  const { getChannels: getOpenCodeChannels } = require('./opencode-channels');
5
- const { isChannelAvailable, getChannelHealthStatus, setOnChannelFrozen } = require('./channel-health');
5
+ const { isChannelAvailable, getChannelHealthStatus, setOnChannelFrozen, setChannelListProvider } = require('./channel-health');
6
6
 
7
7
  const channelProviders = {
8
8
  claude: () => getAllChannels(),
@@ -64,6 +64,11 @@ function unbindChannelSessions(source, channelId) {
64
64
 
65
65
  // 注册冻结回调,当渠道被冻结时解绑其会话
66
66
  setOnChannelFrozen(unbindChannelSessions);
67
+ setChannelListProvider((source = 'claude') => {
68
+ const provider = channelProviders[source || 'claude'];
69
+ if (!provider) return [];
70
+ return provider();
71
+ });
67
72
 
68
73
  function refreshChannels(source = 'claude') {
69
74
  const state = getState(source);