coding-tool-x 3.7.7 → 3.7.8

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.
@@ -5,7 +5,7 @@
5
5
  <link rel="icon" href="/favicon.ico">
6
6
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
7
7
  <title>coding-tool-x · AI 编程工作台</title>
8
- <script type="module" crossorigin src="/assets/index-DxkAvn4g.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-CeTaG4IX.js"></script>
9
9
  <link rel="modulepreload" crossorigin href="/assets/markdown-BNJfEgCA.js">
10
10
  <link rel="modulepreload" crossorigin href="/assets/vue-vendor-B5RqjEjQ.js">
11
11
  <link rel="modulepreload" crossorigin href="/assets/naive-ui-CVyvdmg1.js">
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "coding-tool-x",
3
- "version": "3.7.7",
3
+ "version": "3.7.8",
4
4
  "description": "Vibe Coding 增强工作助手 - 智能会话管理、动态渠道切换、全局搜索、实时监控",
5
5
  "main": "src/index.js",
6
6
  "bin": {
@@ -24,8 +24,7 @@ const {
24
24
  const {
25
25
  findAuthProviderForKey,
26
26
  getCachedOmpAuthProviderSnapshot,
27
- getOmpAuthProviderCacheMeta,
28
- warmOmpAuthProviderSnapshot
27
+ getOmpAuthProviderCacheMeta
29
28
  } = require('../services/omp-auth-providers');
30
29
 
31
30
  const CHANNEL_LIST_AUTH_OPTIONS = { accountCheck: false, includeStatus: false };
@@ -77,6 +76,8 @@ async function discoverOmpModels(channel, gatewaySourceType, { forceRefresh = fa
77
76
  models: preferredModels,
78
77
  supported: preferredModels.length > 0,
79
78
  cached: false,
79
+ stale: false,
80
+ retryAfter: null,
80
81
  fallbackUsed: true,
81
82
  fetchedAt: new Date().toISOString(),
82
83
  error: preferredModels.length > 0 ? null : 'OAuth 渠道未配置 API Key,无法直接调用远端模型列表接口',
@@ -91,11 +92,27 @@ async function discoverOmpModels(channel, gatewaySourceType, { forceRefresh = fa
91
92
  });
92
93
  const listedModels = Array.isArray(listResult.models) ? uniqueModels(listResult.models) : [];
93
94
 
95
+ if (listResult.backoff || listResult.stale) {
96
+ return {
97
+ models: listedModels,
98
+ supported: listedModels.length > 0,
99
+ cached: !!listResult.cached,
100
+ stale: true,
101
+ retryAfter: listResult.retryAfter || null,
102
+ fallbackUsed: false,
103
+ fetchedAt: listResult.lastChecked || new Date().toISOString(),
104
+ error: listResult.error || (listedModels.length > 0 ? null : '模型目录缓存已过期'),
105
+ errorHint: listResult.errorHint || '可点击“刷新可选模型”更新,或手动填写模型名称。'
106
+ };
107
+ }
108
+
94
109
  if (listedModels.length > 0) {
95
110
  return {
96
111
  models: listedModels,
97
112
  supported: true,
98
113
  cached: !!listResult.cached,
114
+ stale: false,
115
+ retryAfter: null,
99
116
  fallbackUsed: false,
100
117
  fetchedAt: listResult.lastChecked || new Date().toISOString(),
101
118
  error: null,
@@ -114,6 +131,8 @@ async function discoverOmpModels(channel, gatewaySourceType, { forceRefresh = fa
114
131
  models: probedModels,
115
132
  supported: probedModels.length > 0,
116
133
  cached: !!probe.cached || !!listResult.cached,
134
+ stale: false,
135
+ retryAfter: null,
117
136
  fallbackUsed: probedModels.length > 0,
118
137
  fetchedAt: probe.lastChecked || listResult.lastChecked || new Date().toISOString(),
119
138
  error: probedModels.length > 0 ? null : (listResult.error || '无法获取可用模型'),
@@ -142,17 +161,12 @@ function attachAuthProvider(channel, snapshot) {
142
161
 
143
162
  function getAuthSnapshotForChannelList() {
144
163
  const snapshot = getCachedOmpAuthProviderSnapshot(CHANNEL_LIST_AUTH_OPTIONS);
145
- const shouldWarm = !snapshot || snapshot.stale;
146
- if (shouldWarm) {
147
- warmOmpAuthProviderSnapshot(CHANNEL_LIST_AUTH_OPTIONS);
148
- }
149
164
  const meta = getOmpAuthProviderCacheMeta(CHANNEL_LIST_AUTH_OPTIONS);
150
165
  return {
151
166
  snapshot,
152
167
  meta: {
153
168
  ...meta,
154
169
  stale: !snapshot || Boolean(snapshot.stale),
155
- refreshing: shouldWarm || meta.refreshing,
156
170
  fallback: !snapshot
157
171
  }
158
172
  };
@@ -241,7 +255,8 @@ module.exports = () => {
241
255
  model,
242
256
  speedTestModel,
243
257
  allowedModels,
244
- modelRedirects
258
+ modelRedirects,
259
+ forceRefresh
245
260
  } = req.body || {};
246
261
  if (!baseUrl) {
247
262
  return res.status(400).json({ error: 'baseUrl is required' });
@@ -259,11 +274,14 @@ module.exports = () => {
259
274
  modelRedirects: Array.isArray(modelRedirects) ? modelRedirects : []
260
275
  };
261
276
  const resolvedGatewaySourceType = resolveGatewaySourceType(tempChannel);
262
- const result = await discoverOmpModels(tempChannel, resolvedGatewaySourceType, { forceRefresh: true });
277
+ const result = await discoverOmpModels(tempChannel, resolvedGatewaySourceType, { forceRefresh: forceRefresh === true });
263
278
  res.json({
264
279
  models: result.models,
265
280
  supported: result.supported,
266
281
  fallbackUsed: result.fallbackUsed,
282
+ cached: result.cached,
283
+ stale: result.stale,
284
+ retryAfter: result.retryAfter,
267
285
  error: result.error,
268
286
  errorHint: result.errorHint
269
287
  });
@@ -291,6 +309,8 @@ module.exports = () => {
291
309
  models: result.models,
292
310
  supported: result.supported,
293
311
  cached: result.cached,
312
+ stale: result.stale,
313
+ retryAfter: result.retryAfter,
294
314
  fallbackUsed: result.fallbackUsed,
295
315
  fetchedAt: result.fetchedAt,
296
316
  error: result.error,
@@ -6,8 +6,7 @@ const {
6
6
  getOmpPaths,
7
7
  getOmpStatus,
8
8
  readJsonFile,
9
- readYamlFile,
10
- readOmpSettings
9
+ readYamlFile
11
10
  } = require('../services/omp-config');
12
11
  const {
13
12
  getOmpAuthProviderSnapshot
@@ -89,13 +88,12 @@ function buildCapabilities() {
89
88
 
90
89
  router.get('/', (req, res) => {
91
90
  try {
92
- const paths = getOmpPaths();
93
- const settings = readOmpSettings();
91
+ const paths = getOmpPaths(process.env, { resolveRuntime: false });
92
+ const settings = readYamlFile(paths.settings, readJsonFile(paths.settingsJsonLegacy, {}));
94
93
  const resources = {
95
94
  config: readYamlFile(paths.config, {}),
96
95
  settings: readYamlFile(paths.settings, {}),
97
96
  auth: fs.existsSync(paths.auth) ? { exists: true, path: paths.auth } : { exists: false, path: paths.auth },
98
- authProviders: getOmpAuthProviderSnapshot({ accountCheck: false }),
99
97
  models: readYamlFile(paths.modelsYml, {}),
100
98
  legacyModels: readJsonFile(paths.modelsJsonLegacy, {}),
101
99
  packages: normalizeArray(settings.packages),
@@ -110,7 +108,7 @@ router.get('/', (req, res) => {
110
108
 
111
109
  res.json({
112
110
  success: true,
113
- status: getOmpStatus(),
111
+ status: getOmpStatus(process.env, { resolveRuntime: false }),
114
112
  capabilities: buildCapabilities(),
115
113
  resources
116
114
  });
@@ -143,8 +141,8 @@ router.get('/auth-providers', (req, res) => {
143
141
 
144
142
  router.get('/resources', (req, res) => {
145
143
  try {
146
- const paths = getOmpPaths();
147
- const settings = readOmpSettings();
144
+ const paths = getOmpPaths(process.env, { resolveRuntime: false });
145
+ const settings = readYamlFile(paths.settings, readJsonFile(paths.settingsJsonLegacy, {}));
148
146
  res.json({
149
147
  success: true,
150
148
  paths,
@@ -233,6 +233,9 @@ const MODEL_ALIASES = {
233
233
  };
234
234
 
235
235
  const TEST_TIMEOUT_MS = 10000; // 10 seconds per model test
236
+ const MODEL_LIST_CACHE_TTL_MS = 24 * 60 * 60 * 1000;
237
+ const MODEL_LIST_FAILURE_TTL_MS = 5 * 60 * 1000;
238
+ const MODEL_LIST_AUTH_FAILURE_TTL_MS = 60 * 60 * 1000;
236
239
  const CLAUDE_CODE_BETA_HEADER = 'claude-code-20250219,interleaved-thinking-2025-05-14';
237
240
 
238
241
  const MODEL_UNAVAILABLE_HINTS = [
@@ -797,6 +800,84 @@ function isSignatureCacheValid(cacheEntry, signatureKey, expectedSignature) {
797
800
  return cacheEntry[signatureKey] === expectedSignature;
798
801
  }
799
802
 
803
+ function getCacheAgeMs(timestamp, now = Date.now()) {
804
+ const checkedAt = Date.parse(timestamp || '');
805
+ return Number.isFinite(checkedAt) ? Math.max(0, now - checkedAt) : Infinity;
806
+ }
807
+
808
+ function isCacheFresh(timestamp, ttlMs, now = Date.now()) {
809
+ return getCacheAgeMs(timestamp, now) < ttlMs;
810
+ }
811
+
812
+ function getModelListFailureTtl(statusCode) {
813
+ return [401, 403, 404, 429].includes(Number(statusCode))
814
+ ? MODEL_LIST_AUTH_FAILURE_TTL_MS
815
+ : MODEL_LIST_FAILURE_TTL_MS;
816
+ }
817
+
818
+ function buildCachedModelListResult(entry, listSignature, forceRefresh) {
819
+ if (forceRefresh || !isSignatureCacheValid(entry, 'listSignature', listSignature)) {
820
+ return null;
821
+ }
822
+
823
+ const failure = entry.modelListFailure;
824
+ if (failure && Date.parse(failure.retryAfter || '') > Date.now()) {
825
+ return {
826
+ models: Array.isArray(entry.fetchedModels) ? entry.fetchedModels : [],
827
+ supported: true,
828
+ cached: true,
829
+ stale: true,
830
+ backoff: true,
831
+ fallbackUsed: false,
832
+ error: failure.error || '模型列表暂时不可用',
833
+ errorHint: failure.errorHint || '请稍后重试,或手动填写模型名称',
834
+ statusCode: failure.statusCode,
835
+ lastChecked: entry.lastChecked || failure.failedAt || null,
836
+ retryAfter: failure.retryAfter
837
+ };
838
+ }
839
+
840
+ if (!Array.isArray(entry.fetchedModels)) {
841
+ return null;
842
+ }
843
+
844
+ const stale = !isCacheFresh(entry.lastChecked, MODEL_LIST_CACHE_TTL_MS);
845
+ const result = {
846
+ models: entry.fetchedModels,
847
+ supported: true,
848
+ cached: true,
849
+ fallbackUsed: false,
850
+ error: null,
851
+ lastChecked: entry.lastChecked || null
852
+ };
853
+ if (stale) {
854
+ result.stale = true;
855
+ result.errorHint = '模型目录缓存已过期;可点击“刷新可选模型”更新。';
856
+ }
857
+ return result;
858
+ }
859
+
860
+ function saveModelListFailure(cache, cacheKey, listSignature, result = {}) {
861
+ const now = new Date();
862
+ const ttlMs = getModelListFailureTtl(result.statusCode);
863
+ const previous = cache[cacheKey] || {};
864
+ const failure = {
865
+ error: result.error || '模型列表暂时不可用',
866
+ errorHint: result.errorHint || '请稍后重试,或手动填写模型名称',
867
+ statusCode: result.statusCode || null,
868
+ failedAt: now.toISOString(),
869
+ retryAfter: new Date(now.getTime() + ttlMs).toISOString()
870
+ };
871
+ cache[cacheKey] = {
872
+ ...previous,
873
+ fetchedModels: Array.isArray(previous.fetchedModels) ? previous.fetchedModels : [],
874
+ listSignature,
875
+ modelListFailure: failure
876
+ };
877
+ saveModelCache(cache);
878
+ return failure;
879
+ }
880
+
800
881
  /**
801
882
  * Test if a specific model is available for a channel
802
883
  * @param {Object} channel - Channel configuration
@@ -869,8 +950,10 @@ async function probeModelAvailability(channel, channelType, options = {}) {
869
950
  });
870
951
  const cacheKey = getChannelCacheKey(channel, probeSignature);
871
952
 
872
- // Return cached result if channel and probe options are unchanged
873
- if (!forceRefresh && isSignatureCacheValid(cache[cacheKey], 'probeSignature', probeSignature)) {
953
+ // Return cached result only while the matching probe result is still fresh.
954
+ if (!forceRefresh
955
+ && isSignatureCacheValid(cache[cacheKey], 'probeSignature', probeSignature)
956
+ && isCacheFresh(cache[cacheKey].lastChecked, MODEL_LIST_CACHE_TTL_MS)) {
874
957
  return {
875
958
  availableModels: cache[cacheKey].availableModels || [],
876
959
  preferredTestModel: cache[cacheKey].preferredTestModel || null,
@@ -1026,21 +1109,17 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1026
1109
  });
1027
1110
  const cacheKey = getChannelCacheKey(channel, listSignature);
1028
1111
 
1029
- // Check cache first, and only reuse when channel/list context is unchanged
1030
- if (!forceRefresh
1031
- && isSignatureCacheValid(cache[cacheKey], 'listSignature', listSignature)
1032
- && Array.isArray(cache[cacheKey].fetchedModels)) {
1033
- return {
1034
- models: cache[cacheKey].fetchedModels || [],
1035
- supported: true,
1036
- cached: true,
1037
- fallbackUsed: false,
1038
- error: null,
1039
- lastChecked: cache[cacheKey].lastChecked
1040
- };
1041
- }
1112
+ // Reuse a fresh catalog immediately. Expired catalogs are also returned as
1113
+ // stale data so opening the editor never blocks on the provider; the user
1114
+ // can explicitly request a refresh.
1115
+ const cachedResult = buildCachedModelListResult(cache[cacheKey], listSignature, forceRefresh);
1116
+ if (cachedResult) return cachedResult;
1042
1117
 
1043
1118
  return new Promise((resolve) => {
1119
+ const resolveFailure = (result) => {
1120
+ saveModelListFailure(cache, cacheKey, listSignature, result);
1121
+ resolve(result);
1122
+ };
1044
1123
  try {
1045
1124
  const baseUrl = channel.baseUrl.trim().replace(/\/+$/, '');
1046
1125
  const endpoint = capability.modelListEndpoint; // e.g. '/v1/models'
@@ -1095,7 +1174,8 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1095
1174
  availableModels: cache[cacheKey]?.availableModels || [],
1096
1175
  preferredTestModel: cache[cacheKey]?.preferredTestModel || null,
1097
1176
  probeSignature: cache[cacheKey]?.probeSignature || null,
1098
- listSignature
1177
+ listSignature,
1178
+ modelListFailure: null
1099
1179
  };
1100
1180
 
1101
1181
  cache[cacheKey] = cacheEntry;
@@ -1113,7 +1193,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1113
1193
  });
1114
1194
  } catch (parseError) {
1115
1195
  console.error(`[ModelDetector] Failed to parse models response: ${parseError.message}`);
1116
- resolve({
1196
+ resolveFailure({
1117
1197
  models: [],
1118
1198
  supported: true,
1119
1199
  cached: false,
@@ -1133,7 +1213,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1133
1213
  errorMessage = 'Cloudflare 防护拦截,无法自动获取模型列表';
1134
1214
  errorHint = '该 API 端点受 Cloudflare 保护,请手动填写模型名称';
1135
1215
  console.warn(`[ModelDetector] Cloudflare protection detected for ${channel.name}, no fallback models injected`);
1136
- resolve({
1216
+ resolveFailure({
1137
1217
  models: [],
1138
1218
  supported: false,
1139
1219
  cached: false,
@@ -1146,7 +1226,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1146
1226
  errorMessage = 'API 密钥认证失败';
1147
1227
  errorHint = '请检查 API 密钥是否正确配置';
1148
1228
  console.error(`[ModelDetector] Authentication failed for ${channel.name}: ${res.statusCode} - ${errorMessage}`);
1149
- resolve({
1229
+ resolveFailure({
1150
1230
  models: [],
1151
1231
  supported: true,
1152
1232
  cached: false,
@@ -1159,7 +1239,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1159
1239
  errorMessage = '访问被拒绝';
1160
1240
  errorHint = '请检查 API 密钥权限或联系服务提供商';
1161
1241
  console.error(`[ModelDetector] Access denied for ${channel.name}: ${res.statusCode} - ${errorMessage}`);
1162
- resolve({
1242
+ resolveFailure({
1163
1243
  models: [],
1164
1244
  supported: true,
1165
1245
  cached: false,
@@ -1171,7 +1251,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1171
1251
  }
1172
1252
  } else if (res.statusCode === 404) {
1173
1253
  console.warn(`[ModelDetector] Model list endpoint not found for ${channel.name}`);
1174
- resolve({
1254
+ resolveFailure({
1175
1255
  models: [],
1176
1256
  supported: false,
1177
1257
  cached: false,
@@ -1182,7 +1262,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1182
1262
  });
1183
1263
  } else if (res.statusCode === 429) {
1184
1264
  console.warn(`[ModelDetector] Rate limited for ${channel.name}`);
1185
- resolve({
1265
+ resolveFailure({
1186
1266
  models: [],
1187
1267
  supported: true,
1188
1268
  cached: false,
@@ -1206,7 +1286,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1206
1286
  })
1207
1287
  .catch((error) => {
1208
1288
  console.error(`[ModelDetector] Failed to read models response: ${error.message}`);
1209
- resolve({
1289
+ resolveFailure({
1210
1290
  models: [],
1211
1291
  supported: true,
1212
1292
  cached: false,
@@ -1218,7 +1298,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1218
1298
 
1219
1299
  req.on('error', (error) => {
1220
1300
  console.error(`[ModelDetector] Network error fetching models from ${channel.name}: ${error.message}`);
1221
- resolve({
1301
+ resolveFailure({
1222
1302
  models: [],
1223
1303
  supported: true,
1224
1304
  cached: false,
@@ -1230,7 +1310,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1230
1310
  req.on('timeout', () => {
1231
1311
  req.destroy();
1232
1312
  console.error(`[ModelDetector] Timeout fetching models from ${channel.name}`);
1233
- resolve({
1313
+ resolveFailure({
1234
1314
  models: [],
1235
1315
  supported: true,
1236
1316
  cached: false,
@@ -1243,7 +1323,7 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1243
1323
 
1244
1324
  } catch (error) {
1245
1325
  console.error(`[ModelDetector] Error in fetchModelsFromProvider: ${error.message}`);
1246
- resolve({
1326
+ resolveFailure({
1247
1327
  models: [],
1248
1328
  supported: true,
1249
1329
  cached: false,
@@ -229,6 +229,28 @@ function readTextFile(filePath, fallback = '') {
229
229
  }
230
230
 
231
231
  function getOmpStatus(env = process.env, options = {}) {
232
+ if (options.resolveRuntime === false) {
233
+ const paths = getOmpPaths(env, options);
234
+ return {
235
+ installed: fs.existsSync(paths.agentDir),
236
+ runtime: 'omp',
237
+ command: getOmpCommand(env),
238
+ commandSource: 'not-probed',
239
+ agentDir: paths.agentDir,
240
+ settingsPath: paths.settings,
241
+ authPath: paths.auth,
242
+ modelsPath: paths.modelsYml,
243
+ modelsYmlPath: paths.modelsYml,
244
+ modelsJsonLegacyPath: paths.modelsJsonLegacy,
245
+ sessionsDir: paths.sessions,
246
+ skillsDir: paths.skills,
247
+ promptsDir: paths.prompts,
248
+ commandsDir: paths.commands,
249
+ extensionsDir: paths.extensions,
250
+ themesDir: paths.themes,
251
+ packagesDir: paths.packages
252
+ };
253
+ }
232
254
  const runtime = resolveOmpRuntime(env, options);
233
255
  const paths = getOmpPaths(env, { ...options, runtime });
234
256
  return {
@@ -20,8 +20,11 @@ const THINKING_MODES = new Set([
20
20
 
21
21
  let lastManagedOmpSyncResult = null;
22
22
 
23
- function getOmpPaths(...args) {
24
- return ompConfig.getOmpPaths(...args);
23
+ function getOmpPaths(env = process.env, options = {}) {
24
+ // Channel CRUD only needs the native OMP file layout. Do not start OMP just
25
+ // to discover a path: OMP_CONFIG_DIR, OMP_PROFILE and OMP_CODING_AGENT_DIR
26
+ // are sufficient to derive it, and a blocked CLI must not block a save.
27
+ return ompConfig.getOmpPaths(env, { ...options, resolveRuntime: false });
25
28
  }
26
29
 
27
30
  function ensureOmpDir(...args) {
@@ -257,7 +260,8 @@ function readOmpCatalogModels(command, providerId, options = {}) {
257
260
  ...process.env,
258
261
  ...(options.env || {})
259
262
  },
260
- timeout: options.catalogTimeout || 5000
263
+ timeout: options.catalogTimeout || 5000,
264
+ windowsHide: true
261
265
  });
262
266
  const status = result?.status === undefined || result?.status === null ? 0 : result.status;
263
267
  if (result?.error || status !== 0) return [];
@@ -270,6 +274,9 @@ function readOmpCatalogModels(command, providerId, options = {}) {
270
274
  }
271
275
 
272
276
  function buildCatalogIndex(channel = {}, options = {}) {
277
+ if (options.catalogFromCli !== true) {
278
+ return new Map();
279
+ }
273
280
  const runtime = options.runtime
274
281
  || (typeof ompConfig.resolveOmpRuntime === 'function'
275
282
  ? ompConfig.resolveOmpRuntime(options.env || process.env, options.runtimeOptions || {})
@@ -499,7 +506,8 @@ function runModelsJson(command, env, options = {}) {
499
506
  ...process.env,
500
507
  ...env
501
508
  },
502
- timeout: options.timeout || 5000
509
+ timeout: options.timeout || 5000,
510
+ windowsHide: true
503
511
  });
504
512
  if (typeof result === 'string' || Buffer.isBuffer(result)) {
505
513
  return { status: 0, stdout: result, stderr: '' };
@@ -515,7 +523,8 @@ function collectVisibleProviderIds(command, env, options = {}) {
515
523
  ...process.env,
516
524
  ...env
517
525
  },
518
- timeout: options.visibilityTimeout || options.timeout || 5000
526
+ timeout: options.visibilityTimeout || options.timeout || 5000,
527
+ windowsHide: true
519
528
  });
520
529
  const status = result?.status === undefined || result?.status === null ? 0 : result.status;
521
530
  if (result?.error || status !== 0) return [];
@@ -658,15 +667,17 @@ function collectManagedVisibility(channels = [], modelsConfig = { providers: {}
658
667
  }
659
668
  });
660
669
 
661
- const env = options.env || process.env;
662
- const runtime = options.runtime
663
- || (typeof ompConfig.resolveOmpRuntime === 'function'
664
- ? ompConfig.resolveOmpRuntime(env, options.runtimeOptions || {})
665
- : null);
666
- if (runtime && runtime.runtime === 'omp' && runtime.installed && options.discoverDisabledProviders !== false) {
667
- collectVisibleProviderIds(runtime.command, env, options)
668
- .filter(providerId => providerId && !isManagedProviderId(providerId))
669
- .forEach(providerId => pushUnique(managedDisabledProviders, providerId));
670
+ if (options.discoverDisabledProviders === true) {
671
+ const env = options.env || process.env;
672
+ const runtime = options.runtime
673
+ || (typeof ompConfig.resolveOmpRuntime === 'function'
674
+ ? ompConfig.resolveOmpRuntime(env, options.runtimeOptions || {})
675
+ : null);
676
+ if (runtime && runtime.runtime === 'omp' && runtime.installed) {
677
+ collectVisibleProviderIds(runtime.command, env, options)
678
+ .filter(providerId => providerId && !isManagedProviderId(providerId))
679
+ .forEach(providerId => pushUnique(managedDisabledProviders, providerId));
680
+ }
670
681
  }
671
682
 
672
683
  return {
@@ -874,7 +885,9 @@ function writeManagedOmpProviders(channels = [], options = {}) {
874
885
  const backupPath = createModelsBackupIfNeeded(paths.modelsYml, options);
875
886
  writeModelsConfig(config, paths.modelsYml);
876
887
  const visibility = syncManagedOmpVisibility(channels, config, options);
877
- const validation = validateOmpModelsConfig(options);
888
+ const validation = options.validateWithCli === true
889
+ ? validateOmpModelsConfig(options)
890
+ : { skipped: true, reason: 'cli-validation-disabled', warnings: [] };
878
891
  const warnings = [
879
892
  ...(visibility.warnings || []),
880
893
  ...(validation.warnings || [])
@@ -916,7 +929,9 @@ function removeManagedOmpProviders(options = {}) {
916
929
  if (before !== after) {
917
930
  backupPath = createModelsBackupIfNeeded(paths.modelsYml, options);
918
931
  writeModelsConfig(config, paths.modelsYml);
919
- validation = validateOmpModelsConfig(options);
932
+ validation = options.validateWithCli === true
933
+ ? validateOmpModelsConfig(options)
934
+ : { skipped: true, reason: 'cli-validation-disabled', warnings: [] };
920
935
  }
921
936
  recordManagedOmpSyncResult({
922
937
  path: paths.modelsYml,