coding-tool-x 3.9.1 → 3.9.2

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 (78) hide show
  1. package/README.md +1 -1
  2. package/dist/web/assets/{AgentsDrawer-CobzHziM.css → AgentsDrawer-Dubq1kQI.css} +1 -1
  3. package/dist/web/assets/AgentsDrawer-x29vGIIi.js +1 -0
  4. package/dist/web/assets/{Analytics-DTIvvMXu.js → Analytics-BJbR6XJO.js} +1 -1
  5. package/dist/web/assets/{AssetPathField-jv-lopQ3.js → AssetPathField-2W-16zgR.js} +1 -1
  6. package/dist/web/assets/{ChatHistoryDrawer-T4OVnJzM.js → ChatHistoryDrawer-C1URzQ_4.js} +1 -1
  7. package/dist/web/assets/{CollapseItem-P6BvSxEu.js → CollapseItem-DjkOkbwE.js} +1 -1
  8. package/dist/web/assets/{CommandsDrawer-DtFJRtow.js → CommandsDrawer-DcnfrHC4.js} +1 -1
  9. package/dist/web/assets/{ConfigExportDrawer-Di7KXhVS.js → ConfigExportDrawer-CKwG_RGJ.js} +1 -1
  10. package/dist/web/assets/{ConfigTemplates-VdKE5Kp0.js → ConfigTemplates-C1tNpfzi.js} +1 -1
  11. package/dist/web/assets/{ConfigTemplatesDrawer-bQvPnUQ0.js → ConfigTemplatesDrawer-gZNPbcUR.js} +1 -1
  12. package/dist/web/assets/{ConfigTemplatesPanel-ChdyQ3C6.js → ConfigTemplatesPanel-C-Fy53Qo.js} +1 -1
  13. package/dist/web/assets/{FavoritesDrawer-A25XfI7C.js → FavoritesDrawer-Bp8FYHPk.js} +1 -1
  14. package/dist/web/assets/GatewayConvertDrawer-CvUkvNQy.css +1 -0
  15. package/dist/web/assets/GatewayConvertDrawer-j5IJzb2Y.js +1 -0
  16. package/dist/web/assets/Home-2MAvBnml.js +1 -0
  17. package/dist/web/assets/Home-BRNWE0Qy.css +1 -0
  18. package/dist/web/assets/{MarkdownEditor-BXwTqbMo.js → MarkdownEditor-BA_PcLed.js} +1 -1
  19. package/dist/web/assets/{McpDrawer-DIaiFq4R.js → McpDrawer-DAep5PP_.js} +1 -1
  20. package/dist/web/assets/{McpFormDrawer-CQgTYAux.js → McpFormDrawer-BRm-hzv1.js} +1 -1
  21. package/dist/web/assets/{PluginManager-BkGrr21J.js → PluginManager-C2yY8-FP.js} +1 -1
  22. package/dist/web/assets/{PluginsDrawer-Bq1JkM5r.js → PluginsDrawer-CxKnWzTO.js} +1 -1
  23. package/dist/web/assets/{PluginsPanel-CzGMK-Ap.css → PluginsPanel-DsmiW9TU.css} +1 -1
  24. package/dist/web/assets/PluginsPanel-DtbXSoXu.js +1 -0
  25. package/dist/web/assets/{ProjectList-QIC7-9ic.js → ProjectList-Cb25pa2s.js} +1 -1
  26. package/dist/web/assets/{PromptsDrawer-BLeUYZWW.js → PromptsDrawer-D39OJVRP.js} +1 -1
  27. package/dist/web/assets/{RecentSessionsDrawer-yOpScIwI.js → RecentSessionsDrawer-DkCjc4kS.js} +1 -1
  28. package/dist/web/assets/{SessionCard-DeKRHAzu.js → SessionCard-5Mwig8zy.js} +1 -1
  29. package/dist/web/assets/{SessionList-B0fPT_VB.js → SessionList-DVkIlraF.js} +1 -1
  30. package/dist/web/assets/{SettingsDrawer-DpZoDj3M.js → SettingsDrawer-Cr9qZDgO.js} +1 -1
  31. package/dist/web/assets/{SkillManager-BpPqV2o4.js → SkillManager-D935sPSV.js} +1 -1
  32. package/dist/web/assets/{SkillsDrawer-BgMlJbgS.js → SkillsDrawer-D1_3j74b.js} +1 -1
  33. package/dist/web/assets/{SkillsPanel-CK-pSSHU.js → SkillsPanel-6SehnNG8.js} +1 -1
  34. package/dist/web/assets/{Upload-DtuQ7ady.js → Upload-38eKB6wv.js} +1 -1
  35. package/dist/web/assets/{WorkspaceDrawer-Bu5mOGsF.js → WorkspaceDrawer-Bkfmlr5M.js} +1 -1
  36. package/dist/web/assets/{WorkspaceManager-D1mhnNV-.js → WorkspaceManager-DFVQ5YoE.js} +1 -1
  37. package/dist/web/assets/{WorkspacePanel-BIrkMC_5.js → WorkspacePanel-CEnogXEv.js} +1 -1
  38. package/dist/web/assets/client-oU6RT-6I.js +1 -0
  39. package/dist/web/assets/dsh-agent-presets-DjJRVMT6.js +1 -0
  40. package/dist/web/assets/index-CCATqHAD.js +2 -0
  41. package/dist/web/assets/index-p-yggtLj.css +1 -0
  42. package/dist/web/assets/{platforms-DrzAtnjY.js → platforms-DILqyP72.js} +1 -1
  43. package/dist/web/assets/platforms-JV36QBCw.js +1 -0
  44. package/dist/web/assets/{project-config-BVt5s-pH.js → project-config-CzFKBw_m.js} +1 -1
  45. package/dist/web/assets/{sessions-CaWUaysM.js → sessions-WXODMvLY.js} +1 -1
  46. package/dist/web/assets/{sessions-53-Fr68H.js → sessions-wvj5oaYr.js} +1 -1
  47. package/dist/web/assets/{useResponsiveDrawer-C-UacYVP.js → useResponsiveDrawer-BWRBXImg.js} +1 -1
  48. package/dist/web/assets/{workspaces-DQuZd4qw.js → workspaces-D2yLnT-B.js} +1 -1
  49. package/dist/web/index.html +5 -5
  50. package/package.json +4 -4
  51. package/src/platforms/drivers/dsh/api-operations.js +60 -2
  52. package/src/platforms/drivers/dsh/common.js +222 -11
  53. package/src/platforms/drivers/dsh/native-config.js +2 -2
  54. package/src/platforms/drivers/dsh/native-logs.js +16 -1
  55. package/src/platforms/drivers/dsh/plugin-manager.js +64 -6
  56. package/src/platforms/drivers/omp/gateway.js +27 -1
  57. package/src/platforms/drivers/opencode/conversion.js +60 -8
  58. package/src/platforms/manifest-schema.js +4 -0
  59. package/src/platforms/manifests/dsh.json +29 -4
  60. package/src/platforms/manifests/omp.json +2 -1
  61. package/src/platforms/registry.js +1 -0
  62. package/src/server/api/convert.js +26 -76
  63. package/src/server/api/platforms.js +1 -0
  64. package/src/server/services/model-detector.js +13 -22
  65. package/src/server/services/native-log-observer.js +1 -0
  66. package/src/server/services/plugins-service.js +13 -14
  67. package/src/server/services/request-logger.js +95 -0
  68. package/src/server/services/usage-log-utils.js +2 -1
  69. package/dist/web/assets/AgentsDrawer-MeGfH4I0.js +0 -1
  70. package/dist/web/assets/GatewayConvertDrawer-CuNlf7AC.css +0 -1
  71. package/dist/web/assets/GatewayConvertDrawer-TSJPvY4f.js +0 -1
  72. package/dist/web/assets/Home-CSuFuJ9Y.css +0 -1
  73. package/dist/web/assets/Home-CzB85lKE.js +0 -1
  74. package/dist/web/assets/PluginsPanel-Clz1r67v.js +0 -1
  75. package/dist/web/assets/client-BgcNg4Bt.js +0 -1
  76. package/dist/web/assets/index-B86H0_5y.css +0 -1
  77. package/dist/web/assets/index-Bw6YXbBA.js +0 -2
  78. package/dist/web/assets/platforms-mqqe_82d.js +0 -1
@@ -5,6 +5,7 @@
5
5
  "command": "dsh",
6
6
  "iconToken": "terminal",
7
7
  "color": "#2563eb",
8
+ "logLayout": "basic",
8
9
  "defaultVisible": true,
9
10
  "defaultEnabled": false,
10
11
  "sortOrder": 50,
@@ -25,7 +26,7 @@
25
26
  "resourceTypes": {
26
27
  "skills": true,
27
28
  "commands": false,
28
- "agents": false,
29
+ "agents": true,
29
30
  "plugins": true,
30
31
  "mcp": true,
31
32
  "prompts": true
@@ -38,10 +39,10 @@
38
39
  "refresh": true
39
40
  },
40
41
  "plugins": {
41
- "repositories": true,
42
+ "repositories": false,
42
43
  "market": false,
43
- "install": false,
44
- "uninstall": false,
44
+ "install": true,
45
+ "uninstall": true,
45
46
  "import": false,
46
47
  "syncRepos": false
47
48
  }
@@ -477,6 +478,30 @@
477
478
  "request": "default",
478
479
  "response": "default"
479
480
  },
481
+ {
482
+ "method": "GET",
483
+ "path": "/profiles/:profileName/agent-presets",
484
+ "capability": "api",
485
+ "operation": "listProfileAgentPresets",
486
+ "request": "default",
487
+ "response": "default"
488
+ },
489
+ {
490
+ "method": "POST",
491
+ "path": "/profiles/:profileName/agent-presets",
492
+ "capability": "api",
493
+ "operation": "upsertAgentPreset",
494
+ "request": "default",
495
+ "response": "default"
496
+ },
497
+ {
498
+ "method": "DELETE",
499
+ "path": "/profiles/:profileName/agent-presets/:presetId",
500
+ "capability": "api",
501
+ "operation": "deleteAgentPreset",
502
+ "request": "default",
503
+ "response": "default"
504
+ },
480
505
  {
481
506
  "method": "GET",
482
507
  "path": "/skills",
@@ -484,6 +484,7 @@
484
484
  "hooks": "legacy:omp",
485
485
  "resourceSync": "legacy:omp",
486
486
  "mcp": "legacy:omp",
487
- "prompts": "legacy:omp"
487
+ "prompts": "legacy:omp",
488
+ "conversion": "opencode-conversion"
488
489
  }
489
490
  }
@@ -188,6 +188,7 @@ function createPlatformRegistry({
188
188
  portLabel: platform.portLabel || null,
189
189
  apiPrefix: platform.api?.prefix || null,
190
190
  modelCatalogKey: platform.modelConfig?.catalogKey || platform.key,
191
+ ...(platform.logLayout ? { logLayout: platform.logLayout } : {}),
191
192
  capabilities
192
193
  };
193
194
  const resourceTypes = publicResourceTypes(platform.resourceTypes);
@@ -1,40 +1,28 @@
1
1
  const express = require('express');
2
2
  const router = express.Router();
3
3
  const { getPlatformCatalog } = require('../services/platform-catalog');
4
+ const { createSameOriginGuard } = require('../services/network-access');
4
5
 
5
- /**
6
- * 获取支持的格式列表
7
- * GET /api/convert/formats
8
- */
9
- router.get('/formats', (req, res) => {
10
- const driver = getPlatformCatalog().driver('opencode', 'conversion');
6
+ router.use(createSameOriginGuard({
7
+ message: '禁止跨站访问请求转换接口'
8
+ }));
9
+
10
+ function getConversionDriver(target) {
11
+ return getPlatformCatalog().driver(target, 'conversion');
12
+ }
13
+
14
+ function getConversionFormats(driver) {
11
15
  const formats = driver?.formats?.() || driver?.getFormats?.();
12
- if (!formats) {
13
- return res.status(404).json({ success: false, error: 'Conversion is not supported' });
14
- }
15
- const sourceTypes = formats.sourceTypes || [];
16
- res.json({
17
- formats: sourceTypes.map(type => ({
18
- id: type,
19
- name: (formats.formats || []).find(item => item.id === type)?.name
20
- || String(type).replace(/[-_]/g, ' ').replace(/\b\w/g, char => char.toUpperCase()),
21
- description: `${type} session format`,
22
- extension: '.jsonl',
23
- icon: type
24
- })),
25
- conversions: sourceTypes.flatMap(sourceType => sourceTypes
26
- .filter(targetType => targetType !== sourceType)
27
- .map(targetType => ({ from: sourceType, to: targetType })))
28
- });
29
- });
16
+ return formats;
17
+ }
30
18
 
31
19
  /**
32
- * 获取 OpenCode 网关支持格式
33
- * GET /api/convert/opencode/formats
20
+ * Read conversion capabilities for a target CLI.
21
+ * GET /api/convert/:target/formats
34
22
  */
35
- router.get('/opencode/formats', (req, res) => {
36
- const driver = getConversionDriver();
37
- const formats = driver?.formats?.() || driver?.getFormats?.();
23
+ router.get('/:target/formats', (req, res) => {
24
+ const driver = getConversionDriver(req.params.target);
25
+ const formats = getConversionFormats(driver);
38
26
  if (!formats) {
39
27
  return res.status(404).json({ success: false, error: 'Conversion is not supported' });
40
28
  }
@@ -45,62 +33,24 @@ router.get('/opencode/formats', (req, res) => {
45
33
  name: (formats.formats || []).find(item => item.id === type)?.name
46
34
  || String(type).replace(/[-_]/g, ' ').replace(/\b\w/g, char => char.toUpperCase())
47
35
  })),
48
- target: 'opencode',
36
+ target: req.params.target,
49
37
  targetApis: formats.targetApis || [],
50
38
  defaultTargetApi: formats.defaultTargetApi,
51
- endpoints: formats.endpoints || {},
52
- sourceEndpoints: Object.fromEntries(sourceTypes.map(type => [type, `/api/convert/opencode/${type}`]))
39
+ endpoints: formats.endpoints || {}
53
40
  });
54
41
  });
55
42
 
56
- function getConversionDriver() {
57
- return getPlatformCatalog().driver('opencode', 'conversion');
58
- }
59
-
60
- function handleOpenCodeConvert(req, res, sourceType) {
61
- try {
62
- const { payload, options = {} } = req.body || {};
63
- const driver = getConversionDriver();
64
-
65
- if (!driver) {
66
- return res.status(404).json({ success: false, error: 'Conversion is not supported' });
67
- }
68
- if (!payload) {
69
- return res.status(400).json({
70
- success: false,
71
- error: 'Missing required parameters: payload'
72
- });
73
- }
74
- const result = driver.convertSource
75
- ? driver.convertSource(sourceType, payload, options)
76
- : driver.convert({ sourceType, payload, options });
77
- return res.json({
78
- success: true,
79
- ...result
80
- });
81
- } catch (error) {
82
- console.error(`[Convert API] OpenCode ${sourceType} convert error:`, error);
83
- return res.status(500).json({
84
- success: false,
85
- error: error.message
86
- });
87
- }
88
- }
89
-
90
- router.post('/opencode/:sourceType', (req, res) => {
91
- return handleOpenCodeConvert(req, res, req.params.sourceType);
92
- });
93
-
94
43
  /**
95
- * 在线转换为 OpenCode 可处理格式
96
- * POST /api/convert/opencode
44
+ * Online conversion to a CLI-compatible request format.
45
+ * POST /api/convert/:target
97
46
  * Body: { sourceType, payload, options? }
98
47
  */
99
- router.post('/opencode', (req, res) => {
48
+ router.post('/:target', (req, res) => {
100
49
  try {
101
50
  const { sourceType, payload, options = {} } = req.body || {};
102
- const driver = getConversionDriver();
103
- const sourceTypes = driver?.formats?.().sourceTypes || driver?.getFormats?.().sourceTypes || [];
51
+ const driver = getConversionDriver(req.params.target);
52
+ const formats = getConversionFormats(driver);
53
+ const sourceTypes = formats?.sourceTypes || [];
104
54
  const normalized = driver?.normalizeSourceType?.(sourceType) || sourceType;
105
55
 
106
56
  if (!driver) {
@@ -124,7 +74,7 @@ router.post('/opencode', (req, res) => {
124
74
  ...driver.convert({ sourceType: normalized, payload, options })
125
75
  });
126
76
  } catch (error) {
127
- console.error('[Convert API] OpenCode gateway convert error:', error);
77
+ console.error(`[Convert API] ${req.params.target} convert error:`, error);
128
78
  return res.status(500).json({
129
79
  success: false,
130
80
  error: error.message
@@ -18,6 +18,7 @@ const PUBLIC_FIELDS = Object.freeze([
18
18
  'portLabel',
19
19
  'apiPrefix',
20
20
  'modelCatalogKey',
21
+ 'logLayout',
21
22
  'promptLabel',
22
23
  'resourceTypes',
23
24
  'resourceActions'
@@ -330,32 +330,29 @@ function buildRequestHeaders(channelType, channel) {
330
330
  }
331
331
 
332
332
  function buildOpenAiCompatibleUrl(baseUrl, endpoint) {
333
- const trimmed = String(baseUrl || '').trim().replace(/\/+$/, '');
334
- if (!trimmed) {
333
+ const rawBaseUrl = String(baseUrl || '').trim();
334
+ if (!rawBaseUrl) {
335
335
  throw new Error('Invalid baseUrl');
336
336
  }
337
337
 
338
+ const parsed = new URL(rawBaseUrl);
339
+ const basePath = parsed.pathname.replace(/\/+$/, '');
338
340
  const normalizedEndpoint = String(endpoint || '').startsWith('/')
339
341
  ? String(endpoint)
340
342
  : `/${endpoint}`;
341
-
342
- if (trimmed.endsWith(normalizedEndpoint)) {
343
- return trimmed;
344
- }
345
-
346
343
  const endpointWithoutV1 = normalizedEndpoint.startsWith('/v1/')
347
344
  ? normalizedEndpoint.slice(3)
348
345
  : normalizedEndpoint;
349
346
 
350
- if (trimmed.endsWith(endpointWithoutV1)) {
351
- return trimmed;
352
- }
347
+ if (basePath.endsWith(normalizedEndpoint)) return parsed.toString();
348
+ if (basePath.endsWith(endpointWithoutV1)) return parsed.toString();
353
349
 
354
- if (trimmed.endsWith('/v1') && normalizedEndpoint.startsWith('/v1/')) {
355
- return `${trimmed}${normalizedEndpoint.slice(3)}`;
356
- }
357
-
358
- return `${trimmed}${normalizedEndpoint}`;
350
+ const suffix = /\/v1$/.test(basePath) && normalizedEndpoint.startsWith('/v1/')
351
+ ? normalizedEndpoint.slice(3)
352
+ : normalizedEndpoint;
353
+ parsed.pathname = `${basePath}${suffix}` || '/';
354
+ parsed.hash = '';
355
+ return parsed.toString();
359
356
  }
360
357
 
361
358
 
@@ -1044,14 +1041,8 @@ async function fetchModelsFromProvider(channel, channelType, options = {}) {
1044
1041
  resolve(result);
1045
1042
  };
1046
1043
  try {
1047
- const baseUrl = channel.baseUrl.trim().replace(/\/+$/, '');
1048
1044
  const endpoint = capability.modelListEndpoint; // e.g. '/v1/models'
1049
- // 避免路径重复:如果 baseUrl 已包含 /v1,则只拼接 /models
1050
- const requestUrl = baseUrl.endsWith('/v1') && endpoint.startsWith('/v1/')
1051
- ? `${baseUrl}${endpoint.slice(3)}`
1052
- : `${baseUrl}${endpoint}`;
1053
-
1054
- const parsedUrl = new URL(requestUrl);
1045
+ const parsedUrl = new URL(buildOpenAiCompatibleUrl(channel.baseUrl, endpoint));
1055
1046
  const isHttps = parsedUrl.protocol === 'https:';
1056
1047
  const httpModule = isHttps ? https : http;
1057
1048
 
@@ -140,6 +140,7 @@ function recordEvent(platform, event, runtime = getPlatformRuntime(), channelCac
140
140
  channel,
141
141
  model: event.model || '',
142
142
  tokens,
143
+ cacheUsageAvailable: event.cacheUsageAvailable,
143
144
  cost: Number(event.cost) || 0,
144
145
  timestamp,
145
146
  usageMissing: false
@@ -117,6 +117,18 @@ const PLATFORM_CAPABILITIES = {
117
117
  import: false,
118
118
  syncRepos: false
119
119
  },
120
+ dsh: {
121
+ platform: 'dsh',
122
+ supportsPlugins: true,
123
+ repositories: false,
124
+ market: false,
125
+ install: true,
126
+ uninstall: true,
127
+ toggle: false,
128
+ config: false,
129
+ import: false,
130
+ syncRepos: false
131
+ },
120
132
  omp: {
121
133
  platform: 'omp',
122
134
  supportsPlugins: true,
@@ -134,19 +146,6 @@ const PLATFORM_CAPABILITIES = {
134
146
  repositoryAuth: false,
135
147
  installMetadataMode: 'omp'
136
148
  },
137
- dsh: {
138
- platform: 'dsh',
139
- supportsPlugins: true,
140
- repositories: true,
141
- market: false,
142
- install: false,
143
- uninstall: false,
144
- toggle: false,
145
- config: false,
146
- import: false,
147
- syncRepos: false,
148
- disabledReason: 'DSH 插件由 profile 管理,当前面板仅提供只读查看'
149
- }
150
149
  };
151
150
 
152
151
  function cloneRepos(repos = []) {
@@ -1670,7 +1669,7 @@ class PluginsService {
1670
1669
  source: 'dsh-profile',
1671
1670
  pluginType: 'dsh-profile',
1672
1671
  enabled: true,
1673
- readonly: true,
1672
+ readonly: plugin.builtIn === true,
1674
1673
  description: plugin.description || '',
1675
1674
  directory: plugin.name || name
1676
1675
  });
@@ -21,6 +21,21 @@ const { PATHS } = pathsModule;
21
21
  const getPlatformStatePath = typeof pathsModule.getPlatformStatePath === 'function'
22
22
  ? pathsModule.getPlatformStatePath
23
23
  : (category, platform) => PATHS[category]?.[platform];
24
+ const latestProxyRequestSnapshots = new Map();
25
+
26
+ function compactProxyRequestSnapshot(payload = {}) {
27
+ const request = payload.request && typeof payload.request === 'object' ? payload.request : {};
28
+ const route = payload.route && typeof payload.route === 'object' ? payload.route : undefined;
29
+ return {
30
+ ...(payload.timestamp !== undefined ? { timestamp: payload.timestamp } : {}),
31
+ ...(payload.source ? { source: payload.source } : {}),
32
+ request: {
33
+ ...(request.method ? { method: request.method } : {}),
34
+ ...(request.headers && typeof request.headers === 'object' ? { headers: request.headers } : {})
35
+ },
36
+ ...(route ? { route: { ...route } } : {})
37
+ };
38
+ }
24
39
 
25
40
  function ensureDir(dir) {
26
41
  if (!fs.existsSync(dir)) {
@@ -57,6 +72,10 @@ function persistProxyRequestSnapshot(source, payload) {
57
72
  if (!isProxyRequestLoggingEnabled()) return;
58
73
 
59
74
  try {
75
+ latestProxyRequestSnapshots.set(
76
+ String(source || '').trim().toLowerCase(),
77
+ compactProxyRequestSnapshot(payload)
78
+ );
60
79
  const logPath = getPlatformStatePath('requestSnapshots', source);
61
80
  if (!logPath) return;
62
81
  ensureDir(path.dirname(logPath));
@@ -70,6 +89,80 @@ function persistProxyRequestSnapshot(source, payload) {
70
89
  }
71
90
  }
72
91
 
92
+ function loadLatestProxyRequestSnapshot(source) {
93
+ const normalizedSource = String(source || '').trim().toLowerCase();
94
+ if (latestProxyRequestSnapshots.has(normalizedSource)) {
95
+ return latestProxyRequestSnapshots.get(normalizedSource);
96
+ }
97
+ const logPath = getPlatformStatePath('requestSnapshots', normalizedSource);
98
+ if (!logPath) return {};
99
+
100
+ let fd;
101
+ try {
102
+ fd = fs.openSync(logPath, 'r');
103
+ const fileSize = fs.fstatSync(fd).size;
104
+ const chunkSize = 1024 * 1024;
105
+ let position = fileSize;
106
+ let parts = [];
107
+ let bytesScanned = 0;
108
+ while (position > 0) {
109
+ const bytesToRead = Math.min(position, chunkSize);
110
+ position -= bytesToRead;
111
+ bytesScanned += bytesToRead;
112
+ const chunk = Buffer.alloc(bytesToRead);
113
+ fs.readSync(fd, chunk, 0, bytesToRead, position);
114
+ let segmentEnd = chunk.length;
115
+ for (let index = chunk.length - 1; index >= 0; index -= 1) {
116
+ if (chunk[index] !== 10) continue;
117
+ if (segmentEnd > index + 1) parts.push(chunk.subarray(index + 1, segmentEnd));
118
+ const line = Buffer.concat(parts.reverse()).toString('utf8');
119
+ parts = [];
120
+ segmentEnd = index;
121
+ if (!line.trim()) continue;
122
+ try {
123
+ const entry = JSON.parse(line);
124
+ if (entry && typeof entry === 'object') {
125
+ const compact = compactProxyRequestSnapshot(entry);
126
+ latestProxyRequestSnapshots.set(normalizedSource, compact);
127
+ return compact;
128
+ }
129
+ } catch {
130
+ // Ignore malformed records and continue scanning older complete lines.
131
+ }
132
+ }
133
+ if (segmentEnd > 0) parts.push(chunk.subarray(0, segmentEnd));
134
+ if (bytesScanned > 128 * 1024 * 1024) break;
135
+ }
136
+ } catch {
137
+ // Request snapshots are optional and may not exist yet.
138
+ } finally {
139
+ if (fd !== undefined) fs.closeSync(fd);
140
+ }
141
+ return {};
142
+ }
143
+
144
+ function loadLatestProxyRequestHeaders(source) {
145
+ const headers = loadLatestProxyRequestSnapshot(source)?.request?.headers;
146
+ if (!headers || typeof headers !== 'object' || Array.isArray(headers)) return {};
147
+
148
+ const hopByHopHeaders = new Set([
149
+ 'connection',
150
+ 'content-length',
151
+ 'host',
152
+ 'keep-alive',
153
+ 'proxy-connection',
154
+ 'te',
155
+ 'trailer',
156
+ 'transfer-encoding',
157
+ 'upgrade'
158
+ ]);
159
+ const sensitiveHeaderName = /(auth|cookie|session|token|secret|credential|api[-_]?key|password|signature|\bjwt\b|capability)/i;
160
+
161
+ return Object.fromEntries(Object.entries(headers).filter(([name]) => (
162
+ !hopByHopHeaders.has(name.toLowerCase()) && !sensitiveHeaderName.test(name)
163
+ )));
164
+ }
165
+
73
166
  /**
74
167
  * 创建 Express 中间件,用于记录 REST API 请求
75
168
  * 仅当 CC_TOOL_LOG_API_REQUESTS=true 时生效
@@ -317,6 +410,8 @@ module.exports = {
317
410
  isProxyRequestLoggingEnabled,
318
411
  isApiRequestLoggingEnabled,
319
412
  persistProxyRequestSnapshot,
413
+ loadLatestProxyRequestSnapshot,
414
+ loadLatestProxyRequestHeaders,
320
415
  persistClaudeRequestTemplate,
321
416
  loadClaudeRequestTemplate,
322
417
  createApiRequestLogger
@@ -73,7 +73,7 @@ function formatRealtimeTime(timestamp = Date.now()) {
73
73
  return new Date(timestamp).toLocaleTimeString('zh-CN', { hour12: false, hour: '2-digit', minute: '2-digit', second: '2-digit' });
74
74
  }
75
75
 
76
- function buildSuccessLogPayload({ source, requestId, channel, model, originalModel, redirectedModel, tokens, cost = 0, timestamp = Date.now(), usageMissing = false }) {
76
+ function buildSuccessLogPayload({ source, requestId, channel, model, originalModel, redirectedModel, tokens, cacheUsageAvailable, cost = 0, timestamp = Date.now(), usageMissing = false }) {
77
77
  const normalized = normalizeUsageTokens(source, tokens);
78
78
  const payload = {
79
79
  type: 'log', status: 'success', id: requestId, time: formatRealtimeTime(timestamp), channel,
@@ -82,6 +82,7 @@ function buildSuccessLogPayload({ source, requestId, channel, model, originalMod
82
82
  reasoningTokens: normalized.reasoning, totalTokens: normalized.total, cost,
83
83
  source: normalizeToolSource(source), timestamp, usageMissing: Boolean(usageMissing)
84
84
  };
85
+ if (typeof cacheUsageAvailable === 'boolean') payload.cacheUsageAvailable = cacheUsageAvailable;
85
86
  if (originalModel) payload.originalModel = originalModel;
86
87
  if (redirectedModel) payload.redirectedModel = redirectedModel;
87
88
  return payload;
@@ -1 +0,0 @@
1
- import{i as e,s as a,t as l}from"./_plugin-vue_export-helper-BeLZJZij.js";import{C as t,D as s,K as n,O as i,S as o,V as u,W as d,Y as r,Z as c,_t as p,b as v,et as m,g,ht as f,m as y,mt as h,r as b,st as k,w,x as _}from"./vue-vendor-BqZbbXHA.js";import{t as C}from"./client-BgcNg4Bt.js";import{r as j,s as x,t as M}from"./platforms-mqqe_82d.js";import{$ as z,At as P,Nt as O,O as L,R as U,S as N,V as $,_ as I,f as A,g as F,j as S,m as R}from"./icons-B-AzZoeE.js";import{l as B}from"./light-C2X7hegj.js";import{a as D,c as q,i as E,o as T,r as H,s as V,t as G}from"./AssetPathField-jv-lopQ3.js";import{n as W,t as Y}from"./useResponsiveDrawer-C-UacYVP.js";import{t as J}from"./DrawerContent-ChXgZ1Xm.js";import{$ as X,Et as Z,I as K,L as Q,M as ee,T as ae,X as le,Y as te,Z as se,ct as ne,ft as ie,j as oe,k as ue,st as de,ut as re,w as ce}from"./index-Bw6YXbBA.js";import{t as pe}from"./MarkdownEditor-BXwTqbMo.js";async function ve(e=null,a="claude",l={}){const t=ce("agents",a,l.scope||"",e||"");return ae(t,l=>C.get("/agents",{params:e?{projectPath:e,platform:a}:{platform:a},signal:l}).then(e=>e.data),"agents",`agents:${a}`)}var me={class:"asset-card-main"},ge={class:"asset-card-top"},fe={class:"asset-card-title-stack"},ye={class:"asset-card-title-row"},he={class:"asset-name"},be={class:"asset-tags"},ke={key:0,class:"asset-description"},we={class:"asset-meta"},_e={key:0,class:"asset-meta-item"},Ce={key:1,class:"asset-meta-item"},je={class:"asset-card-actions"},xe={class:"asset-platform-strip"},Me=["onClick"],ze=l({__name:"AgentCard",props:{agent:{type:Object,required:!0},deleting:{type:Boolean,default:!1},registryInfo:{type:Object,default:null},toggling:{type:Boolean,default:!1}},emits:["click","edit","delete","toggle-enabled","toggle-platform"],setup(l,{emit:u}){const c=l,b=u,{byCapability:k}=Z(),C=v(()=>k("agents"));return(u,v)=>{return d(),w("div",{class:f(["agent-card asset-card",{"is-project":"project"===l.agent.scope,"is-managed":l.registryInfo}]),onClick:v[4]||(v[4]=e=>b("click",l.agent))},[_("div",me,[_("div",ge,[_("div",fe,[_("div",ye,[_("span",{class:f(["asset-status-dot",{active:l.registryInfo?.enabled||"project"===l.agent.scope}])},null,2),_("span",he,p(l.agent.name),1)]),_("div",be,[i(h(re),{type:"user"===l.agent.scope?"info":"success",size:"tiny",bordered:!1},{default:m(()=>[s(p("user"===l.agent.scope?"用户级":"项目级"),1)]),_:1},8,["type"]),l.registryInfo?(d(),o(h(re),{key:0,type:"success",size:"tiny",bordered:!1},{default:m(()=>[...v[5]||(v[5]=[s("托管",-1)])]),_:1})):t("",!0),l.agent.model?(d(),o(h(re),{key:1,type:"warning",size:"tiny",bordered:!1},{default:m(()=>[s(p(l.agent.model),1)]),_:1})):t("",!0)])])]),l.agent.description?(d(),w("div",ke,p((j=l.agent.description,j?j.length>80?j.slice(0,80)+"...":j:"")),1)):t("",!0),_("div",we,[l.agent.tools?(d(),w("span",_e,[i(h(e),{size:"12"},{default:m(()=>[i(h($))]),_:1}),s(" "+p((k=l.agent.tools,k?k.length>30?k.slice(0,30)+"...":k:"")),1)])):t("",!0),l.agent.permissionMode?(d(),w("span",Ce,[i(h(e),{size:"12"},{default:m(()=>[i(h(A))]),_:1}),s(" "+p(l.agent.permissionMode),1)])):t("",!0)])]),_("div",je,[l.registryInfo?(d(),w(g,{key:0},[i(h(te),{trigger:"hover"},{trigger:m(()=>[i(h(oe),{value:l.registryInfo.enabled,size:"small",loading:l.toggling,"onUpdate:value":v[0]||(v[0]=e=>b("toggle-enabled",l.agent,e)),onClick:v[1]||(v[1]=y(()=>{},["stop"]))},null,8,["value","loading"])]),default:m(()=>[s(" "+p(l.registryInfo.enabled?"已启用":"已禁用"),1)]),_:1}),_("div",xe,[(d(!0),w(g,null,n(C.value,a=>(d(),o(h(te),{key:a.key,trigger:"hover"},{trigger:m(()=>[_("span",{class:f(["asset-platform-icon",{active:l.registryInfo.platforms?.[a.key]}]),onClick:y(e=>b("toggle-platform",l.agent,a.key,!l.registryInfo.platforms?.[a.key]),["stop"])},[i(h(e),{size:"14"},{default:m(()=>[(d(),o(r(a.icon)))]),_:2},1024)],10,Me)]),default:m(()=>[s(" "+p(a.label||a.title||a.key)+" "+p(l.registryInfo.platforms?.[a.key]?"已启用":"未启用"),1)]),_:2},1024))),128))])],64)):(d(),w(g,{key:1},[i(h(a),{size:"tiny",tertiary:"",onClick:v[2]||(v[2]=y(e=>b("edit",l.agent),["stop"]))},{default:m(()=>[...v[6]||(v[6]=[s(" 编辑 ",-1)])]),_:1}),i(h(a),{size:"tiny",tertiary:"",type:"error",loading:c.deleting,onClick:v[3]||(v[3]=y(e=>b("delete",l.agent),["stop"]))},{default:m(()=>[...v[7]||(v[7]=[s(" 删除 ",-1)])]),_:1},8,["loading"])],64))])],2);var k,j}}},[["__scopeId","data-v-044e4192"]]),Pe={class:"asset-detail-drawer-header"},Oe={class:"asset-detail-identity"},Le={class:"asset-detail-heading"},Ue={class:"asset-detail-name"},Ne={class:"asset-detail-meta"},$e={key:2,class:"asset-detail-subtle"},Ie={class:"asset-detail-body"},Ae={key:0,class:"asset-detail-loading"},Fe={class:"asset-detail-container"},Se={class:"asset-detail-info-section"},Re={class:"asset-detail-description"},Be={class:"asset-detail-info-section"},De={class:"asset-detail-list"},qe={class:"asset-detail-row"},Ee={class:"asset-detail-value"},Te={key:0,class:"asset-detail-row"},He={class:"asset-detail-value mono"},Ve={key:1,class:"asset-detail-row"},Ge={class:"asset-detail-value mono"},We={key:2,class:"asset-detail-row"},Ye={class:"asset-detail-value"},Je={key:3,class:"asset-detail-row"},Xe={class:"asset-detail-value"},Ze={key:4,class:"asset-detail-row"},Ke={class:"asset-detail-value"},Qe={key:1,class:"asset-detail-info-section"},ea={class:"asset-detail-box"},aa={class:"asset-detail-code"},la={key:2,class:"asset-detail-empty"},ta={__name:"AgentDetailDrawer",props:{visible:{type:Boolean,default:!1},agent:{type:Object,default:null},platform:{type:String,default:"claude"},loading:{type:Boolean,default:!1}},emits:["update:visible"],setup(a,{emit:l}){const{drawerWidth:n}=Y(600),u=a,r=l,c=v({get:()=>u.visible,set:e=>r("update:visible",e)}),g={height:"100%",minHeight:0,overflow:"hidden"},f=v(()=>({claude:"Claude Code",codex:"Codex CLI",gemini:"Gemini CLI",opencode:"OpenCode"}[u.platform]||"")),y=v(()=>u.agent?.fileName?"codex"===u.platform?`[agents.${u.agent.fileName}]`:`${u.agent.fileName}.md`:""),b=v(()=>u.agent?"codex"===u.platform?u.agent.fullContent||"(无内容)":u.agent.systemPrompt||"(无内容)":"(无内容)"),k=v(()=>u.agent?.fullPath||u.agent?.path||"");function C(e){return"project"===e?"项目级":"用户级"}return(l,u)=>(d(),o(h(W),{show:c.value,"onUpdate:show":u[0]||(u[0]=e=>c.value=e),width:h(n),placement:"right","show-mask":!0},{default:m(()=>[i(h(J),{closable:"","native-scrollbar":!1,"body-content-style":g},{header:m(()=>[_("div",Pe,[_("div",Oe,[i(h(e),{size:20,class:"asset-detail-icon"},{default:m(()=>[i(h(L))]),_:1}),_("div",Le,[_("span",Ue,p(a.agent?.name||"代理详情"),1),_("div",Ne,[a.agent?.scope?(d(),o(h(re),{key:0,type:"project"===a.agent.scope?"success":"info",size:"tiny",bordered:!1},{default:m(()=>[s(p(C(a.agent.scope)),1)]),_:1},8,["type"])):t("",!0),a.agent?.model?(d(),o(h(re),{key:1,type:"warning",size:"tiny",bordered:!1},{default:m(()=>[s(p(a.agent.model),1)]),_:1})):t("",!0),f.value?(d(),w("span",$e,p(f.value),1)):t("",!0)])])])])]),default:m(()=>[_("div",Ie,[a.loading&&!a.agent?(d(),w("div",Ae,[i(h(ee),{size:"medium"}),u[1]||(u[1]=_("p",null,"加载代理详情中...",-1))])):a.agent?(d(),o(h(V),{key:1,type:"line",animated:"",class:"asset-detail-tabs"},{default:m(()=>[i(h(q),{name:"overview",tab:"概览"},{default:m(()=>[_("div",Fe,["codex"===a.platform&&a.agent.configReadError?(d(),o(h(ne),{key:0,type:"warning",bordered:!1},{default:m(()=>[s(p(a.agent.configReadError),1)]),_:1})):t("",!0),_("div",Se,[u[2]||(u[2]=_("h3",{class:"asset-detail-section-title"},"描述",-1)),_("p",Re,p(a.agent.description||"暂无描述"),1)]),_("div",Be,[u[9]||(u[9]=_("h3",{class:"asset-detail-section-title"},"基础信息",-1)),_("div",De,[_("div",qe,[u[3]||(u[3]=_("span",{class:"asset-detail-label"},"作用域",-1)),_("span",Ee,p(C(a.agent.scope)),1)]),a.agent.fileName?(d(),w("div",Te,[u[4]||(u[4]=_("span",{class:"asset-detail-label"},"文件名",-1)),_("span",He,p(y.value),1)])):t("",!0),"codex"===a.platform&&a.agent.configFile?(d(),w("div",Ve,[u[5]||(u[5]=_("span",{class:"asset-detail-label"},"config_file",-1)),_("span",Ge,p(a.agent.configFile),1)])):t("",!0),a.agent.model?(d(),w("div",We,[u[6]||(u[6]=_("span",{class:"asset-detail-label"},"模型",-1)),_("span",Ye,p(a.agent.model),1)])):t("",!0),a.agent.permissionMode?(d(),w("div",Je,[u[7]||(u[7]=_("span",{class:"asset-detail-label"},"权限模式",-1)),_("span",Xe,p(a.agent.permissionMode),1)])):t("",!0),a.agent.skills?(d(),w("div",Ze,[u[8]||(u[8]=_("span",{class:"asset-detail-label"},"技能",-1)),_("span",Ke,p(a.agent.skills),1)])):t("",!0)])]),a.agent.tools?(d(),w("div",Qe,[u[10]||(u[10]=_("h3",{class:"asset-detail-section-title"},"可用工具",-1)),_("div",ea,[i(h(e),{size:16},{default:m(()=>[i(h($))]),_:1}),_("code",null,p(a.agent.tools),1)])])):t("",!0),i(G,{path:k.value},null,8,["path"])])]),_:1}),i(h(q),{name:"content",tab:"codex"===a.platform?"配置":"提示词"},{default:m(()=>[_("pre",aa,p(b.value),1)]),_:1},8,["tab"])]),_:1})):(d(),w("div",la,[i(h(e),{size:44,class:"asset-detail-empty-icon"},{default:m(()=>[i(h(z))]),_:1}),u[11]||(u[11]=_("p",null,"未选择代理",-1))]))])]),_:1})]),_:1},8,["show","width"]))}},sa={class:"config-edit-header"},na={class:"config-edit-identity"},ia={class:"config-edit-icon"},oa={class:"config-edit-title-stack"},ua={class:"config-edit-kicker"},da={class:"config-edit-title"},ra={class:"config-edit-badges"},ca={class:"config-edit-section"},pa={class:"section-heading"},va={class:"field-grid"},ma={class:"scope-option"},ga={class:"scope-path"},fa={class:"scope-option"},ya={class:"scope-path"},ha={key:0,class:"platform-note"},ba={class:"config-edit-section"},ka={class:"section-heading"},wa={key:1,class:"empty-config-box"},_a={key:0,class:"field-grid"},Ca={class:"field-grid"},ja={key:0,class:"config-edit-section content-section"},xa={class:"section-heading"},Ma={class:"modal-footer"},za=l({__name:"AgentFormModal",props:{visible:{type:Boolean,default:!1},agent:{type:Object,default:null},projectPath:{type:String,default:null},platform:{type:String,default:"claude"}},emits:["update:visible","saved"],setup(l,{emit:n}){const u=l,r=n,y=v({get:()=>u.visible,set:e=>r("update:visible",e)}),b=v(()=>!!u.agent),j=v(()=>"codex"===u.platform),x=v(()=>"claude"===u.platform),M=v(()=>"omp"===u.platform),P=v(()=>["codex","opencode","omp"].includes(u.platform)),O=v(()=>b.value?"编辑代理":"创建代理"),N=v(()=>({claude:"Claude Code",codex:"Codex CLI",gemini:"Gemini CLI",opencode:"OpenCode",omp:"OMP"}[u.platform]||"Claude Code")),$=v(()=>"codex"===u.platform?"~/.codex/config.toml":"opencode"===u.platform?"~/.config/opencode/agents/":"gemini"===u.platform?"~/.gemini/agents/":"omp"===u.platform?"~/.omp/agent/agents/":"~/.claude/agents/"),A=v(()=>"opencode"===u.platform?".opencode/agents/":"gemini"===u.platform?".gemini/agents/":"omp"===u.platform?".omp/agents/":".claude/agents/"),F=v(()=>"project"===Y.value.scope?"项目级":"用户级"),D=v(()=>W.find(e=>e.value===Y.value.configMode)?.label||"配置"),q=k(null),E=k(!1),T={maxHeight:"calc(92vh - 134px)",overflowY:"auto",paddingBottom:"18px"},H=[{label:"Sonnet",value:"sonnet"},{label:"Opus",value:"opus"},{label:"Haiku",value:"haiku"},{label:"继承 (inherit)",value:"inherit"}],V=[{label:"默认 (default)",value:"default"},{label:"宽松 (permissive)",value:"permissive"},{label:"严格 (strict)",value:"strict"}],G=[{label:"Off",value:"off"},{label:"Minimal",value:"minimal"},{label:"Low",value:"low"},{label:"Medium",value:"medium"},{label:"High",value:"high"},{label:"XHigh",value:"xhigh"},{label:"Max",value:"max"}],W=[{label:"仅描述",value:"none"},{label:"托管 TOML",value:"managed"},{label:"自定义路径",value:"custom"}],Y=k({fileName:"",scope:"user",name:"",description:"",configMode:"none",configFile:"",configContent:"",tools:"",model:"",thinkingLevel:"",spawns:"",permissionMode:"",skills:"",systemPrompt:""}),J=v(()=>{const e={fileName:{required:!0,message:"请输入文件名",trigger:"blur"},scope:{required:!0,message:"请选择作用域",trigger:"change"},description:{required:!0,message:"请输入代理描述",trigger:"blur"}};return x.value&&(e.name={required:!0,message:"请输入代理名称",trigger:"blur"}),j.value&&(e.configMode={required:!0,message:"请选择配置模式",trigger:"change"},e.configFile={required:"custom"===Y.value.configMode,message:"请输入自定义配置文件路径",trigger:"blur"}),e});function Z(){Y.value={fileName:"",scope:"user",name:"",description:"",configMode:"none",configFile:"",configContent:"",tools:"",model:"",thinkingLevel:"",spawns:"",permissionMode:"",skills:"",systemPrompt:""}}function ee(){y.value=!1,Z()}async function ae(){try{await(q.value?.validate())}catch{return}E.value=!0;try{const e={...Y.value,projectPath:u.projectPath};j.value?("custom"===Y.value.configMode?e.configFile=Y.value.configFile:delete e.configFile,"managed"===Y.value.configMode||"custom"===Y.value.configMode?e.configContent=Y.value.configContent||"":delete e.configContent):(delete e.configMode,delete e.configFile,delete e.configContent),b.value?(await async function(e,a,l,t="claude"){return(await C.put(`/agents/${a}/${e}`,{...l,platform:t})).data}(Y.value.fileName,Y.value.scope,e,u.platform),ue.success("代理更新成功")):(await async function(e,a="claude"){return(await C.post("/agents",{...e,platform:a})).data}(e,u.platform),ue.success("代理创建成功")),r("saved"),y.value=!1,Z()}catch(e){ue.error("操作失败: "+e.message)}finally{E.value=!1}}return c(()=>u.agent,e=>{if(e){const a=e.configMode||(e.configFile?String(e.configFile).includes("/.codex/agents/")?"managed":"custom":"none");Y.value={fileName:e.fileName||"",scope:e.scope||"user",name:e.name||"",description:e.description||"",configMode:a,configFile:e.configFile||"",configContent:e.fullContent||"",tools:e.tools||"",model:e.model||"",thinkingLevel:e.thinkingLevel||"",spawns:e.spawns||"",permissionMode:e.permissionMode||"",skills:e.skills||"",systemPrompt:e.systemPrompt||""}}else Z()},{immediate:!0}),c(()=>u.platform,e=>{"codex"===e&&(Y.value.scope="user",Y.value.configMode||(Y.value.configMode="none"))},{immediate:!0}),(l,n)=>(d(),o(h(B),{show:y.value,"onUpdate:show":n[15]||(n[15]=e=>y.value=e),preset:"card",title:O.value,style:{width:"min(940px, 96vw)","max-height":"92vh"},"content-style":T,"mask-closable":!1,class:"config-edit-modal"},{footer:m(()=>[_("div",Ma,[i(h(a),{onClick:ee},{default:m(()=>[...n[23]||(n[23]=[s("取消",-1)])]),_:1}),i(h(a),{type:"primary",loading:E.value,onClick:ae},{icon:m(()=>[i(h(e),null,{default:m(()=>[i(h(I))]),_:1})]),default:m(()=>[s(" "+p(b.value?"保存":"创建"),1)]),_:1},8,["loading"])])]),default:m(()=>[_("div",sa,[_("div",na,[_("span",ia,[i(h(e),{size:22},{default:m(()=>[i(h(L))]),_:1})]),_("div",oa,[_("span",ua,p(N.value),1),_("span",da,p(b.value?Y.value.fileName||"编辑代理":"创建新代理"),1)])]),_("div",ra,[i(h(re),{size:"small",bordered:!1,type:"info"},{default:m(()=>[s(p(F.value),1)]),_:1}),j.value?(d(),o(h(re),{key:0,size:"small",bordered:!1,type:"success"},{default:m(()=>[...n[16]||(n[16]=[s("Codex 用户级",-1)])]),_:1})):t("",!0),j.value&&"none"!==Y.value.configMode?(d(),o(h(re),{key:1,size:"small",bordered:!1,type:"warning"},{default:m(()=>[s(p(D.value),1)]),_:1})):t("",!0)])]),i(h(Q),{ref_key:"formRef",ref:q,model:Y.value,rules:J.value,"label-placement":"top","require-mark-placement":"right-hanging",class:"config-edit-form"},{default:m(()=>[_("div",{class:f(["config-edit-grid",{"is-codex":j.value}])},[_("section",ca,[_("div",pa,[i(h(e),{size:16},{default:m(()=>[i(h(S))]),_:1}),n[17]||(n[17]=_("span",null,"基础信息",-1))]),_("div",va,[i(h(K),{label:"文件名",path:"fileName"},{default:m(()=>[i(h(de),{value:Y.value.fileName,"onUpdate:value":n[0]||(n[0]=e=>Y.value.fileName=e),disabled:b.value,placeholder:"code-reviewer"},null,8,["value","disabled"])]),_:1}),x.value?(d(),o(h(K),{key:0,label:"显示名称",path:"name"},{default:m(()=>[i(h(de),{value:Y.value.name,"onUpdate:value":n[1]||(n[1]=e=>Y.value.name=e),placeholder:"代理的显示名称"},null,8,["value"])]),_:1})):t("",!0)]),i(h(K),{label:"作用域",path:"scope"},{default:m(()=>[i(h(le),{value:Y.value.scope,"onUpdate:value":n[2]||(n[2]=e=>Y.value.scope=e),disabled:b.value,class:f(["scope-picker",{"single-option":j.value}])},{default:m(()=>[i(h(se),{value:"user"},{default:m(()=>[_("span",ma,[n[18]||(n[18]=_("span",{class:"scope-title"},"用户级",-1)),_("span",ga,p($.value),1)])]),_:1}),j.value?t("",!0):(d(),o(h(se),{key:0,value:"project"},{default:m(()=>[_("span",fa,[n[19]||(n[19]=_("span",{class:"scope-title"},"项目级",-1)),_("span",ya,p(A.value),1)])]),_:1}))]),_:1},8,["value","disabled","class"])]),_:1}),i(h(K),{label:"描述",path:"description"},{default:m(()=>[i(h(de),{value:Y.value.description,"onUpdate:value":n[3]||(n[3]=e=>Y.value.description=e),type:"textarea",autosize:{minRows:3,maxRows:5},placeholder:"一句话说明这个代理什么时候该被调用"},null,8,["value"])]),_:1}),j.value?(d(),w("div",ha,[i(h(e),{size:16},{default:m(()=>[i(h(U))]),_:1}),n[20]||(n[20]=_("span",null,[s("Codex agents 写入用户级配置。描述会进入 "),_("code",null,"~/.codex/config.toml"),s(",TOML 配置可托管到 "),_("code",null,"~/.codex/agents/"),s("。")],-1))])):t("",!0)]),_("section",ba,[_("div",ka,[i(h(e),{size:16},{default:m(()=>[i(h(R))]),_:1}),_("span",null,p(j.value?"运行配置":"模型与权限"),1)]),j.value?(d(),w(g,{key:0},[i(h(K),{label:"配置模式",path:"configMode"},{default:m(()=>[i(h(X),{value:Y.value.configMode,"onUpdate:value":n[4]||(n[4]=e=>Y.value.configMode=e),options:W,placeholder:"选择配置模式"},null,8,["value"])]),_:1}),"custom"===Y.value.configMode?(d(),o(h(K),{key:0,label:"配置文件路径",path:"configFile"},{default:m(()=>[i(h(de),{value:Y.value.configFile,"onUpdate:value":n[5]||(n[5]=e=>Y.value.configFile=e),placeholder:"~/.omx/agents/my-agent.toml"},null,8,["value"])]),_:1})):t("",!0),"none"===Y.value.configMode?(d(),w("div",wa,[i(h(e),{size:18},{default:m(()=>[i(h(z))]),_:1}),n[21]||(n[21]=_("span",null,"仅保存代理描述,不额外写入模型或工具配置。",-1))])):(d(),o(h(K),{key:2,label:"Agent Config (TOML)",path:"configContent",class:"editor-form-item"},{default:m(()=>[i(h(de),{value:Y.value.configContent,"onUpdate:value":n[6]||(n[6]=e=>Y.value.configContent=e),type:"textarea",autosize:{minRows:14,maxRows:22},class:"toml-editor",placeholder:'model = "gpt-5.4"'},null,8,["value"])]),_:1}))],64)):(d(),w(g,{key:1},[i(h(K),{label:"可用工具",path:"tools"},{default:m(()=>[i(h(de),{value:Y.value.tools,"onUpdate:value":n[7]||(n[7]=e=>Y.value.tools=e),placeholder:M.value?"read, edit, bash(逗号分隔)":"Read, Edit, Bash"},null,8,["value","placeholder"])]),_:1}),i(h(K),{label:"模型",path:"model"},{default:m(()=>[P.value?(d(),o(h(de),{key:0,value:Y.value.model,"onUpdate:value":n[8]||(n[8]=e=>Y.value.model=e),placeholder:M.value?"@default、@review 或 provider/model;多个候选用逗号分隔":"gpt-5, claude-sonnet-4, openrouter/model"},null,8,["value","placeholder"])):(d(),o(h(X),{key:1,value:Y.value.model,"onUpdate:value":n[9]||(n[9]=e=>Y.value.model=e),options:H,placeholder:"选择模型",clearable:""},null,8,["value"]))]),_:1}),M.value?(d(),w("div",_a,[i(h(K),{label:"思考级别",path:"thinkingLevel"},{default:m(()=>[i(h(X),{value:Y.value.thinkingLevel,"onUpdate:value":n[10]||(n[10]=e=>Y.value.thinkingLevel=e),options:G,placeholder:"使用模型默认级别",clearable:""},null,8,["value"])]),_:1}),i(h(K),{label:"可调用的子 Agent",path:"spawns"},{default:m(()=>[i(h(de),{value:Y.value.spawns,"onUpdate:value":n[11]||(n[11]=e=>Y.value.spawns=e),placeholder:"* 或 reviewer, scout"},null,8,["value"])]),_:1})])):t("",!0),_("div",Ca,["opencode"===u.platform||M.value?t("",!0):(d(),o(h(K),{key:0,label:"权限模式",path:"permissionMode"},{default:m(()=>[i(h(X),{value:Y.value.permissionMode,"onUpdate:value":n[12]||(n[12]=e=>Y.value.permissionMode=e),options:V,placeholder:"选择权限模式",clearable:""},null,8,["value"])]),_:1})),"opencode"===u.platform||M.value?t("",!0):(d(),o(h(K),{key:1,label:"技能",path:"skills"},{default:m(()=>[i(h(de),{value:Y.value.skills,"onUpdate:value":n[13]||(n[13]=e=>Y.value.skills=e),placeholder:"自动加载的技能,逗号分隔"},null,8,["value"])]),_:1}))])],64))]),j.value?t("",!0):(d(),w("section",ja,[_("div",xa,[i(h(e),{size:16},{default:m(()=>[i(h(z))]),_:1}),n[22]||(n[22]=_("span",null,"系统提示词",-1))]),i(h(K),{path:"systemPrompt","show-label":!1,class:"editor-form-item"},{default:m(()=>[i(pe,{modelValue:Y.value.systemPrompt,"onUpdate:modelValue":n[14]||(n[14]=e=>Y.value.systemPrompt=e),rows:10,"min-height":240,placeholder:"定义代理的行为、边界和输出要求","default-editing":!b.value},null,8,["modelValue","default-editing"])]),_:1})]))],2)]),_:1},8,["model","rules"])]),_:1},8,["show","title"]))}},[["__scopeId","data-v-ea89272f"]]),Pa={key:0,class:"asset-panel-header"},Oa={class:"asset-title-group"},La={class:"asset-heading"},Ua={class:"asset-title-row"},Na={class:"asset-platform-pill"},$a={key:0,class:"asset-action-row"},Ia={key:1,class:"asset-drawer-toolbar"},Aa={key:0,class:"asset-action-row"},Fa={key:2,class:"omp-model-roles"},Sa={class:"omp-model-roles-toolbar"},Ra={class:"omp-model-roles-copy"},Ba={key:0,class:"omp-model-roles-stale"},Da={key:1,class:"omp-model-roles-error"},qa={class:"asset-summary"},Ea={class:"asset-summary-item"},Ta={class:"asset-summary-value"},Ha={class:"asset-summary-item"},Va={class:"asset-summary-value"},Ga={class:"asset-summary-item"},Wa={class:"asset-summary-value"},Ya={class:"asset-summary-item"},Ja={class:"asset-summary-value"},Xa={class:"asset-filter-bar"},Za={class:"asset-panel-content"},Ka={key:0,class:"asset-empty"},Qa={key:1,class:"asset-list"},el={class:"asset-footer"},al=l({__name:"AgentsPanel",props:{hideBack:{type:Boolean,default:!1},inDrawer:{type:Boolean,default:!1},projectPath:{type:String,default:null},platform:{type:String,default:""}},emits:["back","updated"],setup(l,{emit:r}){const y=l,z=r,$=M(),I=b(),A=k([]),S=k(!1),R=k(""),B=k("all"),q=k(!1),V=k(!1),G=k(null),W=k(!1),Y=k(null),J=k({}),K=k({}),Q=k({}),le=k([]),te=k(""),se=k([]),ne=k([]),oe=v(()=>new Set(se.value.map(e=>e.value))),re=k(!1),pe=k(!1),me=k(""),ge=v(()=>JSON.stringify(_e())!==te.value);let fe=0;const{byCapability:ye}=Z(),he=v(()=>ye("agents").map(e=>e.key)),be=v(()=>he.value.includes(xe.value)),ke=["default","smol","slow","plan","commit","vision","designer","task","advisor","tiny"],we=["minimal","low","medium","high","xhigh","max"].map(e=>({label:e,value:e}));function _e(){return Object.fromEntries(le.value.map(e=>[e.key,{model:e.model||"",thinkingLevel:e.thinkingLevel||""}]))}function Ce(e={}){const a=e.roles&&"object"==typeof e.roles?e.roles:{};le.value=ke.map(e=>({key:e,model:"string"==typeof a[e]?.model?a[e].model:"",thinkingLevel:"string"==typeof a[e]?.thinkingLevel?a[e].thinkingLevel:""})),te.value=JSON.stringify(_e()),se.value=(Array.isArray(e.modelOptions)?e.modelOptions:[]).filter(e=>"string"==typeof e&&e.trim()).map(e=>({label:e,value:e})),ne.value=e.unavailableRoles&&"object"==typeof e.unavailableRoles?Object.keys(e.unavailableRoles).filter(a=>!0===e.unavailableRoles[a]):[]}function je(e,a){return e?.data?.error||e?.response?.data?.error||e?.message||a}const xe=v(()=>String(y.platform||j(I)||"").trim().toLowerCase()),Me=v(()=>"opencode"===xe.value?"使用 @agent 或 Task 在 OpenCode 中调用":"omp"===xe.value?"OMP Agent 由 task 工具按名称调用;模型可在 Agent 配置中单独指定":"gemini"===xe.value?"使用 @agent 调用 Gemini 自定义代理":"使用 Task tool 调用自定义代理"),Pe=v(()=>{const e=$.get(xe.value)||x(xe.value);return e.label||e.title||"Claude Code"}),Oe=[{label:"全部",value:"all"},{label:"用户级",value:"user"},{label:"项目级",value:"project"},{label:"已托管",value:"managed"}],Le=v(()=>A.value.filter(e=>"user"===e.scope).length),Ue=v(()=>A.value.filter(e=>"project"===e.scope).length),Ne=v(()=>Object.keys(K.value).length);function $e(e){if(!e)return"";const a="string"==typeof e.scope&&e.scope.trim()?e.scope.trim():"unknown",l="string"==typeof e.path&&e.path.trim()?e.path.trim():"",t="string"==typeof e.fileName&&e.fileName.trim()?e.fileName.trim():"";return`${a}:${l||t}`}function Ie(e){if(!e)return"";const a=[];if("string"==typeof e.path&&e.path.trim()&&a.push(e.path.trim()),"string"==typeof e.fileName&&e.fileName.trim()){const l=e.fileName.trim();a.push(`${l}.md`,l)}"string"==typeof e.name&&e.name.trim()&&a.push(e.name.trim());for(const l of a)if(K.value[l])return l;return a[0]||""}const Ae=v(()=>{let e=A.value;if("user"===B.value?e=e.filter(e=>"user"===e.scope):"project"===B.value?e=e.filter(e=>"project"===e.scope):"managed"===B.value&&(e=e.filter(e=>K.value[Ie(e)])),R.value.trim()){const a=R.value.toLowerCase();e=e.filter(e=>e.name?.toLowerCase().includes(a)||e.description?.toLowerCase().includes(a)||e.fileName?.toLowerCase().includes(a))}return e}),Fe=v(()=>R.value?"没有匹配的代理":"user"===B.value?"暂无用户级代理":"project"===B.value?"暂无项目级代理":"managed"===B.value?"暂无托管的代理":be.value?"暂无自定义代理":`${Pe.value} 暂未提供 Agents 能力`);async function Se(){if(!be.value)return A.value=[],K.value={},void(S.value=!1);S.value=!0;try{const[e,a]=await Promise.all([ve(y.projectPath,xe.value),H("agents",{platform:xe.value,projectPath:y.projectPath})]);if(e.success&&(A.value=e.agents||[]),a.success){K.value={};for(const[e,l]of Object.entries(a.items||{}))K.value[e]=l}}catch(e){ue.error("加载代理列表失败: "+e.message)}finally{S.value=!1}}async function Re(e,a){const l=Ie(e);if(l){Q.value[l]=!0;try{(await D("agents",l,a)).success&&(ue.success(a?"已启用":"已禁用"),await Se())}catch(t){ue.error("切换失败: "+t.message)}finally{delete Q.value[l]}}else ue.error("未找到代理对应的托管配置键")}async function Be(e,a,l){const t=Ie(e);if(t){Q.value[t]=!0;try{(await T("agents",t,a,l)).success&&(ue.success(`${a} ${l?"已启用":"已禁用"}`),await Se())}catch(s){ue.error("切换失败: "+s.message)}finally{delete Q.value[t]}}else ue.error("未找到代理对应的托管配置键")}async function De(){S.value=!0;try{const e=await E("agents");e?.success||ue.warning(e?.message||"同步失败,已继续刷新列表")}catch(e){ue.warning("同步失败,已继续刷新列表: "+e.message)}await Se()}async function qe(){if("omp"===xe.value){re.value=!0,me.value="";try{const{data:e}=await C.get("/settings/omp-model-roles");Ce(e)}catch(e){me.value=`加载 OMP 角色配置失败:${je(e,"未知错误")}`}finally{re.value=!1}}}async function Ee(){if(!(re.value||pe.value||me.value)){pe.value=!0;try{const{data:e}=await C.put("/settings/omp-model-roles",{roles:_e()});Ce(e),ue.success("OMP 模型角色配置已保存")}catch(e){ue.error(`保存 OMP 角色配置失败:${je(e,"未知错误")}`)}finally{pe.value=!1}}}function Te(){Y.value=null,q.value=!0}function He(e){q.value=e,e||(Y.value=null)}function Ve(e){We(e,!0)}async function Ge(e){const a=$e(e);J.value[a]=!0;try{(await async function(e,a,l=null,t="claude"){const s={};return t&&(s.platform=t),l&&(s.projectPath=l),(await C.delete(`/agents/${a}/${e}`,{params:s})).data}(e.fileName,e.scope,y.projectPath,xe.value)).success&&(ue.success("代理已删除"),await Se(),z("updated"))}catch(l){ue.error("删除失败: "+l.message)}finally{delete J.value[a]}}async function We(e,a=!1){if(!e)return;const l=++fe;W.value=!0,a||(G.value=null,V.value=!0);try{const t=await async function(e,a,l=null,t="claude"){const s={};t&&(s.platform=t),l&&(s.projectPath=l);const n=ce(`agent-detail:${e}`,t,a||"",l||"");return ae(n,l=>C.get(`/agents/${a}/${e}`,{params:s,signal:l}).then(e=>e.data),"agent-detail",`agent-detail:${t}`)}(e.fileName,e.scope,y.projectPath,xe.value);if(l!==fe)return;const s=t?.agent||e;a?(Y.value=s,q.value=!0):G.value=s}catch(t){if(l!==fe)return;ue.error("加载代理详情失败: "+t.message),a?(Y.value=e,q.value=!0):G.value=e}finally{l===fe&&(W.value=!1)}}function Ye(e){We(e,!1)}function Je(){Y.value=null,Se(),z("updated")}function Xe(){z("back")}return u(()=>{Se(),qe()}),c(xe,()=>{fe+=1,W.value=!1,G.value=null,Se(),qe()}),(u,r)=>(d(),w("div",{class:f(["agents-panel asset-panel",{"in-drawer":y.inDrawer}])},[y.inDrawer?t("",!0):(d(),w("div",Pa,[_("div",Oa,[y.hideBack?t("",!0):(d(),o(h(a),{key:0,text:"",onClick:Xe,class:"back-btn"},{icon:m(()=>[i(h(e),null,{default:m(()=>[i(h(P))]),_:1})]),_:1})),_("div",La,[_("div",Ua,[r[3]||(r[3]=_("span",{class:"asset-title"},"代理管理",-1)),_("span",Na,p(Pe.value),1)]),r[4]||(r[4]=_("div",{class:"asset-subtitle"},"维护自定义代理、工具权限和平台启用状态",-1))])]),be.value?(d(),w("div",$a,[i(h(a),{text:"",onClick:Te,class:"action-btn"},{icon:m(()=>[i(h(e),null,{default:m(()=>[i(h(O))]),_:1})]),default:m(()=>[r[5]||(r[5]=s(" 创建 ",-1))]),_:1}),i(h(a),{text:"",onClick:De,loading:S.value,class:"action-btn"},{icon:m(()=>[i(h(e),null,{default:m(()=>[i(h(N))]),_:1})]),default:m(()=>[r[6]||(r[6]=s(" 刷新 ",-1))]),_:1},8,["loading"])])):t("",!0)])),y.inDrawer?(d(),w("div",Ia,[be.value?(d(),w("div",Aa,[i(h(a),{text:"",onClick:Te,class:"action-btn"},{icon:m(()=>[i(h(e),null,{default:m(()=>[i(h(O))]),_:1})]),default:m(()=>[r[7]||(r[7]=s(" 创建 ",-1))]),_:1}),i(h(a),{text:"",onClick:De,loading:S.value,class:"action-btn"},{icon:m(()=>[i(h(e),null,{default:m(()=>[i(h(N))]),_:1})]),default:m(()=>[r[8]||(r[8]=s(" 刷新 ",-1))]),_:1},8,["loading"])])):t("",!0)])):t("",!0),"omp"===xe.value?(d(),w("div",Fa,[_("div",Sa,[_("div",Ra,[r[10]||(r[10]=_("strong",null,"OMP 模型角色",-1)),r[11]||(r[11]=_("span",null,"候选模型只取当前启用渠道。原生 OAuth 渠道读取允许模型列表(未设置时读取已声明模型),并使用 OAuth provider ID 作为前缀;OAuth Auth Gateway 使用该托管渠道的模型配置。思考强度会写入选择器后缀,例如 :high。",-1)),ne.value.length?(d(),w("span",Ba," 以下角色当前指向未启用渠道模型:"+p(ne.value.join("、"))+"。请切换到已启用渠道中的模型,或清空对应角色。 ",1)):t("",!0),me.value?(d(),w("div",Da,[_("span",null,p(me.value),1),i(h(a),{text:"",size:"tiny",disabled:re.value,onClick:qe},{default:m(()=>[...r[9]||(r[9]=[s("重试",-1)])]),_:1},8,["disabled"])])):t("",!0)]),i(h(a),{type:"primary",size:"small",loading:pe.value,disabled:re.value||!!me.value||pe.value||!ge.value,onClick:Ee},{default:m(()=>[...r[12]||(r[12]=[s(" 保存角色配置 ",-1)])]),_:1},8,["loading","disabled"])]),r[13]||(r[13]=_("div",{class:"omp-model-roles-header"},[_("span",null,"角色"),_("span",null,"模型"),_("span",null,"思考强度")],-1)),(d(!0),w(g,null,n(le.value,e=>(d(),w("div",{key:e.key,class:"omp-model-role-row"},[_("strong",null,p(e.key),1),i(h(X),{value:e.model,"onUpdate:value":a=>e.model=a,options:se.value,placeholder:"请选择已启用渠道模型",filterable:"",clearable:"",size:"small",disabled:re.value||!!me.value||pe.value},null,8,["value","onUpdate:value","options","disabled"]),i(h(X),{value:e.thinkingLevel,"onUpdate:value":a=>e.thinkingLevel=a,options:h(we),placeholder:"跟随默认",clearable:"",size:"small",disabled:re.value||!!me.value||pe.value||!oe.value.has(e.model)},null,8,["value","onUpdate:value","options","disabled"])]))),128))])):t("",!0),_("div",qa,[_("span",Ea,[r[14]||(r[14]=_("span",{class:"asset-summary-label"},"全部",-1)),_("span",Ta,p(A.value.length),1)]),_("span",Ha,[r[15]||(r[15]=_("span",{class:"asset-summary-label"},"用户级",-1)),_("span",Va,p(Le.value),1)]),_("span",Ga,[r[16]||(r[16]=_("span",{class:"asset-summary-label"},"项目级",-1)),_("span",Wa,p(Ue.value),1)]),_("span",Ya,[r[17]||(r[17]=_("span",{class:"asset-summary-label"},"托管",-1)),_("span",Ja,p(Ne.value),1)])]),_("div",Xa,[i(h(de),{value:R.value,"onUpdate:value":r[0]||(r[0]=e=>R.value=e),placeholder:"搜索代理、描述或文件名",clearable:"",size:"small",class:"asset-search"},{prefix:m(()=>[i(h(e),null,{default:m(()=>[i(h(F))]),_:1})]),_:1},8,["value"]),i(h(X),{value:B.value,"onUpdate:value":r[1]||(r[1]=e=>B.value=e),options:Oe,size:"small",class:"asset-filter"},null,8,["value"])]),_("div",Za,[i(h(ee),{show:S.value},{default:m(()=>[0!==Ae.value.length||S.value?(d(),w("div",Qa,[(d(!0),w(g,null,n(Ae.value,e=>(d(),o(ze,{key:$e(e),agent:e,deleting:!!J.value[$e(e)],"registry-info":K.value[Ie(e)],toggling:!!Q.value[Ie(e)],onEdit:Ve,onDelete:Ge,onClick:Ye,onToggleEnabled:Re,onTogglePlatform:Be},null,8,["agent","deleting","registry-info","toggling"]))),128))])):(d(),w("div",Ka,[i(h(ie),{description:Fe.value},{icon:m(()=>[i(h(e),{size:"48",color:"var(--text-quaternary)"},{default:m(()=>[i(h(L))]),_:1})]),extra:m(()=>[be.value?(d(),o(h(a),{key:0,size:"small",onClick:Te},{default:m(()=>[...r[18]||(r[18]=[s(" 创建第一个代理 ",-1)])]),_:1})):t("",!0)]),_:1},8,["description"])]))]),_:1},8,["show"])]),_("div",el,[i(h(e),{size:"14",class:"asset-info-icon"},{default:m(()=>[i(h(U))]),_:1}),_("span",null,p(Me.value),1)]),i(za,{visible:q.value,"onUpdate:visible":He,agent:Y.value,"project-path":l.projectPath,platform:xe.value,onSaved:Je},null,8,["visible","agent","project-path","platform"]),i(ta,{visible:V.value,"onUpdate:visible":r[2]||(r[2]=e=>V.value=e),agent:G.value,platform:xe.value,loading:W.value},null,8,["visible","agent","platform","loading"])],2))}},[["__scopeId","data-v-393fa5d6"]]),ll=l({__name:"AgentsDrawer",props:{visible:Boolean,platform:{type:String,default:""}},emits:["update:visible"],setup(e,{emit:a}){const l=e,t=a,{drawerWidth:s}=Y(720),n=v({get:()=>l.visible,set:e=>t("update:visible",e)}),u={padding:0,display:"flex",flexDirection:"column",overflow:"hidden"};return(e,a)=>(d(),o(h(W),{show:n.value,"onUpdate:show":a[1]||(a[1]=e=>n.value=e),width:h(s),placement:"right","mask-closable":!0},{default:m(()=>[i(h(J),{closable:"","native-scrollbar":!1,"body-content-style":u},{header:m(()=>[...a[2]||(a[2]=[_("div",{class:"drawer-header"},[_("span",null,"代理管理")],-1)])]),default:m(()=>[i(al,{onBack:a[0]||(a[0]=e=>n.value=!1),"hide-back":!0,"in-drawer":!0,platform:l.platform},null,8,["platform"])]),_:1})]),_:1},8,["show","width"]))}},[["__scopeId","data-v-b9b7a7fa"]]);export{ll as default};
@@ -1 +0,0 @@
1
- .drawer-header[data-v-27295eec]{align-items:center;gap:8px;display:flex}.header-icon-wrapper[data-v-27295eec]{background:var(--bg-secondary);border:1px solid var(--border-primary);width:28px;height:28px;color:var(--primary-color);border-radius:6px;justify-content:center;align-items:center;display:flex}.convert-container[data-v-27295eec]{flex-direction:column;gap:10px;padding-bottom:8px;display:flex}.action-row[data-v-27295eec]{justify-content:space-between;align-items:center;gap:8px;display:flex}.result-section[data-v-27295eec]{border:1px solid var(--border-primary);background:var(--bg-secondary);border-radius:8px;padding:12px}.result-meta[data-v-27295eec]{flex-wrap:wrap;gap:10px 14px;display:flex}.meta-row[data-v-27295eec]{align-items:center;gap:6px;display:flex}.meta-label[data-v-27295eec]{color:var(--text-secondary);font-size:12px}.warning-title[data-v-27295eec]{margin-bottom:4px;font-size:12px;font-weight:600}.warning-list[data-v-27295eec]{margin:0;padding-left:16px;font-size:12px;line-height:1.5}
@@ -1 +0,0 @@
1
- import{i as e,s as a,t}from"./_plugin-vue_export-helper-BeLZJZij.js";import{C as s,D as l,K as o,O as n,S as r,W as u,Z as i,_t as c,b as p,et as d,g as v,mt as m,st as y,w as f,x as w}from"./vue-vendor-BqZbbXHA.js";import{n as g}from"./vendors-D0c_0KLP.js";import{H as h}from"./icons-B-AzZoeE.js";import{n as x,t as b}from"./useResponsiveDrawer-C-UacYVP.js";import{t as _}from"./DrawerContent-ChXgZ1Xm.js";import{$ as C,H as A,I as k,L as O,ct as S,k as j,st as J,ut as N}from"./index-Bw6YXbBA.js";var T="http://localhost:19999";async function z(e){return(await g.post(`${T}/api/convert/opencode/claude`,e)).data}async function R(e){return(await g.post(`${T}/api/convert/opencode/codex`,e)).data}async function I(e){return(await g.post(`${T}/api/convert/opencode/gemini`,e)).data}var $={class:"drawer-header"},q={class:"header-icon-wrapper"},B={class:"convert-container"},U={class:"action-row"},D={key:1,class:"result-section"},P={class:"result-meta"},Y={class:"meta-row"},E={class:"meta-row"},G={class:"meta-row"},H={class:"warning-list"},W={class:"action-row"},K=t({__name:"GatewayConvertDrawer",props:{visible:{type:Boolean,default:!1}},emits:["update:visible"],setup(t,{emit:K}){const{drawerWidth:L}=b(620,520),Z=t,F=K,M=p({get:()=>Z.visible,set:e=>F("update:visible",e)}),Q=y([{label:"Claude Code",value:"claude"},{label:"Codex",value:"codex"},{label:"Gemini",value:"gemini"}]),V=y([{label:"Responses API (/v1/responses)",value:"responses"},{label:"Chat Completions (/v1/chat/completions)",value:"chat.completions"}]),X=y("claude"),ee=y("responses"),ae=y(""),te=y(!1),se=y(null),le=y(""),oe=y(!1),ne={claude:{model:"claude-sonnet-4-5",system:[{type:"text",text:"You are a concise assistant."}],messages:[{role:"user",content:[{type:"text",text:"请用三点总结这个需求。"}]}],max_tokens:256,stream:!1},codex:{model:"gpt-5-codex",instructions:"You are Codex.",input:[{type:"message",role:"user",content:[{type:"input_text",text:"ping"}]}],stream:!1,store:!1},gemini:{model:"gemini-2.5-pro",system_instruction:{parts:[{text:"You are a concise assistant."}]},contents:[{role:"user",parts:[{text:"请将以下文本转换为要点列表。"}]}],generationConfig:{maxOutputTokens:256,temperature:.2}}},re=p(()=>se.value?.requestBody?JSON.stringify(se.value.requestBody,null,2):"");function ue(){const e=ne[X.value]||ne.claude;ae.value=JSON.stringify(e,null,2)}async function ie(){if(!oe.value)try{const e=await async function(){return(await g.get(`${T}/api/convert/opencode/formats`)).data}();Array.isArray(e.sourceTypes)&&e.sourceTypes.length>0&&(Q.value=e.sourceTypes.map(e=>({label:e.name||e.id,value:e.id}))),Array.isArray(e.targetApis)&&e.targetApis.length>0&&(V.value=e.targetApis.map(e=>"chat.completions"===e?{label:"Chat Completions (/v1/chat/completions)",value:e}:{label:"Responses API (/v1/responses)",value:e})),e.defaultTargetApi&&(ee.value=e.defaultTargetApi),oe.value=!0}catch(e){}}function ce(){le.value="",se.value=null}async function pe(){let e;le.value="",se.value=null;try{e=JSON.parse(ae.value||"{}")}catch(a){return void(le.value="原始请求 JSON 解析失败,请检查格式")}te.value=!0;try{const a={claude:z,codex:R,gemini:I}[X.value];if(!a)throw new Error(`Unsupported sourceType: ${X.value}`);const t=await a({payload:e,options:{targetApi:ee.value}});if(!1===t.success)throw new Error(t.error||"转换失败");se.value=t,j.success("转换完成")}catch(a){const e=a.response?.data?.error||a.message||"转换失败";le.value=e}finally{te.value=!1}}async function de(){if(!se.value)return;const e={endpoint:se.value.endpoint,requestBody:se.value.requestBody};try{await navigator.clipboard.writeText(JSON.stringify(e,null,2)),j.success("已复制转换结果")}catch(a){j.error("复制失败,请手动复制")}}return i(()=>X.value,()=>{ue()}),i(()=>Z.visible,async e=>{e&&(await ie(),ae.value||ue())},{immediate:!0}),(t,i)=>(u(),r(m(x),{show:M.value,"onUpdate:show":i[3]||(i[3]=e=>M.value=e),width:m(L),placement:"right","show-mask":!0},{default:d(()=>[n(m(_),{closable:"","native-scrollbar":!1},{header:d(()=>[w("div",$,[w("div",q,[n(m(e),{size:18},{default:d(()=>[n(m(h))]),_:1})]),i[4]||(i[4]=w("span",null,"OpenCode 请求转换",-1))])]),default:d(()=>[w("div",B,[n(m(O),{"label-placement":"top"},{default:d(()=>[n(m(k),{label:"来源渠道"},{default:d(()=>[n(m(C),{value:X.value,"onUpdate:value":i[0]||(i[0]=e=>X.value=e),options:Q.value,placeholder:"选择来源渠道"},null,8,["value","options"])]),_:1}),n(m(k),{label:"目标 API"},{default:d(()=>[n(m(C),{value:ee.value,"onUpdate:value":i[1]||(i[1]=e=>ee.value=e),options:V.value,placeholder:"选择目标 API"},null,8,["value","options"])]),_:1}),n(m(k),{label:"原始请求 JSON"},{default:d(()=>[n(m(J),{value:ae.value,"onUpdate:value":i[2]||(i[2]=e=>ae.value=e),type:"textarea",autosize:{minRows:10,maxRows:18},placeholder:"粘贴原始请求 JSON"},null,8,["value"])]),_:1})]),_:1}),w("div",U,[n(m(a),{quaternary:"",onClick:ue},{default:d(()=>[...i[5]||(i[5]=[l("填充示例",-1)])]),_:1}),n(m(A),null,{default:d(()=>[n(m(a),{onClick:ce},{default:d(()=>[...i[6]||(i[6]=[l("清空结果",-1)])]),_:1}),n(m(a),{type:"primary",loading:te.value,onClick:pe},{default:d(()=>[...i[7]||(i[7]=[l("开始转换",-1)])]),_:1},8,["loading"])]),_:1})]),le.value?(u(),r(m(S),{key:0,type:"error","show-icon":!0,style:{"margin-top":"12px"}},{default:d(()=>[l(c(le.value),1)]),_:1})):s("",!0),se.value?(u(),f("div",D,[w("div",P,[w("div",Y,[i[8]||(i[8]=w("span",{class:"meta-label"},"转换端点",-1)),n(m(N),{type:"success",size:"small",bordered:!1},{default:d(()=>[l(c(se.value.endpoint),1)]),_:1})]),w("div",E,[i[9]||(i[9]=w("span",{class:"meta-label"},"目标协议",-1)),n(m(N),{type:"info",size:"small",bordered:!1},{default:d(()=>[l(c(se.value.targetApi),1)]),_:1})]),w("div",G,[i[10]||(i[10]=w("span",{class:"meta-label"},"消息数量",-1)),n(m(N),{size:"small",bordered:!1},{default:d(()=>[l(c(se.value.meta?.messageCount??0),1)]),_:1})])]),Array.isArray(se.value.warnings)&&se.value.warnings.length>0?(u(),r(m(S),{key:0,type:"warning","show-icon":!0,style:{"margin-top":"12px"}},{default:d(()=>[i[11]||(i[11]=w("div",{class:"warning-title"},"转换警告",-1)),w("ul",H,[(u(!0),f(v,null,o(se.value.warnings,(e,a)=>(u(),f("li",{key:a},c(e),1))),128))])]),_:1})):s("",!0),n(m(k),{label:"转换后请求 JSON",style:{"margin-top":"12px"}},{default:d(()=>[n(m(J),{value:re.value,type:"textarea",readonly:"",autosize:{minRows:10,maxRows:18}},null,8,["value"])]),_:1}),w("div",W,[i[13]||(i[13]=w("div",null,null,-1)),n(m(a),{size:"small",onClick:de},{default:d(()=>[...i[12]||(i[12]=[l("复制转换结果",-1)])]),_:1})])])):s("",!0)])]),_:1})]),_:1},8,["show","width"]))}},[["__scopeId","data-v-27295eec"]]);export{K as default};