coding-tool-x 3.7.0 → 3.7.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.
Files changed (72) hide show
  1. package/README.md +5 -6
  2. package/dist/web/assets/{Analytics-Dma_j2Rq.js → Analytics-Dpje9L84.js} +1 -1
  3. package/dist/web/assets/{ConfigTemplates-C49Y1PLH.js → ConfigTemplates-Cz6kasek.js} +1 -1
  4. package/dist/web/assets/Home-BwTaYu3q.js +1 -0
  5. package/dist/web/assets/Home-Jom_ZcdY.css +1 -0
  6. package/dist/web/assets/{PluginManager-B1ECTHyY.js → PluginManager-DsN-xNXx.js} +1 -1
  7. package/dist/web/assets/ProjectList-4BCObimU.js +1 -0
  8. package/dist/web/assets/{ProjectList-B_L55-l9.css → ProjectList-D3a0oRum.css} +1 -1
  9. package/dist/web/assets/{SessionList-CH5GwwiJ.js → SessionList-D4c8FRzq.js} +1 -1
  10. package/dist/web/assets/{SkillManager-CRyVhjYQ.js → SkillManager-C-KYbXB5.js} +1 -1
  11. package/dist/web/assets/{WorkspaceManager-CO9tclVt.js → WorkspaceManager-B-BlPJHX.js} +1 -1
  12. package/dist/web/assets/index-Btfvh0GG.js +2 -0
  13. package/dist/web/assets/{index-CM26UrnJ.css → index-CLNa6DhH.css} +1 -1
  14. package/dist/web/index.html +2 -2
  15. package/package.json +2 -1
  16. package/src/commands/channels.js +8 -8
  17. package/src/commands/daemon.js +4 -4
  18. package/src/commands/doctor.js +1 -1
  19. package/src/commands/logs.js +1 -3
  20. package/src/commands/port-config.js +4 -4
  21. package/src/commands/proxy-control.js +13 -13
  22. package/src/commands/stats.js +6 -6
  23. package/src/commands/toggle-proxy.js +8 -8
  24. package/src/config/default.js +3 -3
  25. package/src/config/paths.js +46 -49
  26. package/src/index.js +4 -5
  27. package/src/server/api/commands.js +1 -1
  28. package/src/server/api/config-export.js +2 -2
  29. package/src/server/api/config-registry.js +1 -1
  30. package/src/server/api/config.js +1 -1
  31. package/src/server/api/dashboard.js +16 -16
  32. package/src/server/api/mcp.js +2 -2
  33. package/src/server/api/{pi-channels.js → omp-channels.js} +15 -15
  34. package/src/server/api/{pi-config.js → omp-config.js} +13 -13
  35. package/src/server/api/{pi-projects.js → omp-projects.js} +5 -5
  36. package/src/server/api/{pi-proxy.js → omp-proxy.js} +18 -18
  37. package/src/server/api/{pi-sessions.js → omp-sessions.js} +21 -21
  38. package/src/server/api/{pi-statistics.js → omp-statistics.js} +1 -1
  39. package/src/server/api/plugins.js +1 -1
  40. package/src/server/api/skills.js +1 -1
  41. package/src/server/api/workspaces.js +1 -1
  42. package/src/server/index.js +10 -10
  43. package/src/server/{pi-proxy-server.js → omp-proxy-server.js} +13 -13
  44. package/src/server/services/channel-balance.js +3 -3
  45. package/src/server/services/channel-scheduler.js +4 -4
  46. package/src/server/services/commands-service.js +5 -5
  47. package/src/server/services/config-export-service.js +53 -53
  48. package/src/server/services/config-registry-service.js +8 -8
  49. package/src/server/services/config-sync-manager.js +26 -26
  50. package/src/server/services/config-templates-service.js +28 -28
  51. package/src/server/services/mcp-service.js +20 -20
  52. package/src/server/services/notification-hooks.js +138 -6
  53. package/src/server/services/{pi-auth-providers.js → omp-auth-providers.js} +2 -2
  54. package/src/server/services/{pi-channels.js → omp-channels.js} +5 -5
  55. package/src/server/services/{pi-config.js → omp-config.js} +47 -65
  56. package/src/server/services/{pi-sessions.js → omp-sessions.js} +20 -20
  57. package/src/server/services/{pi-settings-manager.js → omp-settings-manager.js} +422 -18
  58. package/src/server/services/{pi-statistics-service.js → omp-statistics-service.js} +2 -2
  59. package/src/server/services/plugins-service.js +122 -122
  60. package/src/server/services/prompts-service.js +20 -20
  61. package/src/server/services/proxy-log-helper.js +1 -1
  62. package/src/server/services/proxy-runtime.js +1 -1
  63. package/src/server/services/skill-service.js +7 -7
  64. package/src/server/services/speed-test.js +29 -29
  65. package/src/server/services/ui-config.js +3 -3
  66. package/src/server/services/workspace-service.js +8 -8
  67. package/src/server/websocket-server.js +4 -4
  68. package/src/shared/platforms.js +201 -0
  69. package/dist/web/assets/Home-DGGC8H4g.js +0 -1
  70. package/dist/web/assets/Home-ewgeIhF7.css +0 -1
  71. package/dist/web/assets/ProjectList-DyiGyT-H.js +0 -1
  72. package/dist/web/assets/index-EQq56pRa.js +0 -2
@@ -2,10 +2,13 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const { spawnSync } = require('child_process');
4
4
  const yaml = require('js-yaml');
5
- const piConfig = require('./pi-config');
5
+ const ompConfig = require('./omp-config');
6
6
 
7
7
  const MANAGED_PROVIDER_PREFIX = 'ctx-';
8
8
  const MODELS_BACKUP_MARKER = '.ctx-backup-';
9
+ const VISIBILITY_STATE_VERSION = 1;
10
+ const MANAGED_VISIBILITY_STATE_FILE = 'coding-tool-x-omp-managed-visibility.json';
11
+ const NO_MANAGED_MODELS_SELECTOR = `${MANAGED_PROVIDER_PREFIX}coding-tool-x/__no_models_configured__`;
9
12
  const THINKING_EFFORT_LEVELS = ['minimal', 'low', 'medium', 'high', 'xhigh'];
10
13
  const THINKING_MODES = new Set([
11
14
  'effort',
@@ -17,12 +20,12 @@ const THINKING_MODES = new Set([
17
20
 
18
21
  let lastManagedOmpSyncResult = null;
19
22
 
20
- function getPiPaths(...args) {
21
- return piConfig.getPiPaths(...args);
23
+ function getOmpPaths(...args) {
24
+ return ompConfig.getOmpPaths(...args);
22
25
  }
23
26
 
24
- function ensurePiDir(...args) {
25
- return piConfig.ensurePiDir(...args);
27
+ function ensureOmpDir(...args) {
28
+ return ompConfig.ensureOmpDir(...args);
26
29
  }
27
30
 
28
31
  function normalizeProviderId(value = '') {
@@ -144,6 +147,41 @@ function compactObject(value = {}) {
144
147
  return next;
145
148
  }
146
149
 
150
+ function uniqueStrings(values = []) {
151
+ const seen = new Set();
152
+ const result = [];
153
+ (Array.isArray(values) ? values : []).forEach((value) => {
154
+ const item = String(value || '').trim();
155
+ if (!item || seen.has(item)) return;
156
+ seen.add(item);
157
+ result.push(item);
158
+ });
159
+ return result;
160
+ }
161
+
162
+ function pushUnique(values, value) {
163
+ const item = String(value || '').trim();
164
+ if (item && !values.includes(item)) {
165
+ values.push(item);
166
+ }
167
+ }
168
+
169
+ function sameStringSet(left = [], right = []) {
170
+ const leftSet = new Set(uniqueStrings(left));
171
+ const rightSet = new Set(uniqueStrings(right));
172
+ if (leftSet.size !== rightSet.size) return false;
173
+ for (const item of leftSet) {
174
+ if (!rightSet.has(item)) return false;
175
+ }
176
+ return true;
177
+ }
178
+
179
+ function getOwnProperty(object, key) {
180
+ return object && typeof object === 'object' && !Array.isArray(object)
181
+ ? Object.prototype.hasOwnProperty.call(object, key)
182
+ : false;
183
+ }
184
+
147
185
  function mergeDefined(base = {}, override = {}) {
148
186
  const next = { ...base };
149
187
  Object.entries(override || {}).forEach(([key, value]) => {
@@ -233,8 +271,8 @@ function readOmpCatalogModels(command, providerId, options = {}) {
233
271
 
234
272
  function buildCatalogIndex(channel = {}, options = {}) {
235
273
  const runtime = options.runtime
236
- || (typeof piConfig.resolvePiRuntime === 'function'
237
- ? piConfig.resolvePiRuntime(options.env || process.env, options.runtimeOptions || {})
274
+ || (typeof ompConfig.resolveOmpRuntime === 'function'
275
+ ? ompConfig.resolveOmpRuntime(options.env || process.env, options.runtimeOptions || {})
238
276
  : null);
239
277
  if (!runtime || runtime.runtime !== 'omp' || !runtime.installed) {
240
278
  return new Map();
@@ -278,7 +316,7 @@ function buildProviderEntry(channel = {}, options = {}) {
278
316
  return entry;
279
317
  }
280
318
 
281
- function readModelsConfig(filePath = getPiPaths().modelsYml) {
319
+ function readModelsConfig(filePath = getOmpPaths().modelsYml) {
282
320
  try {
283
321
  if (!fs.existsSync(filePath)) {
284
322
  return { providers: {} };
@@ -300,8 +338,8 @@ function readModelsConfig(filePath = getPiPaths().modelsYml) {
300
338
  }
301
339
  }
302
340
 
303
- function writeModelsConfig(config, filePath = getPiPaths().modelsYml) {
304
- ensurePiDir(path.dirname(filePath));
341
+ function writeModelsConfig(config, filePath = getOmpPaths().modelsYml) {
342
+ ensureOmpDir(path.dirname(filePath));
305
343
  const doc = yaml.dump(config || { providers: {} }, {
306
344
  lineWidth: 120,
307
345
  noRefs: true,
@@ -310,6 +348,44 @@ function writeModelsConfig(config, filePath = getPiPaths().modelsYml) {
310
348
  fs.writeFileSync(filePath, doc, 'utf8');
311
349
  }
312
350
 
351
+ function getOmpSettingsPath(paths = getOmpPaths()) {
352
+ if (paths.settings || paths.config) {
353
+ return paths.settings || paths.config;
354
+ }
355
+ const baseDir = paths.agentDir
356
+ || path.dirname(paths.modelsYml || paths.models || process.cwd());
357
+ return path.join(baseDir, 'config.yml');
358
+ }
359
+
360
+ function readOmpSettingsConfig(filePath = getOmpSettingsPath()) {
361
+ try {
362
+ if (!filePath || !fs.existsSync(filePath)) {
363
+ return {};
364
+ }
365
+ const raw = fs.readFileSync(filePath, 'utf8');
366
+ if (!raw.trim()) {
367
+ return {};
368
+ }
369
+ const parsed = yaml.load(raw);
370
+ if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
371
+ return {};
372
+ }
373
+ return parsed;
374
+ } catch (error) {
375
+ throw new Error(`Failed to read OMP config.yml: ${error.message}`);
376
+ }
377
+ }
378
+
379
+ function writeOmpSettingsConfig(config, filePath = getOmpSettingsPath()) {
380
+ ensureOmpDir(path.dirname(filePath));
381
+ const doc = yaml.dump(config || {}, {
382
+ lineWidth: 120,
383
+ noRefs: true,
384
+ sortKeys: false
385
+ });
386
+ fs.writeFileSync(filePath, doc, 'utf8');
387
+ }
388
+
313
389
  function getModelsBackupPrefix(filePath) {
314
390
  return `${path.basename(filePath)}${MODELS_BACKUP_MARKER}`;
315
391
  }
@@ -343,6 +419,44 @@ function createModelsBackupIfNeeded(filePath, options = {}) {
343
419
  return backupPath;
344
420
  }
345
421
 
422
+ function getManagedVisibilityStatePath(paths = getOmpPaths()) {
423
+ if (paths.managedVisibilityState) {
424
+ return paths.managedVisibilityState;
425
+ }
426
+ const baseDir = paths.agentDir
427
+ || path.dirname(paths.settings || paths.config || paths.modelsYml || paths.models || process.cwd());
428
+ return path.join(baseDir, MANAGED_VISIBILITY_STATE_FILE);
429
+ }
430
+
431
+ function readManagedVisibilityState(paths = getOmpPaths()) {
432
+ const filePath = getManagedVisibilityStatePath(paths);
433
+ try {
434
+ if (!fs.existsSync(filePath)) return null;
435
+ const raw = fs.readFileSync(filePath, 'utf8');
436
+ if (!raw.trim()) return null;
437
+ const parsed = JSON.parse(raw);
438
+ return parsed && typeof parsed === 'object' && !Array.isArray(parsed) ? parsed : null;
439
+ } catch {
440
+ return null;
441
+ }
442
+ }
443
+
444
+ function writeManagedVisibilityState(state, paths = getOmpPaths()) {
445
+ const filePath = getManagedVisibilityStatePath(paths);
446
+ ensureOmpDir(path.dirname(filePath));
447
+ fs.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
448
+ return filePath;
449
+ }
450
+
451
+ function removeManagedVisibilityState(paths = getOmpPaths()) {
452
+ const filePath = getManagedVisibilityStatePath(paths);
453
+ if (fs.existsSync(filePath)) {
454
+ fs.unlinkSync(filePath);
455
+ return true;
456
+ }
457
+ return false;
458
+ }
459
+
346
460
  function normalizeCommandOutput(value) {
347
461
  if (value === undefined || value === null) return '';
348
462
  return Buffer.isBuffer(value) ? value.toString('utf8') : String(value);
@@ -393,11 +507,32 @@ function runModelsJson(command, env, options = {}) {
393
507
  return result || { status: 0, stdout: '', stderr: '' };
394
508
  }
395
509
 
510
+ function collectVisibleProviderIds(command, env, options = {}) {
511
+ const runner = options.visibilityModelsRunner || options.modelsRunner || spawnSync;
512
+ const result = runner(command, ['models', '--json'], {
513
+ encoding: 'utf8',
514
+ env: {
515
+ ...process.env,
516
+ ...env
517
+ },
518
+ timeout: options.visibilityTimeout || options.timeout || 5000
519
+ });
520
+ const status = result?.status === undefined || result?.status === null ? 0 : result.status;
521
+ if (result?.error || status !== 0) return [];
522
+ try {
523
+ const parsed = JSON.parse(normalizeCommandOutput(result?.stdout).trim() || '{}');
524
+ const models = Array.isArray(parsed.models) ? parsed.models : [];
525
+ return uniqueStrings(models.map(model => model?.provider));
526
+ } catch {
527
+ return [];
528
+ }
529
+ }
530
+
396
531
  function validateOmpModelsConfig(options = {}) {
397
532
  const env = options.env || process.env;
398
533
  const runtime = options.runtime
399
- || (typeof piConfig.resolvePiRuntime === 'function'
400
- ? piConfig.resolvePiRuntime(env, options.runtimeOptions || {})
534
+ || (typeof ompConfig.resolveOmpRuntime === 'function'
535
+ ? ompConfig.resolveOmpRuntime(env, options.runtimeOptions || {})
401
536
  : null);
402
537
 
403
538
  if (!runtime || runtime.runtime !== 'omp' || !runtime.installed) {
@@ -455,7 +590,7 @@ function getLastManagedOmpSyncResult() {
455
590
  : null;
456
591
  }
457
592
 
458
- function removeLegacyManagedExtension(paths = getPiPaths()) {
593
+ function removeLegacyManagedExtension(paths = getOmpPaths()) {
459
594
  const target = paths.managedProviderExtension;
460
595
  if (target && fs.existsSync(target)) {
461
596
  fs.unlinkSync(target);
@@ -488,27 +623,287 @@ function buildManagedModelsConfig(channels = [], baseConfig = readModelsConfig()
488
623
  return next;
489
624
  }
490
625
 
626
+ function getOriginalProviderId(channel = {}) {
627
+ const providerId = normalizeProviderId(channel.providerKey || channel.provider || channel.name || channel.id);
628
+ return isManagedProviderId(providerId)
629
+ ? providerId.slice(MANAGED_PROVIDER_PREFIX.length)
630
+ : providerId;
631
+ }
632
+
633
+ function collectManagedVisibility(channels = [], modelsConfig = { providers: {} }, options = {}) {
634
+ const managedEnabledModels = [];
635
+ const managedDisabledProviders = [];
636
+ const warnings = [];
637
+ const providers = modelsConfig.providers || {};
638
+
639
+ (channels || [])
640
+ .filter(channel => channel && channel.enabled !== false && channel.baseUrl)
641
+ .forEach((channel) => {
642
+ const managedProviderId = getManagedProviderId(channel);
643
+ const providerEntry = providers[managedProviderId] || {};
644
+ const models = Array.isArray(providerEntry.models) ? providerEntry.models : [];
645
+ models.forEach((model) => {
646
+ const modelId = String(model?.id || model?.name || '').trim();
647
+ if (modelId) {
648
+ pushUnique(managedEnabledModels, `${managedProviderId}/${modelId}`);
649
+ }
650
+ });
651
+
652
+ const originalProviderId = getOriginalProviderId(channel);
653
+ if (originalProviderId && !isManagedProviderId(originalProviderId)) {
654
+ pushUnique(managedDisabledProviders, originalProviderId);
655
+ }
656
+ if (models.length === 0) {
657
+ warnings.push(`OMP channel "${channel.name || channel.id || managedProviderId}" has no configured models; OMP model scope will resolve to no usable managed models.`);
658
+ }
659
+ });
660
+
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
+ }
671
+
672
+ return {
673
+ managedEnabledModels,
674
+ managedEnabledModelsForSettings: managedEnabledModels.length > 0
675
+ ? managedEnabledModels
676
+ : [NO_MANAGED_MODELS_SELECTOR],
677
+ managedDisabledProviders,
678
+ managedDefaultModel: managedEnabledModels[0] || null,
679
+ warnings
680
+ };
681
+ }
682
+
683
+ function captureOriginalVisibility(settings = {}) {
684
+ const modelRoles = settings.modelRoles && typeof settings.modelRoles === 'object' && !Array.isArray(settings.modelRoles)
685
+ ? settings.modelRoles
686
+ : {};
687
+ return {
688
+ enabledModels: getOwnProperty(settings, 'enabledModels') ? uniqueStrings(settings.enabledModels) : null,
689
+ disabledProviders: getOwnProperty(settings, 'disabledProviders') ? uniqueStrings(settings.disabledProviders) : null,
690
+ modelRolesHadDefault: getOwnProperty(modelRoles, 'default'),
691
+ modelRolesDefault: getOwnProperty(modelRoles, 'default') ? String(modelRoles.default || '') : null
692
+ };
693
+ }
694
+
695
+ function collectUserDisabledProviders(settings = {}, previousState = null) {
696
+ const currentDisabled = uniqueStrings(settings.disabledProviders);
697
+ if (!previousState?.active || !previousState.original) {
698
+ return currentDisabled;
699
+ }
700
+ const originalDisabled = Array.isArray(previousState.original.disabledProviders)
701
+ ? previousState.original.disabledProviders
702
+ : [];
703
+ const previousManagedDisabled = uniqueStrings(previousState.managedDisabledProviders);
704
+ const userAddedDuringManagedMode = currentDisabled
705
+ .filter(providerId => !previousManagedDisabled.includes(providerId) && !originalDisabled.includes(providerId));
706
+ return uniqueStrings([...originalDisabled, ...userAddedDuringManagedMode]);
707
+ }
708
+
709
+ function applyManagedModelRole(settings, visibility, previousState = null) {
710
+ const roles = settings.modelRoles && typeof settings.modelRoles === 'object' && !Array.isArray(settings.modelRoles)
711
+ ? { ...settings.modelRoles }
712
+ : {};
713
+ const currentDefault = String(roles.default || '').trim();
714
+ const previousManagedDefault = String(previousState?.managedDefaultModel || '').trim();
715
+ if (!visibility.managedDefaultModel) {
716
+ return roles;
717
+ }
718
+ const defaultIsManaged = visibility.managedEnabledModels.includes(currentDefault);
719
+ if (!currentDefault || !defaultIsManaged || currentDefault === previousManagedDefault) {
720
+ roles.default = visibility.managedDefaultModel;
721
+ }
722
+ return roles;
723
+ }
724
+
725
+ function syncManagedOmpVisibility(channels = [], modelsConfig = { providers: {} }, options = {}) {
726
+ const paths = getOmpPaths();
727
+ const settingsPath = getOmpSettingsPath(paths);
728
+ const statePath = getManagedVisibilityStatePath(paths);
729
+ const settings = readOmpSettingsConfig(settingsPath);
730
+ const previousState = readManagedVisibilityState(paths);
731
+ const visibility = collectManagedVisibility(channels, modelsConfig, options);
732
+ const original = previousState?.active && previousState.original
733
+ ? previousState.original
734
+ : captureOriginalVisibility(settings);
735
+ const before = JSON.stringify(settings);
736
+ const settingsBackupPath = createModelsBackupIfNeeded(settingsPath, options);
737
+
738
+ settings.enabledModels = visibility.managedEnabledModelsForSettings;
739
+ settings.disabledProviders = uniqueStrings([
740
+ ...collectUserDisabledProviders(settings, previousState),
741
+ ...visibility.managedDisabledProviders
742
+ ]);
743
+ settings.modelRoles = applyManagedModelRole(settings, visibility, previousState);
744
+
745
+ const after = JSON.stringify(settings);
746
+ if (before !== after) {
747
+ writeOmpSettingsConfig(settings, settingsPath);
748
+ }
749
+
750
+ const state = {
751
+ version: VISIBILITY_STATE_VERSION,
752
+ active: true,
753
+ settingsPath,
754
+ managedEnabledModels: visibility.managedEnabledModels,
755
+ managedEnabledModelsForSettings: visibility.managedEnabledModelsForSettings,
756
+ managedDisabledProviders: visibility.managedDisabledProviders,
757
+ managedDefaultModel: visibility.managedDefaultModel,
758
+ original,
759
+ updatedAt: new Date().toISOString()
760
+ };
761
+ writeManagedVisibilityState(state, paths);
762
+
763
+ return {
764
+ settingsPath,
765
+ statePath,
766
+ settingsBackupPath,
767
+ changed: before !== after,
768
+ managedEnabledModels: visibility.managedEnabledModels,
769
+ managedEnabledModelsForSettings: visibility.managedEnabledModelsForSettings,
770
+ managedDisabledProviders: visibility.managedDisabledProviders,
771
+ managedDefaultModel: visibility.managedDefaultModel,
772
+ warnings: visibility.warnings
773
+ };
774
+ }
775
+
776
+ function restoreOriginalList(settings, key, originalValue) {
777
+ if (Array.isArray(originalValue)) {
778
+ settings[key] = uniqueStrings(originalValue);
779
+ return;
780
+ }
781
+ delete settings[key];
782
+ }
783
+
784
+ function removeManagedOmpVisibility(options = {}) {
785
+ const paths = getOmpPaths();
786
+ const settingsPath = getOmpSettingsPath(paths);
787
+ const state = readManagedVisibilityState(paths);
788
+ const settings = readOmpSettingsConfig(settingsPath);
789
+ const before = JSON.stringify(settings);
790
+ const warnings = [];
791
+ let settingsBackupPath = null;
792
+
793
+ if (state?.active) {
794
+ settingsBackupPath = createModelsBackupIfNeeded(settingsPath, options);
795
+ const managedEnabled = uniqueStrings(state.managedEnabledModelsForSettings || state.managedEnabledModels);
796
+ const currentEnabled = uniqueStrings(settings.enabledModels);
797
+ if (sameStringSet(currentEnabled, managedEnabled)) {
798
+ restoreOriginalList(settings, 'enabledModels', state.original?.enabledModels);
799
+ } else if (managedEnabled.length > 0) {
800
+ const nextEnabled = currentEnabled.filter(model => !managedEnabled.includes(model));
801
+ if (nextEnabled.length > 0) {
802
+ settings.enabledModels = nextEnabled;
803
+ } else {
804
+ restoreOriginalList(settings, 'enabledModels', state.original?.enabledModels);
805
+ }
806
+ warnings.push('OMP enabledModels was changed while coding-tool-x managed visibility was active; preserved non-managed entries during cleanup.');
807
+ }
808
+
809
+ const managedDisabled = uniqueStrings(state.managedDisabledProviders);
810
+ const originalDisabled = Array.isArray(state.original?.disabledProviders)
811
+ ? state.original.disabledProviders
812
+ : [];
813
+ const currentDisabled = uniqueStrings(settings.disabledProviders);
814
+ const expectedDisabled = uniqueStrings([...originalDisabled, ...managedDisabled]);
815
+ if (sameStringSet(currentDisabled, expectedDisabled)) {
816
+ restoreOriginalList(settings, 'disabledProviders', state.original?.disabledProviders);
817
+ } else if (managedDisabled.length > 0) {
818
+ const nextDisabled = currentDisabled.filter(providerId => !managedDisabled.includes(providerId));
819
+ if (nextDisabled.length > 0) {
820
+ settings.disabledProviders = nextDisabled;
821
+ } else {
822
+ restoreOriginalList(settings, 'disabledProviders', state.original?.disabledProviders);
823
+ }
824
+ warnings.push('OMP disabledProviders was changed while coding-tool-x managed visibility was active; removed only managed provider entries.');
825
+ }
826
+
827
+ if (settings.modelRoles && typeof settings.modelRoles === 'object' && !Array.isArray(settings.modelRoles)) {
828
+ const roles = { ...settings.modelRoles };
829
+ if (roles.default === state.managedDefaultModel) {
830
+ if (state.original?.modelRolesHadDefault) {
831
+ roles.default = state.original.modelRolesDefault;
832
+ } else {
833
+ delete roles.default;
834
+ }
835
+ settings.modelRoles = roles;
836
+ }
837
+ if (Object.keys(settings.modelRoles).length === 0) {
838
+ delete settings.modelRoles;
839
+ }
840
+ }
841
+ } else {
842
+ const managedEnabled = uniqueStrings(settings.enabledModels)
843
+ .filter(model => isManagedProviderId(String(model).split('/')[0]));
844
+ if (managedEnabled.length > 0) {
845
+ settingsBackupPath = createModelsBackupIfNeeded(settingsPath, options);
846
+ settings.enabledModels = uniqueStrings(settings.enabledModels)
847
+ .filter(model => !managedEnabled.includes(model));
848
+ if (settings.enabledModels.length === 0) {
849
+ delete settings.enabledModels;
850
+ }
851
+ warnings.push('Removed stale coding-tool-x managed OMP enabledModels entries without a visibility state file.');
852
+ }
853
+ }
854
+
855
+ const after = JSON.stringify(settings);
856
+ if (before !== after) {
857
+ writeOmpSettingsConfig(settings, settingsPath);
858
+ }
859
+ const stateRemoved = removeManagedVisibilityState(paths);
860
+
861
+ return {
862
+ settingsPath,
863
+ statePath: getManagedVisibilityStatePath(paths),
864
+ settingsBackupPath,
865
+ changed: before !== after || stateRemoved,
866
+ stateRemoved,
867
+ warnings
868
+ };
869
+ }
870
+
491
871
  function writeManagedOmpProviders(channels = [], options = {}) {
492
- const paths = getPiPaths();
872
+ const paths = getOmpPaths();
493
873
  const config = buildManagedModelsConfig(channels, readModelsConfig(paths.modelsYml), options);
494
874
  const backupPath = createModelsBackupIfNeeded(paths.modelsYml, options);
495
875
  writeModelsConfig(config, paths.modelsYml);
876
+ const visibility = syncManagedOmpVisibility(channels, config, options);
496
877
  const validation = validateOmpModelsConfig(options);
878
+ const warnings = [
879
+ ...(visibility.warnings || []),
880
+ ...(validation.warnings || [])
881
+ ];
497
882
  recordManagedOmpSyncResult({
498
883
  path: paths.modelsYml,
499
884
  modelsPath: paths.modelsYml,
885
+ settingsPath: visibility.settingsPath,
886
+ statePath: visibility.statePath,
500
887
  backupPath,
888
+ modelsBackupPath: backupPath,
889
+ settingsBackupPath: visibility.settingsBackupPath,
501
890
  changed: true,
891
+ visibilityChanged: visibility.changed,
892
+ managedEnabledModels: visibility.managedEnabledModels,
893
+ managedEnabledModelsForSettings: visibility.managedEnabledModelsForSettings,
894
+ managedDisabledProviders: visibility.managedDisabledProviders,
895
+ managedDefaultModel: visibility.managedDefaultModel,
502
896
  validation,
503
- warnings: validation.warnings || []
897
+ warnings
504
898
  });
505
899
  removeLegacyManagedExtension(paths);
506
900
  return paths.modelsYml;
507
901
  }
508
902
 
509
903
  function removeManagedOmpProviders(options = {}) {
510
- const paths = getPiPaths();
904
+ const paths = getOmpPaths();
511
905
  const config = readModelsConfig(paths.modelsYml);
906
+ const visibility = removeManagedOmpVisibility(options);
512
907
  const before = Object.keys(config.providers || {}).length;
513
908
  pruneManagedProviders(config.providers);
514
909
  const after = Object.keys(config.providers || {}).length;
@@ -526,16 +921,25 @@ function removeManagedOmpProviders(options = {}) {
526
921
  recordManagedOmpSyncResult({
527
922
  path: paths.modelsYml,
528
923
  modelsPath: paths.modelsYml,
924
+ settingsPath: visibility.settingsPath,
925
+ statePath: visibility.statePath,
529
926
  backupPath,
927
+ modelsBackupPath: backupPath,
928
+ settingsBackupPath: visibility.settingsBackupPath,
530
929
  changed: before !== after,
930
+ visibilityChanged: visibility.changed,
931
+ stateRemoved: visibility.stateRemoved,
531
932
  validation,
532
- warnings: validation.warnings || []
933
+ warnings: [
934
+ ...(visibility.warnings || []),
935
+ ...(validation.warnings || [])
936
+ ]
533
937
  });
534
938
  removeLegacyManagedExtension(paths);
535
939
  }
536
940
 
537
941
  function isManagedOmpProvidersActive() {
538
- const paths = getPiPaths();
942
+ const paths = getOmpPaths();
539
943
  if (!fs.existsSync(paths.modelsYml)) {
540
944
  return false;
541
945
  }
@@ -6,7 +6,7 @@ const {
6
6
  } = require('./statistics-service');
7
7
  const { normalizeUsageTokens, toNumber } = require('./proxy-log-helper');
8
8
 
9
- const TOOL_TYPE = 'pi';
9
+ const TOOL_TYPE = 'omp';
10
10
 
11
11
  function initAggregateEntry(name = '') {
12
12
  return {
@@ -174,7 +174,7 @@ function addSessionUsage(entry, session) {
174
174
 
175
175
  function readSessionUsage() {
176
176
  try {
177
- const { getAllSessions } = require('./pi-sessions');
177
+ const { getAllSessions } = require('./omp-sessions');
178
178
  const sessions = getAllSessions();
179
179
  return Array.isArray(sessions) ? sessions : [];
180
180
  } catch {