coding-tool-x 3.7.10 → 3.7.11

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 (33) hide show
  1. package/dist/web/assets/{Analytics-CkUtnoz9.js → Analytics-Ck_00Brj.js} +1 -1
  2. package/dist/web/assets/{ConfigTemplates-BwPqhE-N.js → ConfigTemplates-Cpw2vUa8.js} +1 -1
  3. package/dist/web/assets/Home-Bft3Kztl.js +1 -0
  4. package/dist/web/assets/Home-CHZJkkjH.css +1 -0
  5. package/dist/web/assets/{PluginManager-JMXWl37b.js → PluginManager-2RlvEJCT.js} +1 -1
  6. package/dist/web/assets/{ProjectList-CZwDgVvt.js → ProjectList-_ecCngpl.js} +1 -1
  7. package/dist/web/assets/{SessionList-2fZmN7Au.js → SessionList-DlWcw3hO.js} +1 -1
  8. package/dist/web/assets/{SkillManager-Bhzhkeis.js → SkillManager-CnY7IXkX.js} +1 -1
  9. package/dist/web/assets/{WorkspaceManager-B3lhhBD0.js → WorkspaceManager-3mqghi09.js} +1 -1
  10. package/dist/web/assets/{index-C5nLTbOk.css → index-BceXgSSe.css} +1 -1
  11. package/dist/web/assets/index-k5oP7c-A.js +2 -0
  12. package/dist/web/index.html +2 -2
  13. package/package.json +1 -1
  14. package/src/commands/toggle-proxy.js +7 -8
  15. package/src/commands/ui.js +11 -1
  16. package/src/config/default.js +1 -1
  17. package/src/index.js +41 -15
  18. package/src/server/api/omp-channels.js +6 -4
  19. package/src/server/api/omp-config.js +1 -1
  20. package/src/server/api/omp-proxy.js +13 -42
  21. package/src/server/index.js +3 -3
  22. package/src/server/omp-proxy-server.js +129 -10
  23. package/src/server/services/channel-scheduler.js +59 -9
  24. package/src/server/services/omp-auth-gateway-client.js +62 -0
  25. package/src/server/services/omp-channels.js +109 -12
  26. package/src/server/services/omp-gateway-routing.js +295 -0
  27. package/src/server/services/omp-gateway.js +919 -0
  28. package/src/server/services/omp-session-log-observer.js +138 -0
  29. package/src/server/services/omp-sessions.js +80 -0
  30. package/src/server/services/omp-settings-manager.js +335 -86
  31. package/dist/web/assets/Home-BANl91zG.js +0 -1
  32. package/dist/web/assets/Home-CqnBXFxj.css +0 -1
  33. package/dist/web/assets/index-LYvv7wtx.js +0 -2
@@ -3,6 +3,7 @@ const path = require('path');
3
3
  const { spawnSync } = require('child_process');
4
4
  const yaml = require('js-yaml');
5
5
  const ompConfig = require('./omp-config');
6
+ const { prepareManagedOmpChannels } = require('./omp-gateway-routing');
6
7
  const { MODEL_METADATA, MODEL_ALIASES } = require('../../config/model-metadata');
7
8
  const {
8
9
  MODEL_METADATA_MODES,
@@ -15,7 +16,7 @@ const {
15
16
 
16
17
  const MANAGED_PROVIDER_PREFIX = 'ctx-';
17
18
  const MODELS_BACKUP_MARKER = '.ctx-backup-';
18
- const VISIBILITY_STATE_VERSION = 1;
19
+ const VISIBILITY_STATE_VERSION = 2;
19
20
  const MANAGED_VISIBILITY_STATE_FILE = 'coding-tool-x-omp-managed-visibility.json';
20
21
  const NO_MANAGED_MODELS_SELECTOR = `${MANAGED_PROVIDER_PREFIX}coding-tool-x/__no_models_configured__`;
21
22
  let lastManagedOmpSyncResult = null;
@@ -45,6 +46,12 @@ function normalizeProviderId(value = '') {
45
46
  }
46
47
 
47
48
  function getManagedProviderId(channel = {}) {
49
+ const explicitId = normalizeProviderId(channel.managedProviderId || '');
50
+ if (channel.managedProviderId) {
51
+ return explicitId.startsWith(MANAGED_PROVIDER_PREFIX)
52
+ ? explicitId
53
+ : `${MANAGED_PROVIDER_PREFIX}${explicitId}`;
54
+ }
48
55
  const baseId = normalizeProviderId(channel.providerKey || channel.provider || channel.name || channel.id);
49
56
  return baseId.startsWith(MANAGED_PROVIDER_PREFIX) ? baseId : `${MANAGED_PROVIDER_PREFIX}${baseId}`;
50
57
  }
@@ -116,6 +123,49 @@ function getOwnProperty(object, key) {
116
123
  : false;
117
124
  }
118
125
 
126
+ function cloneJson(value) {
127
+ if (value === undefined) return undefined;
128
+ return JSON.parse(JSON.stringify(value));
129
+ }
130
+
131
+ function captureFileSnapshots(filePaths = []) {
132
+ return filePaths.map((filePath) => ({
133
+ filePath,
134
+ existed: Boolean(filePath && fs.existsSync(filePath)),
135
+ content: filePath && fs.existsSync(filePath) ? fs.readFileSync(filePath) : null
136
+ }));
137
+ }
138
+
139
+ function restoreFileSnapshots(snapshots = []) {
140
+ snapshots.forEach((snapshot) => {
141
+ if (!snapshot.filePath) return;
142
+ if (snapshot.existed) {
143
+ ensureOmpDir(path.dirname(snapshot.filePath));
144
+ writeFileAtomic(snapshot.filePath, snapshot.content);
145
+ } else if (fs.existsSync(snapshot.filePath)) {
146
+ fs.unlinkSync(snapshot.filePath);
147
+ }
148
+ });
149
+ }
150
+
151
+ function writeFileAtomic(filePath, content) {
152
+ ensureOmpDir(path.dirname(filePath));
153
+ const temporaryPath = `${filePath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
154
+ const mode = fs.existsSync(filePath)
155
+ ? fs.statSync(filePath).mode & 0o777
156
+ : 0o600;
157
+ try {
158
+ fs.writeFileSync(temporaryPath, content, { mode });
159
+ fs.chmodSync(temporaryPath, mode);
160
+ fs.renameSync(temporaryPath, filePath);
161
+ } catch (error) {
162
+ if (fs.existsSync(temporaryPath)) {
163
+ fs.unlinkSync(temporaryPath);
164
+ }
165
+ throw error;
166
+ }
167
+ }
168
+
119
169
  function normalizeModels(channel = {}, options = {}) {
120
170
  const models = [];
121
171
  const catalogIndex = options.catalogIndex instanceof Map ? options.catalogIndex : new Map();
@@ -277,13 +327,32 @@ function buildProviderEntry(channel = {}, options = {}) {
277
327
  const providerName = channel.providerKey || channel.provider || channel.name || channel.id || 'provider';
278
328
  options.warnings.push(...normalizedProvider.warnings.map(message => `${providerName}: ${message}`));
279
329
  }
330
+ let models = normalizeModels(channel, { catalogIndex, warnings: options.warnings });
331
+ if (channel._ompGatewayRoute) {
332
+ models = models.map((model) => {
333
+ const sanitized = { ...model };
334
+ delete sanitized.baseUrl;
335
+ delete sanitized.headers;
336
+ return sanitized;
337
+ });
338
+ }
280
339
  const entry = compactObject({
281
340
  ...normalizedProvider.config,
282
341
  baseUrl: String(channel.baseUrl || providerConfig.baseUrl || '').trim(),
283
342
  api: normalizeProviderApi(channel.providerApi || channel.api || channel.wireApi || providerConfig.api),
284
- models: normalizeModels(channel, { catalogIndex, warnings: options.warnings })
343
+ models
285
344
  });
286
345
 
346
+ if (channel._ompGatewayRoute) {
347
+ delete entry.headers;
348
+ delete entry.apiKey;
349
+ if (channel.authMode === 'none') {
350
+ entry.auth = 'none';
351
+ } else {
352
+ delete entry.auth;
353
+ }
354
+ }
355
+
287
356
  const apiKey = String(channel.apiKey || providerConfig.apiKey || '').trim();
288
357
  if (apiKey) {
289
358
  entry.apiKey = apiKey;
@@ -321,7 +390,7 @@ function writeModelsConfig(config, filePath = getOmpPaths().modelsYml) {
321
390
  noRefs: true,
322
391
  sortKeys: false
323
392
  });
324
- fs.writeFileSync(filePath, doc, 'utf8');
393
+ writeFileAtomic(filePath, doc);
325
394
  }
326
395
 
327
396
  function getOmpSettingsPath(paths = getOmpPaths()) {
@@ -359,7 +428,7 @@ function writeOmpSettingsConfig(config, filePath = getOmpSettingsPath()) {
359
428
  noRefs: true,
360
429
  sortKeys: false
361
430
  });
362
- fs.writeFileSync(filePath, doc, 'utf8');
431
+ writeFileAtomic(filePath, doc);
363
432
  }
364
433
 
365
434
  function getModelsBackupPrefix(filePath) {
@@ -420,7 +489,7 @@ function readManagedVisibilityState(paths = getOmpPaths()) {
420
489
  function writeManagedVisibilityState(state, paths = getOmpPaths()) {
421
490
  const filePath = getManagedVisibilityStatePath(paths);
422
491
  ensureOmpDir(path.dirname(filePath));
423
- fs.writeFileSync(filePath, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
492
+ writeFileAtomic(filePath, `${JSON.stringify(state, null, 2)}\n`);
424
493
  return filePath;
425
494
  }
426
495
 
@@ -602,6 +671,9 @@ function buildManagedModelsConfig(channels = [], baseConfig = readModelsConfig()
602
671
  }
603
672
 
604
673
  function getOriginalProviderId(channel = {}) {
674
+ if (channel.originalProviderId) {
675
+ return normalizeProviderId(channel.originalProviderId);
676
+ }
605
677
  const providerId = normalizeProviderId(channel.providerKey || channel.provider || channel.name || channel.id);
606
678
  return isManagedProviderId(providerId)
607
679
  ? providerId.slice(MANAGED_PROVIDER_PREFIX.length)
@@ -679,11 +751,18 @@ function captureOriginalVisibility(settings = {}) {
679
751
  const modelRoles = settings.modelRoles && typeof settings.modelRoles === 'object' && !Array.isArray(settings.modelRoles)
680
752
  ? settings.modelRoles
681
753
  : {};
754
+ const fallbackChains = settings.retry && typeof settings.retry === 'object' && !Array.isArray(settings.retry)
755
+ ? settings.retry.fallbackChains
756
+ : undefined;
682
757
  return {
683
758
  enabledModels: getOwnProperty(settings, 'enabledModels') ? uniqueStrings(settings.enabledModels) : null,
684
759
  disabledProviders: getOwnProperty(settings, 'disabledProviders') ? uniqueStrings(settings.disabledProviders) : null,
685
760
  modelRolesHadDefault: getOwnProperty(modelRoles, 'default'),
686
- modelRolesDefault: getOwnProperty(modelRoles, 'default') ? String(modelRoles.default || '') : null
761
+ modelRolesDefault: getOwnProperty(modelRoles, 'default') ? String(modelRoles.default || '') : null,
762
+ modelRolesHadValue: getOwnProperty(settings, 'modelRoles'),
763
+ modelRoles: getOwnProperty(settings, 'modelRoles') ? cloneJson(modelRoles) : null,
764
+ retryFallbackChainsHadValue: fallbackChains !== undefined,
765
+ retryFallbackChains: fallbackChains !== undefined ? cloneJson(fallbackChains) : null
687
766
  };
688
767
  }
689
768
 
@@ -701,22 +780,86 @@ function collectUserDisabledProviders(settings = {}, previousState = null) {
701
780
  return uniqueStrings([...originalDisabled, ...userAddedDuringManagedMode]);
702
781
  }
703
782
 
704
- function applyManagedModelRole(settings, visibility, previousState = null) {
705
- const roles = settings.modelRoles && typeof settings.modelRoles === 'object' && !Array.isArray(settings.modelRoles)
706
- ? { ...settings.modelRoles }
707
- : {};
708
- const currentDefault = String(roles.default || '').trim();
709
- const previousManagedDefault = String(previousState?.managedDefaultModel || '').trim();
710
- if (!visibility.managedDefaultModel) {
711
- return roles;
783
+ function buildProviderMappings(channels = []) {
784
+ const originalToManaged = {};
785
+ const managedToOriginal = {};
786
+ channels.forEach((channel) => {
787
+ const original = getOriginalProviderId(channel);
788
+ const managed = getManagedProviderId(channel);
789
+ if (!original || !managed) return;
790
+ if (!originalToManaged[original]) {
791
+ originalToManaged[original] = managed;
792
+ }
793
+ managedToOriginal[managed] = original;
794
+ });
795
+ return { originalToManaged, managedToOriginal };
796
+ }
797
+
798
+ function rewriteModelSelector(value, providerMap = {}) {
799
+ if (typeof value !== 'string') return value;
800
+ const index = value.indexOf('/');
801
+ if (index <= 0) return value;
802
+ const providerId = value.slice(0, index);
803
+ const replacement = providerMap[providerId];
804
+ return replacement ? `${replacement}${value.slice(index)}` : value;
805
+ }
806
+
807
+ function rewriteSelectorTree(value, providerMap = {}) {
808
+ if (typeof value === 'string') return rewriteModelSelector(value, providerMap);
809
+ if (Array.isArray(value)) return value.map(item => rewriteSelectorTree(item, providerMap));
810
+ if (value && typeof value === 'object') {
811
+ return Object.fromEntries(
812
+ Object.entries(value).map(([key, item]) => [key, rewriteSelectorTree(item, providerMap)])
813
+ );
814
+ }
815
+ return value;
816
+ }
817
+
818
+ function removeManagedSelectorTree(value) {
819
+ if (typeof value === 'string') {
820
+ const providerId = String(value).split('/')[0];
821
+ return isManagedProviderId(providerId) ? undefined : value;
822
+ }
823
+ if (Array.isArray(value)) {
824
+ return value
825
+ .map(removeManagedSelectorTree)
826
+ .filter(item => item !== undefined);
827
+ }
828
+ if (value && typeof value === 'object') {
829
+ return Object.fromEntries(
830
+ Object.entries(value)
831
+ .map(([key, item]) => [key, removeManagedSelectorTree(item)])
832
+ .filter(([, item]) => item !== undefined)
833
+ );
712
834
  }
713
- const defaultIsManaged = visibility.managedEnabledModels.includes(currentDefault);
714
- if (!currentDefault || !defaultIsManaged || currentDefault === previousManagedDefault) {
835
+ return value;
836
+ }
837
+
838
+ function applyManagedModelRoles(settings, visibility, mappings, previousState = null) {
839
+ const originalRoles = previousState?.active && previousState.original?.modelRolesHadValue
840
+ ? previousState.original.modelRoles
841
+ : settings.modelRoles;
842
+ const roles = originalRoles && typeof originalRoles === 'object' && !Array.isArray(originalRoles)
843
+ ? rewriteSelectorTree(cloneJson(originalRoles), mappings.originalToManaged)
844
+ : {};
845
+ const originalDefault = String(originalRoles?.default || '');
846
+ const mappedDefault = rewriteModelSelector(originalDefault, mappings.originalToManaged);
847
+ const defaultWasMapped = originalDefault && mappedDefault !== originalDefault;
848
+ const defaultIsManaged = isManagedProviderId(String(roles.default || '').split('/')[0]);
849
+ if (visibility.managedDefaultModel && (!roles.default || (!defaultWasMapped && !defaultIsManaged))) {
715
850
  roles.default = visibility.managedDefaultModel;
716
851
  }
717
852
  return roles;
718
853
  }
719
854
 
855
+ function applyManagedFallbackChains(settings, mappings, previousState = null) {
856
+ const source = previousState?.active && previousState.original?.retryFallbackChainsHadValue
857
+ ? previousState.original.retryFallbackChains
858
+ : settings.retry?.fallbackChains;
859
+ if (source === undefined) return undefined;
860
+ return rewriteSelectorTree(cloneJson(source), mappings.originalToManaged);
861
+ }
862
+
720
863
  function syncManagedOmpVisibility(channels = [], modelsConfig = { providers: {} }, options = {}) {
721
864
  const paths = getOmpPaths();
722
865
  const settingsPath = getOmpSettingsPath(paths);
@@ -724,6 +867,7 @@ function syncManagedOmpVisibility(channels = [], modelsConfig = { providers: {}
724
867
  const settings = readOmpSettingsConfig(settingsPath);
725
868
  const previousState = readManagedVisibilityState(paths);
726
869
  const visibility = collectManagedVisibility(channels, modelsConfig, options);
870
+ const mappings = buildProviderMappings(channels);
727
871
  const original = previousState?.active && previousState.original
728
872
  ? previousState.original
729
873
  : captureOriginalVisibility(settings);
@@ -735,7 +879,13 @@ function syncManagedOmpVisibility(channels = [], modelsConfig = { providers: {}
735
879
  ...collectUserDisabledProviders(settings, previousState),
736
880
  ...visibility.managedDisabledProviders
737
881
  ]);
738
- settings.modelRoles = applyManagedModelRole(settings, visibility, previousState);
882
+ settings.modelRoles = applyManagedModelRoles(settings, visibility, mappings, previousState);
883
+ const fallbackChains = applyManagedFallbackChains(settings, mappings, previousState);
884
+ if (fallbackChains !== undefined) {
885
+ settings.retry = settings.retry && typeof settings.retry === 'object' && !Array.isArray(settings.retry)
886
+ ? { ...settings.retry, fallbackChains }
887
+ : { fallbackChains };
888
+ }
739
889
 
740
890
  const after = JSON.stringify(settings);
741
891
  if (before !== after) {
@@ -750,7 +900,12 @@ function syncManagedOmpVisibility(channels = [], modelsConfig = { providers: {}
750
900
  managedEnabledModelsForSettings: visibility.managedEnabledModelsForSettings,
751
901
  managedDisabledProviders: visibility.managedDisabledProviders,
752
902
  managedDefaultModel: visibility.managedDefaultModel,
903
+ providerMappings: mappings,
753
904
  original,
905
+ applied: {
906
+ modelRoles: cloneJson(settings.modelRoles),
907
+ retryFallbackChains: cloneJson(settings.retry?.fallbackChains)
908
+ },
754
909
  updatedAt: new Date().toISOString()
755
910
  };
756
911
  writeManagedVisibilityState(state, paths);
@@ -819,7 +974,39 @@ function removeManagedOmpVisibility(options = {}) {
819
974
  warnings.push('OMP disabledProviders was changed while coding-tool-x managed visibility was active; removed only managed provider entries.');
820
975
  }
821
976
 
822
- if (settings.modelRoles && typeof settings.modelRoles === 'object' && !Array.isArray(settings.modelRoles)) {
977
+ if (state.version >= 2 && state.original) {
978
+ const currentRoles = settings.modelRoles && typeof settings.modelRoles === 'object' && !Array.isArray(settings.modelRoles)
979
+ ? settings.modelRoles
980
+ : undefined;
981
+ if (JSON.stringify(currentRoles) === JSON.stringify(state.applied?.modelRoles)) {
982
+ if (state.original.modelRolesHadValue) {
983
+ settings.modelRoles = cloneJson(state.original.modelRoles);
984
+ } else {
985
+ delete settings.modelRoles;
986
+ }
987
+ } else if (currentRoles) {
988
+ settings.modelRoles = rewriteSelectorTree(currentRoles, state.providerMappings?.managedToOriginal || {});
989
+ warnings.push('OMP modelRoles was changed while coding-tool-x managed visibility was active; restored managed provider references only.');
990
+ }
991
+
992
+ const currentFallback = settings.retry?.fallbackChains;
993
+ if (JSON.stringify(currentFallback) === JSON.stringify(state.applied?.retryFallbackChains)) {
994
+ if (state.original.retryFallbackChainsHadValue) {
995
+ settings.retry = settings.retry && typeof settings.retry === 'object' && !Array.isArray(settings.retry)
996
+ ? { ...settings.retry, fallbackChains: cloneJson(state.original.retryFallbackChains) }
997
+ : { fallbackChains: cloneJson(state.original.retryFallbackChains) };
998
+ } else if (settings.retry && typeof settings.retry === 'object' && !Array.isArray(settings.retry)) {
999
+ delete settings.retry.fallbackChains;
1000
+ if (Object.keys(settings.retry).length === 0) delete settings.retry;
1001
+ }
1002
+ } else if (currentFallback !== undefined) {
1003
+ settings.retry.fallbackChains = rewriteSelectorTree(
1004
+ currentFallback,
1005
+ state.providerMappings?.managedToOriginal || {}
1006
+ );
1007
+ warnings.push('OMP retry.fallbackChains was changed while coding-tool-x managed visibility was active; restored managed provider references only.');
1008
+ }
1009
+ } else if (settings.modelRoles && typeof settings.modelRoles === 'object' && !Array.isArray(settings.modelRoles)) {
823
1010
  const roles = { ...settings.modelRoles };
824
1011
  if (roles.default === state.managedDefaultModel) {
825
1012
  if (state.original?.modelRolesHadDefault) {
@@ -845,6 +1032,39 @@ function removeManagedOmpVisibility(options = {}) {
845
1032
  }
846
1033
  warnings.push('Removed stale coding-tool-x managed OMP enabledModels entries without a visibility state file.');
847
1034
  }
1035
+
1036
+ if (settings.modelRoles && typeof settings.modelRoles === 'object' && !Array.isArray(settings.modelRoles)) {
1037
+ const roles = Object.fromEntries(
1038
+ Object.entries(settings.modelRoles).filter(([, model]) => {
1039
+ const providerId = String(model || '').split('/')[0];
1040
+ return !isManagedProviderId(providerId);
1041
+ })
1042
+ );
1043
+ if (Object.keys(roles).length !== Object.keys(settings.modelRoles).length) {
1044
+ settingsBackupPath = settingsBackupPath || createModelsBackupIfNeeded(settingsPath, options);
1045
+ if (Object.keys(roles).length > 0) {
1046
+ settings.modelRoles = roles;
1047
+ } else {
1048
+ delete settings.modelRoles;
1049
+ }
1050
+ warnings.push('Removed stale coding-tool-x managed OMP modelRoles entries without a visibility state file.');
1051
+ }
1052
+ }
1053
+
1054
+ const currentFallback = settings.retry?.fallbackChains;
1055
+ if (currentFallback !== undefined) {
1056
+ const fallbackChains = removeManagedSelectorTree(currentFallback);
1057
+ if (JSON.stringify(fallbackChains) !== JSON.stringify(currentFallback)) {
1058
+ settingsBackupPath = settingsBackupPath || createModelsBackupIfNeeded(settingsPath, options);
1059
+ if (fallbackChains && typeof fallbackChains === 'object' && Object.keys(fallbackChains).length > 0) {
1060
+ settings.retry.fallbackChains = fallbackChains;
1061
+ } else {
1062
+ delete settings.retry.fallbackChains;
1063
+ if (Object.keys(settings.retry).length === 0) delete settings.retry;
1064
+ }
1065
+ warnings.push('Removed stale coding-tool-x managed OMP retry.fallbackChains entries without a visibility state file.');
1066
+ }
1067
+ }
848
1068
  }
849
1069
 
850
1070
  const after = JSON.stringify(settings);
@@ -865,81 +1085,110 @@ function removeManagedOmpVisibility(options = {}) {
865
1085
 
866
1086
  function writeManagedOmpProviders(channels = [], options = {}) {
867
1087
  const paths = getOmpPaths();
1088
+ const settingsPath = getOmpSettingsPath(paths);
1089
+ const statePath = getManagedVisibilityStatePath(paths);
1090
+ const snapshots = captureFileSnapshots([paths.modelsYml, settingsPath, statePath]);
868
1091
  const buildWarnings = [];
869
- const config = buildManagedModelsConfig(channels, readModelsConfig(paths.modelsYml), {
870
- ...options,
871
- warnings: buildWarnings
872
- });
873
- const backupPath = createModelsBackupIfNeeded(paths.modelsYml, options);
874
- writeModelsConfig(config, paths.modelsYml);
875
- const visibility = syncManagedOmpVisibility(channels, config, options);
876
- const validation = options.validateWithCli === true
877
- ? validateOmpModelsConfig(options)
878
- : { skipped: true, reason: 'cli-validation-disabled', warnings: [] };
879
- const warnings = [
880
- ...buildWarnings,
881
- ...(visibility.warnings || []),
882
- ...(validation.warnings || [])
883
- ];
884
- recordManagedOmpSyncResult({
885
- path: paths.modelsYml,
886
- modelsPath: paths.modelsYml,
887
- settingsPath: visibility.settingsPath,
888
- statePath: visibility.statePath,
889
- backupPath,
890
- modelsBackupPath: backupPath,
891
- settingsBackupPath: visibility.settingsBackupPath,
892
- changed: true,
893
- visibilityChanged: visibility.changed,
894
- managedEnabledModels: visibility.managedEnabledModels,
895
- managedEnabledModelsForSettings: visibility.managedEnabledModelsForSettings,
896
- managedDisabledProviders: visibility.managedDisabledProviders,
897
- managedDefaultModel: visibility.managedDefaultModel,
898
- validation,
899
- warnings
1092
+ const orderedChannels = options.activeChannelId
1093
+ ? [
1094
+ ...channels.filter(channel => channel?.id === options.activeChannelId),
1095
+ ...channels.filter(channel => channel?.id !== options.activeChannelId)
1096
+ ]
1097
+ : channels;
1098
+ const prepared = options.gateway
1099
+ ? prepareManagedOmpChannels(orderedChannels, options.gateway)
1100
+ : { managedChannels: orderedChannels, unsupportedChannels: [] };
1101
+ const effectiveChannels = prepared.managedChannels;
1102
+ prepared.unsupportedChannels.forEach((channel) => {
1103
+ buildWarnings.push(`OMP channel "${channel.name || channel.id}" was not managed: ${channel.reason}.`);
900
1104
  });
901
- removeLegacyManagedExtension(paths);
902
- return paths.modelsYml;
903
- }
904
-
905
- function removeManagedOmpProviders(options = {}) {
906
- const paths = getOmpPaths();
907
- const config = readModelsConfig(paths.modelsYml);
908
- const visibility = removeManagedOmpVisibility(options);
909
- const before = Object.keys(config.providers || {}).length;
910
- pruneManagedProviders(config.providers);
911
- const after = Object.keys(config.providers || {}).length;
912
- let backupPath = null;
913
- let validation = {
914
- skipped: true,
915
- reason: before === after ? 'no-managed-providers' : 'not-run',
916
- warnings: []
917
- };
918
- if (before !== after) {
919
- backupPath = createModelsBackupIfNeeded(paths.modelsYml, options);
1105
+ try {
1106
+ const config = buildManagedModelsConfig(effectiveChannels, readModelsConfig(paths.modelsYml), {
1107
+ ...options,
1108
+ warnings: buildWarnings
1109
+ });
1110
+ const backupPath = createModelsBackupIfNeeded(paths.modelsYml, options);
920
1111
  writeModelsConfig(config, paths.modelsYml);
921
- validation = options.validateWithCli === true
1112
+ const visibility = syncManagedOmpVisibility(effectiveChannels, config, options);
1113
+ const validation = options.validateWithCli === true
922
1114
  ? validateOmpModelsConfig(options)
923
1115
  : { skipped: true, reason: 'cli-validation-disabled', warnings: [] };
924
- }
925
- recordManagedOmpSyncResult({
926
- path: paths.modelsYml,
927
- modelsPath: paths.modelsYml,
928
- settingsPath: visibility.settingsPath,
929
- statePath: visibility.statePath,
930
- backupPath,
931
- modelsBackupPath: backupPath,
932
- settingsBackupPath: visibility.settingsBackupPath,
933
- changed: before !== after,
934
- visibilityChanged: visibility.changed,
935
- stateRemoved: visibility.stateRemoved,
936
- validation,
937
- warnings: [
1116
+ const warnings = [
1117
+ ...buildWarnings,
938
1118
  ...(visibility.warnings || []),
939
1119
  ...(validation.warnings || [])
940
- ]
941
- });
942
- removeLegacyManagedExtension(paths);
1120
+ ];
1121
+ recordManagedOmpSyncResult({
1122
+ path: paths.modelsYml,
1123
+ modelsPath: paths.modelsYml,
1124
+ settingsPath: visibility.settingsPath,
1125
+ statePath: visibility.statePath,
1126
+ backupPath,
1127
+ modelsBackupPath: backupPath,
1128
+ settingsBackupPath: visibility.settingsBackupPath,
1129
+ changed: true,
1130
+ visibilityChanged: visibility.changed,
1131
+ managedEnabledModels: visibility.managedEnabledModels,
1132
+ managedEnabledModelsForSettings: visibility.managedEnabledModelsForSettings,
1133
+ managedDisabledProviders: visibility.managedDisabledProviders,
1134
+ managedDefaultModel: visibility.managedDefaultModel,
1135
+ validation,
1136
+ warnings
1137
+ });
1138
+ removeLegacyManagedExtension(paths);
1139
+ return paths.modelsYml;
1140
+ } catch (error) {
1141
+ restoreFileSnapshots(snapshots);
1142
+ throw error;
1143
+ }
1144
+ }
1145
+
1146
+ function removeManagedOmpProviders(options = {}) {
1147
+ const paths = getOmpPaths();
1148
+ const settingsPath = getOmpSettingsPath(paths);
1149
+ const statePath = getManagedVisibilityStatePath(paths);
1150
+ const snapshots = captureFileSnapshots([paths.modelsYml, settingsPath, statePath]);
1151
+ try {
1152
+ const config = readModelsConfig(paths.modelsYml);
1153
+ const visibility = removeManagedOmpVisibility(options);
1154
+ const before = Object.keys(config.providers || {}).length;
1155
+ pruneManagedProviders(config.providers);
1156
+ const after = Object.keys(config.providers || {}).length;
1157
+ let backupPath = null;
1158
+ let validation = {
1159
+ skipped: true,
1160
+ reason: before === after ? 'no-managed-providers' : 'not-run',
1161
+ warnings: []
1162
+ };
1163
+ if (before !== after) {
1164
+ backupPath = createModelsBackupIfNeeded(paths.modelsYml, options);
1165
+ writeModelsConfig(config, paths.modelsYml);
1166
+ validation = options.validateWithCli === true
1167
+ ? validateOmpModelsConfig(options)
1168
+ : { skipped: true, reason: 'cli-validation-disabled', warnings: [] };
1169
+ }
1170
+ recordManagedOmpSyncResult({
1171
+ path: paths.modelsYml,
1172
+ modelsPath: paths.modelsYml,
1173
+ settingsPath: visibility.settingsPath,
1174
+ statePath: visibility.statePath,
1175
+ backupPath,
1176
+ modelsBackupPath: backupPath,
1177
+ settingsBackupPath: visibility.settingsBackupPath,
1178
+ changed: before !== after,
1179
+ visibilityChanged: visibility.changed,
1180
+ stateRemoved: visibility.stateRemoved,
1181
+ validation,
1182
+ warnings: [
1183
+ ...(visibility.warnings || []),
1184
+ ...(validation.warnings || [])
1185
+ ]
1186
+ });
1187
+ removeLegacyManagedExtension(paths);
1188
+ } catch (error) {
1189
+ restoreFileSnapshots(snapshots);
1190
+ throw error;
1191
+ }
943
1192
  }
944
1193
 
945
1194
  function isManagedOmpProvidersActive() {
@@ -1 +0,0 @@
1
- import{d as e}from"./vendors-DQA4Hw9v.js";import{r as a,a8 as l,f as n,w as s,h as c,K as t,a7 as o,c as i,o as u,a as r,W as d,a0 as v,X as p,Y as h,N as y,a3 as m,_ as g,p as f,R as k,L as T,F as C,$ as b,Z as w,v as x}from"./vue-vendor-B5RqjEjQ.js";import{g as $,u as _,a as S,s as z,D as q,_ as P,b as j,c as L,d as M,R as I,e as O,f as E,h as N,i as F,j as A,n as D}from"./index-LYvv7wtx.js";import{h as U,ar as J,E as V,P as R,as as K,at as B,au as Z,av as G,aa as H,i as W,A as X,aw as Y,ax as Q,T as ee,a as ae,b as le}from"./icons-nGdSKQn2.js";import{L as ne,N as se,B as ce,s as te,b as oe,c as ie,a as ue,Z as re}from"./naive-ui-BALVpMgd.js";import"./markdown-BNJfEgCA.js";const de=a({theme:"light",panelVisibility:{showChannels:!0,showLogs:!0},channelBalance:{showRemaining:!1},channelLocks:{claude:!1,codex:!1,gemini:!1,opencode:!1,omp:!1},channelCollapse:{claude:[],codex:[],gemini:[],opencode:[],omp:[]},channelOrder:{claude:[],codex:[],gemini:[],opencode:[],omp:[]},dashboardChannelOrder:q,homeCliColumns:q,customCliPlatforms:[]});let ve=!1,pe=null;async function he(){return ve?de.value:pe||(pe=(async()=>{try{const e=await $();e.success&&e.config&&(de.value=e.config,ve=!0)}catch(e){}finally{pe=null}return de.value})(),pe)}function ye(){return ve||he(),{uiConfig:de,loadUIConfig:he,saveConfig:async function(e){try{const a=await z(e);return!!a.success&&(de.value=a.config,!0)}catch(a){return!1}},updateConfig:async function(e,a){try{const l=await S(e,a);return!!l.success&&(de.value=l.config,!0)}catch(l){return!1}},updateNestedConfig:async function(e,a,l){try{const n=await _(e,a,l);return!!n.success&&(de.value=n.config,!0)}catch(n){return!1}}}}const me={class:"channel-column"},ge={class:"drag-handle",title:"拖拽排序"},fe={class:"header-icon"},ke={class:"channel-title"},Te={key:0,class:"claude-extra-area"},Ce={key:0,class:"channel-content"},be={key:0,class:"card"},we={class:"card-header"},xe={key:0,class:"runtime-badge"},$e={class:"card-body",style:{padding:"6px 10px"}},_e={class:"proxy-info-row"},Se={class:"proxy-status"},ze={class:"proxy-port"},qe={class:"channel-name"},Pe={class:"channel-quick-panel"},je={class:"panel-title"},Le={key:0,class:"no-channels"},Me={key:1,class:"channel-quick-list"},Ie={class:"channel-quick-info"},Oe={class:"channel-quick-name"},Ee={key:0,class:"channel-switching-tip"},Ne={class:"channel-metrics"},Fe={class:"metric-item"},Ae={class:"metric-value"},De={class:"metric-item"},Ue={class:"metric-value"},Je={class:"metric-item"},Ve={class:"metric-value"},Re={class:"metric-item"},Ke={class:"card"},Be={class:"card-header compact"},Ze={class:"card-body",style:{padding:"8px 10px"}},Ge={class:"quick-access-list"},He={class:"access-icon"},We={class:"access-content"},Xe={class:"access-value"},Ye={class:"access-icon"},Qe={class:"access-content"},ea={class:"access-value"},aa={class:"access-icon"},la={class:"access-content"},na={class:"access-goto"},sa={class:"card-header compact"},ca={class:"card-body",style:{padding:"8px 10px"}},ta={class:"stats-inline stats-3col"},oa={class:"stat-inline-item stat-requests"},ia={class:"stat-info"},ua={class:"stat-inline-item stat-input"},ra={class:"stat-info"},da={class:"stat-inline-item stat-output"},va={class:"stat-info"},pa={key:2,class:"card logs-card"},ha={class:"card-header compact"},ya={class:"card-body logs-card-body"},ma={class:"logs-table-wrapper"},ga={key:0,class:"empty-logs"},fa=["title"],ka={key:0,class:"action-content"},Ta={class:"action-msg"},Ca={class:"action-time"},ba={key:1,class:"action-content"},wa={class:"action-msg error-msg"},xa={class:"action-time error-time"},$a={class:"locked-content"},_a={class:"lock-icon"},Sa="channelLocks",za=P({__name:"ChannelColumn",props:{channelType:{type:String,required:!0}},setup(e){const S=e,z=l(),q=ne(),{uiConfig:P}=ye(),{claudeProxy:D,codexProxy:de,geminiProxy:ve,opencodeProxy:pe,ompProxy:he,claudeChannels:za,codexChannels:qa,geminiChannels:Pa,opencodeChannels:ja,ompChannels:La,schedulerState:Ma,getProxyState:Ia,startProxy:Oa,stopProxy:Ea,getLogs:Na,clearLogsForSource:Fa,loadChannels:Aa,logLimit:Da,statsInterval:Ua}=j(),{dashboardData:Ja,loadDashboard:Va}=L(),Ra=n(()=>M(S.channelType,P.value.customCliPlatforms)),Ka=n(()=>Ra.value.title||Ra.value.label||S.channelType),Ba=n(()=>Ra.value.icon||U),Za=n(()=>Ra.value.color||"#64748b"),Ga=n(()=>"claude"===S.channelType?D.value:"codex"===S.channelType?de.value:"gemini"===S.channelType?ve.value:"opencode"===S.channelType?pe.value:"omp"===S.channelType?he.value:Ra.value.custom?{running:!1,loading:!1,activeChannel:null,port:null,runtime:null,startTime:null}:{}),Ha=n(()=>"claude"===S.channelType?"Claude":"codex"===S.channelType?"Codex":"gemini"===S.channelType?"Gemini":"opencode"===S.channelType?"OpenCode":"omp"===S.channelType?"OMP":Ra.value.label||S.channelType),Wa=a({projects:0,sessions:0}),Xa=a({requests:0,tokens:0,cost:0}),Ya=a({requests:0,tokens:0,cost:0}),Qa={requests:0,tokens:0,cost:3};let el={requests:null,tokens:null,cost:null};const al=a({requests:!1,tokens:!1,cost:!1}),ll=n(()=>{var e,a,l;return(null==(l=null==(a=null==(e=Ja.value)?void 0:e.todayStats)?void 0:a[S.channelType])?void 0:l.byModel)||{}});function nl(e,a,l,n=600){el[e]&&cancelAnimationFrame(el[e]),al.value[e]=!0;const s=Date.now(),c=()=>{const t=Date.now()-s,o=Math.min(t/n,1),i=1-Math.pow(1-o,2),u=a+(l-a)*i,r=Qa[e]??0,d=Math.pow(10,r),v=r>0?Math.round(u*d)/d:Math.round(u);Ya.value[e]=v,o<1?el[e]=requestAnimationFrame(c):al.value[e]=!1};el[e]=requestAnimationFrame(c)}const sl=a(!1),cl={claude:"claudeChannelCollapse",codex:"codexChannelCollapse",gemini:"geminiChannelCollapse",opencode:"opencodeChannelCollapse",omp:"ompChannelCollapse"};function tl(e){try{const a=localStorage.getItem(Sa),l=a?JSON.parse(a):{};l[S.channelType]=e,localStorage.setItem(Sa,JSON.stringify(l))}catch(a){}}const ol=a(function(){try{const e=localStorage.getItem(Sa);if(e){return JSON.parse(e)[S.channelType]||!1}}catch(e){}return!1}());async function il(){ol.value=!ol.value,tl(ol.value);try{if(await _("channelLocks",S.channelType,ol.value),Ja.value){const e=Ja.value.uiConfig||{},a={...e.channelLocks||{}};a[S.channelType]=ol.value,Ja.value={...Ja.value,uiConfig:{...e,channelLocks:a}}}}catch(e){}}const ul=a(!0);function rl(e){e.detail&&void 0!==e.detail.showLogs&&(ul.value=e.detail.showLogs)}const dl=a(Date.now()),vl=n(()=>{if(!Ga.value.running)return"";const e=Ga.value.startTime;if(!e)return"";const a=dl.value-e;if(a<=0)return"";const l=Math.floor(a/1e3),n=Math.floor(l/3600),s=Math.floor(l%3600/60),c=l%60;let t="已运行 ";return n>0&&(t+=`${n}小时`),s>0&&(t+=`${s}分`),(c>0||0===n&&0===s)&&(t+=`${c}秒`),t}),pl=a(null),hl=n(()=>Da.value),yl={claude:Na("claude"),codex:Na("codex"),gemini:Na("gemini"),opencode:Na("opencode"),omp:Na("omp")},ml=n(()=>{if(!zl())return[];const e=yl[S.channelType];if(!e)return[];return(e.value||[]).slice(0,hl.value)});let gl=null,fl=null,kl=null,Tl=!1,Cl=null;const bl=n(()=>{let e=[];"claude"===S.channelType?e=za.value||[]:"codex"===S.channelType?e=qa.value||[]:"gemini"===S.channelType?e=Pa.value||[]:"opencode"===S.channelType?e=ja.value||[]:"omp"===S.channelType&&(e=La.value||[]);return[...e.filter(e=>!1!==e.enabled),...e.filter(e=>!1===e.enabled)]});function wl(e){const a=Ma[S.channelType];if(!a||!a.channels)return 0;const l=a.channels.find(a=>a.id===e);return l?l.inflight:0}const xl=n(()=>{const e=bl.value.filter(e=>!1!==e.enabled).length;return Ga.value.running?`${e}个渠道调度中`:bl.value.length>0?`${e}个渠道已启用`:"无渠道"}),$l=n(()=>{var e,a,l;return(null==(l=null==(a=null==(e=Ja.value)?void 0:e.todayStats)?void 0:a[S.channelType])?void 0:l.byChannel)||{}});function _l(e){return $l.value[e]||{requests:0,tokens:0,cost:0}}function Sl(e,a){var l;return(null==e?void 0:e.usageMissing)?"--":(null==(l=null==e?void 0:e.tokens)?void 0:l[a])||0}function zl(){return["claude","codex","gemini","opencode","omp"].includes(S.channelType)}function ql(e){if(!e||"action"===e.type||"error"===e.status)return"";const a=[];return e.model&&a.push(`模型: ${e.model}`),e.originalModel&&e.originalModel!==e.model&&a.push(`重定向: ${e.originalModel} -> ${e.model}`),e.usageMissing&&a.push("上游响应未返回 usage,当前无法计算 token。"),a.join("\n")}async function Pl(e){var a,l;if(!1===Ra.value.supportsProxy||!zl())return void q.warning(`${Ka.value} 暂未接入动态代理`);const n=Ia(S.channelType);n.value.loading=!0;try{let a;a=e?await Oa(S.channelType):await Ea(S.channelType,{refreshChannelsDrawer:!0}),!1!==a.success?q.success(e?`${Ka.value} 代理已启动`:`${Ka.value} 代理已停止`):(q.error(a.error||"操作失败"),n.value.running=!e)}catch(s){q.error((null==(l=null==(a=s.response)?void 0:a.data)?void 0:l.error)||s.message||"操作失败"),n.value.running=!e}finally{n.value.loading=!1}}function jl(){!1!==Ra.value.supportsProjects&&zl()?z.push({name:`${S.channelType}-projects`}):q.info(`${Ka.value} 当前仅作为首页显示项`)}function Ll(){!1!==Ra.value.supportsProjects&&zl()?z.push({name:`${S.channelType}-projects`}):q.info(`${Ka.value} 当前仅作为首页显示项`)}function Ml(){!1!==Ra.value.supportsSessions&&zl()?sl.value=!0:q.info(`${Ka.value} 当前没有托管会话列表`)}s(ml,e=>{var a;const l=(null==(a=e[0])?void 0:a.id)||null;if(!l||l===gl)return void(gl=l);gl=l,Cl&&clearTimeout(Cl),Cl=setTimeout(()=>{Va(!0,{fresh:!0}).then(()=>Al()).catch(()=>{})},5e3);(!pl.value||pl.value.scrollTop<20)&&x(()=>{pl.value&&(pl.value.scrollTop=0)})}),s(()=>Xa.value.requests,e=>{nl("requests",Ya.value.requests,e,600)}),s(()=>Xa.value.tokens,e=>{nl("tokens",Ya.value.tokens,e,600)}),s(()=>Xa.value.cost,e=>{nl("cost",Ya.value.cost,e,600)}),s(()=>S.channelType,()=>{var e;gl=(null==(e=ml.value[0])?void 0:e.id)||null}),s(()=>{var e,a;return null==(a=null==(e=Ja.value)?void 0:e.counts)?void 0:a[S.channelType]},()=>{Fl()}),s(()=>{var e,a;return null==(a=null==(e=Ja.value)?void 0:e.todayStats)?void 0:a[S.channelType]},()=>{Al()},{deep:!0}),s(Ua,()=>{Tl&&function(){fl&&(clearInterval(fl),fl=null);const e=Ua.value||30,a=Math.max(1e3*e,1e4);fl=setInterval(()=>{Va(!0,{fresh:!0}).then(()=>Al()).catch(()=>{})},a)}()});const Il=a({});function Ol(e){return!!Il.value[e]}function El(e,a){e&&(Il.value={...Il.value,[e]:!!a})}async function Nl(e,a){if(e&&!Ol(e.id)){El(e.id,!0);try{let l;"claude"===S.channelType?l=O:"codex"===S.channelType?l=E:"gemini"===S.channelType?l=N:"opencode"===S.channelType?l=F:"omp"===S.channelType&&(l=A),l&&(await l(e.id,{enabled:a}),await async function(e,a){if(!e)return;const l=cl[S.channelType];let n={};try{const e=l?localStorage.getItem(l):null;n=e?JSON.parse(e):{}}catch(s){n={}}a?delete n[e]:n[e]=!0;try{l&&localStorage.setItem(l,JSON.stringify(n))}catch(s){}try{await _("channelCollapse",S.channelType,n)}catch(s){}}(e.id,a),q.success(a?`渠道「${e.name}」已启用`:`渠道「${e.name}」已停用`),await Aa(),window.dispatchEvent(new CustomEvent("channel-management-refresh",{detail:{channel:S.channelType}})))}catch(l){q.error("操作失败: "+l.message)}finally{El(e.id,!1)}}}function Fl(){var e,a;const l=null==(a=null==(e=Ja.value)?void 0:e.counts)?void 0:a[S.channelType];Wa.value.projects=(null==l?void 0:l.projectCount)||0,Wa.value.sessions=(null==l?void 0:l.sessionCount)||0}async function Al(){if(Ja.value&&Ja.value.todayStats){const e=Ja.value.todayStats[S.channelType];e&&(Xa.value.requests=e.requests||0,Xa.value.tokens=e.tokens||0,Xa.value.cost=e.cost||0)}Fl()}function Dl(){Fa(S.channelType)}function Ul(e){var a,l;null==(l=null==(a=null==e?void 0:e.currentTarget)?void 0:a.blur)||l.call(a)}function Jl(e){window.dispatchEvent(new CustomEvent("open-skills-drawer",{detail:{platform:S.channelType}})),Ul(e)}function Vl(e){window.dispatchEvent(new CustomEvent("open-commands-drawer",{detail:{platform:S.channelType}})),Ul(e)}function Rl(e){if(!1===Ra.value.supportsAgents)return q.info(`${Ka.value} 的 Agent 能力通过插件/扩展提供`),void Ul(e);window.dispatchEvent(new CustomEvent("open-agents-drawer",{detail:{platform:S.channelType}})),Ul(e)}return c(async()=>{await Va().then(()=>Al()),async function(){var e,a;try{if(Ja.value&&Ja.value.uiConfig)ul.value=!1!==(null==(e=Ja.value.uiConfig.panelVisibility)?void 0:e.showLogs);else{const e=await $();e.success&&e.config&&(ul.value=!1!==(null==(a=e.config.panelVisibility)?void 0:a.showLogs))}}catch(l){}}(),async function(){var e,a;try{let l=!1;if(Ja.value&&Ja.value.uiConfig)l=(null==(e=Ja.value.uiConfig.channelLocks)?void 0:e[S.channelType])||!1;else{const e=await $();e.success&&e.config&&(l=(null==(a=e.config.channelLocks)?void 0:a[S.channelType])||!1)}ol.value=l,tl(l)}catch(l){}}(),window.addEventListener("panel-visibility-change",rl),Ya.value={...Xa.value},Tl=!0,kl=setInterval(()=>{dl.value=Date.now()},1e3)}),t(()=>{Tl=!1,fl&&clearInterval(fl),kl&&clearInterval(kl),Cl&&clearTimeout(Cl),window.removeEventListener("panel-visibility-change",rl),Object.values(el).forEach(e=>{e&&cancelAnimationFrame(e)})}),(a,l)=>{const n=o("n-collapse");return u(),i("div",me,[r("div",{class:T(["channel-header",[e.channelType,{"custom-cli":Ra.value.custom}]]),style:k(Ra.value.custom?{"--channel-accent":Za.value}:null)},[r("div",ge,[p(y(se),{size:16},{default:h(()=>[p(y(J))]),_:1})]),r("div",fe,[p(y(se),{size:20},{default:h(()=>[(u(),v(m(Ba.value)))]),_:1})]),r("h2",ke,g(Ka.value),1),!1!==Ra.value.supportsManagedConfig?(u(),i("div",Te,[p(y(te),{trigger:"hover"},{trigger:h(()=>[p(y(ce),{text:"",class:"config-shortcut-button",onClick:Jl,title:"Skills 技能管理"},{icon:h(()=>[p(y(se),{size:18},{default:h(()=>[p(y(V))]),_:1})]),_:1})]),default:h(()=>[l[2]||(l[2]=f(" Skills 技能管理 ",-1))]),_:1}),p(y(te),{trigger:"hover"},{trigger:h(()=>[p(y(ce),{text:"",class:"config-shortcut-button",onClick:Vl,title:"Commands 命令管理"},{icon:h(()=>[p(y(se),{size:18},{default:h(()=>[p(y(U))]),_:1})]),_:1})]),default:h(()=>[l[3]||(l[3]=f(" Commands 命令管理 ",-1))]),_:1}),!1!==Ra.value.supportsAgents?(u(),v(y(te),{key:0,trigger:"hover"},{trigger:h(()=>[p(y(ce),{text:"",class:"config-shortcut-button",onClick:Rl,title:"Agents 代理管理"},{icon:h(()=>[p(y(se),{size:18},{default:h(()=>[p(y(R))]),_:1})]),_:1})]),default:h(()=>[l[4]||(l[4]=f(" Agents 代理管理 ",-1))]),_:1})):d("",!0)])):d("",!0),p(y(ce),{text:"",class:"lock-button",onClick:il,title:ol.value?"解锁此列":"锁定此列"},{icon:h(()=>[p(y(se),{size:18},{default:h(()=>[ol.value?(u(),v(y(K),{key:0})):(u(),v(y(B),{key:1}))]),_:1})]),_:1},8,["title"])],6),ol.value?d("",!0):(u(),i("div",Ce,[!1!==Ra.value.supportsProxy?(u(),i("div",be,[r("div",we,[p(y(se),{size:16},{default:h(()=>[p(y(Z))]),_:1}),l[5]||(l[5]=r("h3",{class:"card-title"},"代理控制",-1)),Ga.value.running&&vl.value?(u(),i("span",xe,g(vl.value),1)):d("",!0),p(y(oe),{value:Ga.value.running,"onUpdate:value":[l[0]||(l[0]=e=>Ga.value.running=e),Pl],loading:Ga.value.loading,size:"small",style:{"margin-left":"auto"}},null,8,["value","loading"])]),r("div",$e,[r("div",_e,[r("div",Se,[r("div",{class:T(["status-dot",{active:Ga.value.running}])},null,2),p(y(ie),{type:Ga.value.running?"success":"default",style:{"font-size":"12px"}},{default:h(()=>[f(g(Ga.value.running?"运行中":"已停止"),1)]),_:1},8,["type"]),r("span",ze,"端口: "+g(Ga.value.port),1)]),p(y(re),{trigger:"click",placement:"bottom",width:320,class:"channel-popover"},{trigger:h(()=>[p(y(ce),{text:"",size:"tiny",class:"channel-status"},{default:h(()=>[r("span",qe,g(xl.value),1)]),_:1})]),default:h(()=>[r("div",Pe,[r("div",je,[l[7]||(l[7]=r("span",null,"渠道快捷管理",-1)),p(y(ie),{depth:"3",style:{"font-size":"11px"}},{default:h(()=>[...l[6]||(l[6]=[f("点击开关切换状态",-1)])]),_:1})]),0===bl.value.length?(u(),i("div",Le,[p(y(ie),{depth:"3"},{default:h(()=>[...l[8]||(l[8]=[f("暂无配置渠道",-1)])]),_:1})])):(u(),i("div",Me,[(u(!0),i(C,null,b(bl.value,e=>{var a,n;return u(),i("div",{key:e.id,class:T(["channel-quick-item",{disabled:!1===e.enabled}])},[r("div",Ie,[r("span",Oe,g(e.name),1),"frozen"===(null==(a=e.health)?void 0:a.status)?(u(),v(y(ue),{key:0,size:"tiny",type:"error",bordered:!1},{default:h(()=>[...l[9]||(l[9]=[f(" 冻结 ",-1)])]),_:1})):d("",!0),p(y(oe),{size:"small",value:!1!==e.enabled,"onUpdate:value":a=>Nl(e,a),loading:Ol(e.id),disabled:Ol(e.id),style:{"margin-left":"auto"}},null,8,["value","onUpdate:value","loading","disabled"])]),Ol(e.id)?(u(),i("div",Ee," 正在切换渠道状态... ")):d("",!0),r("div",Ne,[r("span",Fe,[l[10]||(l[10]=r("span",{class:"metric-label"},"请求",-1)),r("span",Ae,g(_l(e.id).requests),1)]),r("span",De,[l[11]||(l[11]=r("span",{class:"metric-label"},"Tokens",-1)),r("span",Ue,g((n=_l(e.id).tokens,n>=1e6?(n/1e6).toFixed(1)+"M":n>=1e3?(n/1e3).toFixed(1)+"K":n.toString())),1)]),r("span",Je,[l[12]||(l[12]=r("span",{class:"metric-label"},"权重",-1)),r("span",Ve,g(e.weight||1),1)]),r("span",Re,[l[13]||(l[13]=r("span",{class:"metric-label"},"并发",-1)),r("span",{class:T(["metric-value",{active:wl(e.id)>0}])},g(wl(e.id))+g(e.maxConcurrency?`/${e.maxConcurrency}`:""),3)])])],2)}),128))]))])]),_:1})])])])):d("",!0),r("div",Ke,[r("div",Be,[p(y(se),{size:14},{default:h(()=>[p(y(G))]),_:1}),l[14]||(l[14]=r("h3",{class:"card-title"},"快速访问",-1))]),r("div",Ze,[r("div",Ge,[r("div",{class:T(["access-card access-card-projects",{clickable:!1!==Ra.value.supportsProjects,disabled:!1===Ra.value.supportsProjects}]),onClick:jl},[r("div",He,[p(y(se),{size:16},{default:h(()=>[p(y(H))]),_:1})]),r("div",We,[l[15]||(l[15]=r("span",{class:"access-label"},"项目",-1)),r("span",Xe,g(Wa.value.projects),1)])],2),r("div",{class:T(["access-card access-card-sessions",{clickable:!1!==Ra.value.supportsSessions,disabled:!1===Ra.value.supportsSessions}]),onClick:Ml},[r("div",Ye,[p(y(se),{size:16},{default:h(()=>[p(y(W))]),_:1})]),r("div",Qe,[l[16]||(l[16]=r("span",{class:"access-label"},"最新对话",-1)),r("span",ea,g(Wa.value.sessions),1)])],2),r("div",{class:T(["access-card access-card-goto",{clickable:!1!==Ra.value.supportsProjects,disabled:!1===Ra.value.supportsProjects}]),onClick:Ll},[r("div",aa,[p(y(se),{size:16},{default:h(()=>[p(y(X))]),_:1})]),r("div",la,[l[17]||(l[17]=r("span",{class:"access-label"},"前往",-1)),r("span",na,g(Ha.value),1)])],2)])])]),r("div",{class:T(["card stats-card",`stats-card-${e.channelType}`])},[r("div",sa,[p(y(se),{size:14},{default:h(()=>[p(y(Y))]),_:1}),l[18]||(l[18]=r("h3",{class:"card-title"},"今日数据",-1))]),r("div",ca,[r("div",ta,[r("div",oa,[l[20]||(l[20]=r("div",{class:"stat-icon-dot requests"},null,-1)),r("div",ia,[l[19]||(l[19]=r("span",{class:"stat-label"},"请求",-1)),r("span",{class:T(["stat-value",{animating:al.value.requests}])},g(Ya.value.requests),3)])]),r("div",ua,[l[22]||(l[22]=r("div",{class:"stat-icon-dot tokens"},null,-1)),r("div",ra,[l[21]||(l[21]=r("span",{class:"stat-label"},"总 Tokens",-1)),r("span",{class:T(["stat-value",{animating:al.value.tokens}])},g((c=Ya.value.tokens,c>=1e6?(c/1e6).toFixed(1)+"M":c>=1e3?(c/1e3).toFixed(1)+"K":c.toString())),3)])]),r("div",da,[l[24]||(l[24]=r("div",{class:"stat-icon-dot cost"},null,-1)),r("div",va,[l[23]||(l[23]=r("span",{class:"stat-label"},"成本 / USD",-1)),r("span",{class:T(["stat-value",{animating:al.value.cost}])},g((s=Ya.value.cost,!s||Number.isNaN(s)?"$0":"$"+Number(s).toFixed(3))),3)])])])])],2),Object.keys(ll.value).length>0?(u(),i("div",{key:1,class:T(["card chart-card",`chart-card-${e.channelType}`])},[p(n,{"default-expanded-names":[],accordion:""})],2)):d("",!0),ul.value?(u(),i("div",pa,[r("div",ha,[p(y(se),{size:14},{default:h(()=>[p(y(Q))]),_:1}),l[25]||(l[25]=r("h3",{class:"card-title"},"实时日志",-1)),p(y(ce),{text:"",size:"tiny",onClick:w(Dl,["stop"]),style:{"margin-left":"auto"},title:"清空日志"},{icon:h(()=>[p(y(se),{size:14},{default:h(()=>[p(y(ee))]),_:1})]),_:1})]),r("div",ya,[r("div",ma,[r("div",{class:T(["logs-table-header",`logs-header-${e.channelType}`])},[r("div",{class:T(["log-col col-channel",`col-channel-${e.channelType}`])},"渠道",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"请求",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"回复",2),"claude"===e.channelType?(u(),i(C,{key:0},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"写入",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"命中",2)],64)):"codex"===e.channelType?(u(),i(C,{key:1},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"gemini"===e.channelType?(u(),i(C,{key:2},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):"opencode"===e.channelType||"omp"===e.channelType?(u(),i(C,{key:3},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"推理",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"缓存",2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},"总计",2)],64)):d("",!0),r("div",{class:T(["log-col col-time",`col-time-${e.channelType}`])},"时间",2)],2),r("div",{class:"logs-container",ref_key:"logsContainer",ref:pl},[0===ml.value.length?(u(),i("div",ga,[p(y(se),{size:32,depth:"3",style:{"margin-bottom":"8px"}},{default:h(()=>[p(y(Q))]),_:1}),p(y(ie),{depth:"3",style:{"font-size":"12px","font-weight":"500"}},{default:h(()=>[...l[26]||(l[26]=[f("暂无实时日志",-1)])]),_:1}),p(y(ie),{depth:"3",style:{"font-size":"11px","margin-top":"4px"}},{default:h(()=>[...l[27]||(l[27]=[f("开启代理后将显示请求记录",-1)])]),_:1})])):d("",!0),(u(!0),i(C,null,b(ml.value,a=>(u(),i("div",{key:a.id,class:T(["log-row",{"action-row":"action"===a.type,"error-row":"error"===a.status,"new-log":a.isNew}]),title:ql(a)},["action"===a.type?(u(),i("div",ka,[p(y(se),{size:12,color:"#18a058"},{default:h(()=>[p(y(ae))]),_:1}),r("span",Ta,g(a.message),1),r("span",Ca,g(a.time),1)])):"error"===a.status?(u(),i("div",ba,[p(y(se),{size:12,color:"#d03050"},{default:h(()=>[p(y(le))]),_:1}),r("span",wa,g(a.channel)+": "+g(a.error||a.message||"请求失败"),1),r("span",xa,g(a.time),1)])):(u(),i(C,{key:2},[r("div",{class:T(["log-col col-channel",`col-channel-${e.channelType}`])},[p(y(ue),{size:"tiny",type:"success"},{default:h(()=>[f(g(a.channel),1)]),_:2},1024)],2),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"input")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"output")),3),"claude"===e.channelType?(u(),i(C,{key:0},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"cacheCreation")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"cacheRead")),3)],64)):"codex"===e.channelType?(u(),i(C,{key:1},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"reasoning")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"cached")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"total")),3)],64)):"gemini"===e.channelType?(u(),i(C,{key:2},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"cached")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"total")),3)],64)):"opencode"===e.channelType||"omp"===e.channelType?(u(),i(C,{key:3},[r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"reasoning")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"cached")),3),r("div",{class:T(["log-col col-token",`col-token-${e.channelType}`])},g(Sl(a,"total")),3)],64)):d("",!0),r("div",{class:T(["log-col col-time",`col-time-${e.channelType}`])},g(a.time),3)],64))],10,fa))),128))],512)])])])):d("",!0)])),ol.value?(u(),i("div",{key:1,class:T(["locked-overlay",`locked-${e.channelType}`])},[r("div",$a,[r("div",_a,[p(y(se),{size:48},{default:h(()=>[p(y(K))]),_:1})]),l[29]||(l[29]=r("h3",{class:"locked-title"},"该渠道已锁定",-1)),p(y(ie),{depth:"3",class:"locked-hint"},{default:h(()=>[...l[28]||(l[28]=[f(" 点击上方按钮解锁以查看内容 ",-1)])]),_:1})])],2)):d("",!0),!1!==Ra.value.supportsSessions?(u(),v(I,{key:2,visible:sl.value,"onUpdate:visible":l[1]||(l[1]=e=>sl.value=e),channel:e.channelType},null,8,["visible","channel"])):d("",!0)]);var s,c}}},[["__scopeId","data-v-8f1f9ae8"]]),qa={class:"dashboard-container"},Pa="dashboardChannelOrder",ja=P({__name:"Home",setup(l){const{uiConfig:n,updateConfig:s,loadUIConfig:o}=ye();function r(e){try{localStorage.setItem(Pa,JSON.stringify(e))}catch(a){}}function d(e,a=n.value.customCliPlatforms){const l=D(e,a);return m.value=l.map(e=>({type:e})),r(l),l}const m=a(function(){try{const e=localStorage.getItem(Pa);if(e){const a=JSON.parse(e);return D(a,n.value.customCliPlatforms)}}catch(e){}return q}().map(e=>({type:e})));async function g(){const e=D(m.value.map(e=>e.type),n.value.customCliPlatforms);m.value=e.map(e=>({type:e})),r(e),await s("homeCliColumns",e),await s("dashboardChannelOrder",e)}function f(e){const a=(null==e?void 0:e.detail)||{};d(a.homeCliColumns,a.customCliPlatforms)}return c(async()=>{window.addEventListener("home-cli-columns-change",f),await o(),d(n.value.homeCliColumns||n.value.dashboardChannelOrder,n.value.customCliPlatforms)}),t(()=>{window.removeEventListener("home-cli-columns-change",f)}),(a,l)=>(u(),i("div",qa,[p(y(e),{modelValue:m.value,"onUpdate:modelValue":l[0]||(l[0]=e=>m.value=e),class:"dashboard-grid","item-key":"type",animation:200,handle:".drag-handle",onEnd:g},{item:h(({element:e})=>[(u(),v(za,{"channel-type":e.type,key:e.type},null,8,["channel-type"]))]),_:1},8,["modelValue"])]))}},[["__scopeId","data-v-276ca51b"]]);export{ja as default};