aicodeswitch 2.1.3 → 2.1.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -10,13 +10,19 @@ AI Code Switch 是帮助你在本地管理 AI 编程工具接入大模型的工
10
10
  - 视频演示:[https://www.bilibili.com/video/BV1uEznBuEJd/](https://www.bilibili.com/video/BV1uEznBuEJd/?from=github)
11
11
  - 1分钟让Claude Code接入GLM国产模型:[https://www.bilibili.com/video/BV1a865B8ErA/](https://www.bilibili.com/video/BV1a865B8ErA/)
12
12
 
13
- ## 安装
13
+ ## 桌面版
14
+
15
+ 桌面端应用下载:[https://github.com/tangshuang/aicodeswitch/releases](https://github.com/tangshuang/aicodeswitch/releases)
16
+
17
+ ## 命令行工具
18
+
19
+ ### 安装
14
20
 
15
21
  ```
16
22
  npm install -g aicodeswitch
17
23
  ```
18
24
 
19
- ## 使用方法
25
+ ### 使用方法
20
26
 
21
27
  **启动服务**
22
28
 
@@ -24,6 +30,12 @@ npm install -g aicodeswitch
24
30
  aicos start
25
31
  ```
26
32
 
33
+ 或者直接运行
34
+
35
+ ```
36
+ aicos ui
37
+ ```
38
+
27
39
  **停止服务**
28
40
 
29
41
  ```
@@ -42,6 +54,8 @@ aicos ui
42
54
  http://127.0.0.1:4567
43
55
  ```
44
56
 
57
+ ## 管理界面
58
+
45
59
  **配置供应商**
46
60
 
47
61
  * 什么是供应商?
@@ -127,11 +141,18 @@ aicodeswitch内部,会根据“源类型”来转换数据。例如,你的
127
141
 
128
142
  你可以根据你的实际情况来实时切换路由,比如,你可以在发现自己的某个服务商处的余额较少时,立即切换到另外一个服务商。
129
143
 
130
- 另外,我还在考虑增加一些自动化切换到逻辑,比如,当上游服务商接口报错时,立即切换到另外一个服务商。
144
+ **智能故障切换机制**
145
+
146
+ 当同一请求类型配置多个规则时,系统会按排序优先使用第一个,如果某个服务报错(4xx/5xx)或请求超时,将自动切换到下一个可用规则,确保你可以正常使用coding工具。
147
+
148
+ ## Skills管理
149
+
150
+ 你可以在 aicodeswitch 中集中统一管理 skills,把skills分发给claude code和codex,随时启用和停用skills。
151
+ 另外,你可以基于自然语言搜索skills,找到skill之后,支持一键安装。
131
152
 
132
- ### 3\. 请求日志
153
+ ## 日志
133
154
 
134
- 在**请求日志**页面,您可以查看:
155
+ 在**日志**页面,您可以查看:
135
156
 
136
157
  **请求日志**:所有 API 请求的详细记录
137
158
 
@@ -140,18 +161,14 @@ aicodeswitch内部,会根据“源类型”来转换数据。例如,你的
140
161
  * 耗时和状态码
141
162
  * 错误信息(如有)
142
163
 
143
- **访问日志**:系统访问记录
144
-
145
- * 访问时间
146
- * 请求路径
147
- * HTTP 方法
148
-
149
164
  **错误日志**:错误和异常记录
150
165
 
151
166
  * 错误类型
152
167
  * 错误详情
153
168
  * 发生时间
154
169
 
170
+ **会话日志**:按照会话session来汇集日志
171
+
155
172
  **日志筛选**
156
173
 
157
174
  根据提供的选项进行筛选。
@@ -219,4 +236,4 @@ PORT=4567
219
236
 
220
237
  ## 技术支持
221
238
 
222
- 如有问题或建议,请访问项目 [GitHub 仓库](https://github.com/tangshuang/aicodeswitch/issues)提交 Issue。
239
+ 如有问题或建议,请访问项目 [GitHub 仓库](https://github.com/tangshuang/aicodeswitch/issues)提交 Issue。
@@ -114,6 +114,7 @@ const writeClaudeConfig = (dbManager) => __awaiter(void 0, void 0, void 0, funct
114
114
  const claudeSettings = {
115
115
  env: {
116
116
  ANTHROPIC_AUTH_TOKEN: config.apiKey || "api_key",
117
+ ANTHROPIC_API_KEY: "",
117
118
  ANTHROPIC_BASE_URL: `http://${host}:${port}/claude-code`,
118
119
  API_TIMEOUT_MS: "3000000"
119
120
  }
@@ -53,6 +53,7 @@ const streaming_1 = require("./transformers/streaming");
53
53
  const chunk_collector_1 = require("./transformers/chunk-collector");
54
54
  const rules_status_service_1 = require("./rules-status-service");
55
55
  const claude_openai_1 = require("./transformers/claude-openai");
56
+ const types_1 = require("../types");
56
57
  const SUPPORTED_TARGETS = ['claude-code', 'codex'];
57
58
  class ProxyServer {
58
59
  constructor(dbManager, app) {
@@ -1128,7 +1129,8 @@ class ProxyServer {
1128
1129
  buildUpstreamHeaders(req, service, sourceType, streamRequested) {
1129
1130
  const headers = {};
1130
1131
  for (const [key, value] of Object.entries(req.headers)) {
1131
- if (['host', 'connection', 'content-length', 'authorization'].includes(key.toLowerCase())) {
1132
+ // 排除原始认证头,防止与代理设置的认证头冲突
1133
+ if (['host', 'connection', 'content-length', 'authorization', 'x-api-key', 'x-anthropic-api-key', 'anthropic-api-key'].includes(key.toLowerCase())) {
1132
1134
  continue;
1133
1135
  }
1134
1136
  if (typeof value === 'string') {
@@ -1142,12 +1144,12 @@ class ProxyServer {
1142
1144
  headers.accept = 'text/event-stream';
1143
1145
  }
1144
1146
  // 确定认证方式:优先使用服务配置的 authType,否则根据 sourceType 自动判断
1145
- const authType = service.authType || 'auto';
1146
- const useXApiKey = authType === 'x-api-key' || (authType === 'auto' && this.isClaudeSource(sourceType));
1147
+ const authType = service.authType || types_1.AuthType.AUTO;
1148
+ const useXApiKey = authType === types_1.AuthType.API_KEY || (authType === types_1.AuthType.AUTO && this.isClaudeSource(sourceType));
1147
1149
  if (useXApiKey) {
1148
1150
  // 使用 x-api-key 认证(适用于 claude-chat, claude-code 及某些需要 x-api-key 的 openai-chat 兼容 API)
1149
1151
  headers['x-api-key'] = service.apiKey;
1150
- if (this.isClaudeSource(sourceType) || authType === 'x-api-key') {
1152
+ if (this.isClaudeSource(sourceType) || authType === types_1.AuthType.API_KEY) {
1151
1153
  // 仅在明确配置或 Claude 源时添加 anthropic-version
1152
1154
  headers['anthropic-version'] = headers['anthropic-version'] || '2023-06-01';
1153
1155
  }
@@ -1,2 +1,11 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AuthType = void 0;
4
+ /** 认证方式类型 */
5
+ var AuthType;
6
+ (function (AuthType) {
7
+ AuthType["AUTH_TOKEN"] = "authorization";
8
+ AuthType["API_KEY"] = "x-api-key";
9
+ AuthType["AUTO"] = "auto";
10
+ })(AuthType || (exports.AuthType = AuthType = {}));
11
+ ;
@@ -0,0 +1 @@
1
+ .nav-item-with-tooltip{position:relative;display:block}.nav-tooltip{position:absolute;left:calc(100% + 12px);top:50%;transform:translateY(-50%);background:var(--bg-card);color:var(--text-primary);padding:8px 12px;border-radius:8px;font-size:14px;font-weight:500;white-space:nowrap;z-index:999999;pointer-events:none;box-shadow:0 4px 12px #00000026;border:1px solid var(--border-primary);animation:tooltipFadeIn .2s ease-out}.nav-tooltip:before{content:"";position:absolute;right:100%;top:50%;transform:translateY(-50%);border:6px solid transparent;border-right-color:var(--border-primary)}.nav-tooltip:after{content:"";position:absolute;right:100%;top:50%;transform:translateY(-50%);border:5px solid transparent;border-right-color:var(--bg-card);margin-right:-1px}@keyframes tooltipFadeIn{0%{opacity:0;transform:translateY(-50%) translate(-8px)}to{opacity:1;transform:translateY(-50%) translate(0)}}.app{display:flex;width:100%;height:100%;overflow:hidden}.sidebar{width:260px;background:var(--bg-sidebar);color:var(--text-primary);display:flex;flex-direction:column;padding:0 0 80px;border-radius:12px;box-shadow:0 4px 12px #00000026;margin:8px;position:relative;z-index:999999;border:1px solid rgba(255,255,255,.15);transition:width .1s ease,margin .1s ease}.sidebar.collapsed{width:80px;margin-right:8px;overflow:visible}.sidebar.collapsed .logo h2,.sidebar.collapsed .nav-menu .nav-text,.sidebar.collapsed .github-link,.sidebar.collapsed .version-info{display:none;opacity:0}.sidebar.collapsed .logo{padding:20px 10px}.sidebar.collapsed .nav-menu a{padding:14px;justify-content:center}.sidebar.collapsed .theme-toggle{flex-direction:column;gap:8px;align-items:center}.logo{padding:16px 20px;border-bottom:2px solid rgba(255,255,255,.2);text-align:center;position:relative;overflow:hidden;transition:padding .3s ease;display:flex;flex-direction:row;align-items:center;justify-content:center;gap:12px}.logo h2{font-size:18px;font-weight:800;background:linear-gradient(135deg,#fff,#f8f8ff,#e6e6fa);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;text-shadow:0 2px 4px rgba(0,0,0,.1);position:relative;z-index:1;letter-spacing:1px;text-transform:uppercase;transition:all .3s ease;margin:0}.logo-image{width:36px;height:36px;object-fit:contain;transition:all .3s ease;filter:drop-shadow(0 2px 8px rgba(0,0,0,.2));flex-shrink:0}.sidebar.collapsed .logo{justify-content:center;padding:12px 10px}.sidebar.collapsed .logo-image{width:32px;height:32px}.nav-menu{list-style:none;padding:16px 0;margin:0;overflow:visible}.nav-menu li{margin:8px 12px;overflow:visible}.nav-menu a{display:flex;align-items:center;gap:12px;padding:14px 20px;color:var(--text-sidebar);text-decoration:none;border-radius:12px;transition:all .3s ease-out;font-weight:500;position:relative;overflow:hidden}.nav-menu .nav-icon{font-size:20px;flex-shrink:0;transition:all .3s ease}.nav-menu .nav-text{transition:opacity .3s ease,width .3s ease;white-space:nowrap}.nav-menu a:before{content:"";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.15),transparent);transition:left .5s ease-out}.nav-menu a:hover:before{left:100%}.nav-menu a:hover{background-color:#ffffff1a}.nav-menu a.active{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary))}.main-content{flex:1;padding:24px;overflow:auto;background:var(--bg-primary);position:relative;z-index:1;margin:16px 16px 16px 0;border-radius:20px;transition:margin .3s ease}.main-content:before{content:"";position:absolute;top:0;left:0;right:0;bottom:0;background-image:radial-gradient(circle at 20% 80%,rgba(255,206,92,.1) 0%,transparent 50%),radial-gradient(circle at 80% 20%,rgba(134,204,202,.1) 0%,transparent 50%),radial-gradient(circle at 40% 40%,rgba(255,113,206,.05) 0%,transparent 50%);pointer-events:none}.main-content>.routes-page,.main-content>.vendors-page,.main-content>.logs-page{min-width:1000px}.page-header{margin-bottom:24px;position:relative;z-index:1}.page-header h1{font-family:Fredoka,sans-serif;font-size:28px;font-weight:700;color:var(--accent-primary);margin-bottom:8px;text-shadow:0 2px 4px var(--shadow-primary)}.page-header p{color:var(--text-muted);font-size:16px;font-weight:400}.card{background:var(--bg-card);border-radius:20px;padding:24px;box-shadow:0 2px 8px #00000014;margin-bottom:24px;border:1px solid var(--border-primary);position:relative;overflow:hidden;transition:all .3s ease-out}.card:before{content:"";position:absolute;top:0;left:0;right:0;height:4px;background:linear-gradient(90deg,var(--accent-primary),var(--accent-secondary));border-radius:20px 20px 0 0}.card:hover{box-shadow:0 4px 12px #0000001f}.btn{padding:10px 20px;border:none;border-radius:12px;cursor:pointer;font-size:14px;font-weight:600;transition:all .3s ease-out;position:relative;overflow:hidden;white-space:nowrap}.btn:before{content:"";position:absolute;top:50%;left:50%;width:0;height:0;background:#ffffff4d;border-radius:50%;transform:translate(-50%,-50%);transition:width .6s,height .6s}.btn:hover:before{width:300px;height:300px}.btn:active{transform:translateY(0)}.btn:disabled{opacity:.4;cursor:not-allowed}.btn-primary{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;position:relative;overflow:hidden}.btn-primary:before{content:"";position:absolute;top:0;left:-100%;width:100%;height:100%;background:linear-gradient(90deg,transparent,rgba(255,255,255,.3),transparent);transition:left .5s ease}.btn-primary:hover:before{left:100%}.btn-danger{background:var(--accent-danger);color:#fff}.btn-danger:hover{background:#ea580c}.btn-success{background:var(--accent-success);color:#fff}.btn-success:hover{background:#059669}.btn-secondary{background:var(--accent-secondary);color:#fff}.btn-secondary:hover{background:var(--accent-primary)}.btn-sm{padding:4px 8px}table{width:100%;border-collapse:collapse;margin-top:16px;border-radius:12px;overflow:hidden}table th,table td{padding:16px;text-align:left;border-bottom:1px solid var(--border-secondary);color:var(--text-primary)}table th{background:var(--bg-table-header);font-weight:700;color:var(--text-secondary);font-family:Fredoka,sans-serif;font-size:14px;white-space:nowrap}.form-group{margin-bottom:20px}.form-group label{display:block;margin-bottom:8px;color:var(--text-primary);font-size:14px;font-weight:600;font-family:Nunito,sans-serif}.form-group input,.form-group select,.form-group textarea{width:100%;padding:12px 16px;border:2px solid var(--border-primary);border-radius:12px;font-size:14px;font-family:Nunito,sans-serif;background:var(--bg-secondary);color:var(--text-primary);transition:all .3s ease-out}.form-group input:focus,.form-group select:focus,.form-group textarea:focus{outline:none;border-color:var(--accent-primary);box-shadow:0 0 0 3px var(--shadow-secondary);background:var(--bg-secondary)}.form-group input:disabled,.form-group select:disabled,.form-group textarea:disabled{background:var(--bg-input-disabled);cursor:not-allowed;color:var(--text-input-disabled);opacity:.7}.form-group label small{font-size:.8em;color:var(--text-light)}@keyframes modalFadeIn{0%{opacity:0}to{opacity:1}}.modal{background:var(--bg-secondary);border-radius:24px;padding:32px 28px 32px 32px;min-width:500px;width:800px;max-width:90%;max-height:90vh;box-shadow:0 8px 24px #0003;border:1px solid var(--border-secondary);animation:modalSlideIn .4s ease-out;overflow:hidden;display:flex;flex-direction:column}.modal-container{overflow-y:auto;overflow-x:hidden;margin-right:-8px;padding-right:8px;scrollbar-gutter:stable;scrollbar-width:thin;scrollbar-color:var(--border-secondary) transparent}.modal-container::-webkit-scrollbar{width:8px}.modal-container::-webkit-scrollbar-track{background:transparent;border-radius:4px;margin:4px}.modal-container::-webkit-scrollbar-thumb{background:var(--border-secondary);border-radius:4px;border:2px solid transparent}.modal-container::-webkit-scrollbar-thumb:hover{background:var(--text-muted)}.modal-overlay{position:fixed;top:0;left:0;right:0;bottom:0;background:#0009;display:flex;justify-content:center;align-items:center;z-index:1000;animation:modalFadeIn .3s ease-out}.modal-close-btn{position:absolute;top:20px;right:20px;width:48px;height:48px;border:none;background:var(--accent-light);color:var(--text-primary);font-size:32px;line-height:1;cursor:pointer;border-radius:50%;transition:all .2s ease;display:flex;align-items:center;justify-content:center;padding:0;border:1px solid rgba(255,255,255,.3);z-index:1001}.modal-close-btn:hover{background:var(--accent-danger);color:#fff;transform:scale(1.05)}.modal-close-btn:active{transform:scale(.95)}@keyframes modalSlideIn{0%{opacity:0;transform:translateY(-20px) scale(.95)}to{opacity:1;transform:translateY(0) scale(1)}}.modal-header{display:flex;justify-content:space-between;align-items:center;margin-bottom:20px;position:relative}.modal-header h2{font-size:20px;color:var(--text-primary);flex:1}.modal-footer{display:flex;justify-content:flex-end;gap:10px;margin-top:20px}.action-buttons{display:flex;gap:8px}.badge{display:inline-block;padding:6px 12px;border-radius:20px;font-size:12px;font-weight:700;text-transform:uppercase;letter-spacing:.5px;white-space:nowrap}.badge-success{background:var(--accent-success);color:#fff}.badge-warning{background:var(--accent-warning);color:#fff}.badge-danger{background:var(--accent-danger);color:#fff}.badge-claude-code{background:#ce653c;color:#fff}.badge-codex{background:#1f2937;color:#fff}.empty-state{text-align:center;padding:60px 20px;color:var(--text-muted);font-family:Nunito,sans-serif}.empty-state p{margin-bottom:20px;font-size:16px;font-weight:500}.pagination{display:flex;justify-content:space-between;align-items:center;margin-top:20px;padding:16px;background:var(--bg-secondary);border-radius:12px;border:1px solid var(--border-primary);gap:20px;flex-wrap:wrap}.pagination-info{color:var(--text-muted);font-size:14px;font-weight:500}.pagination-controls{display:flex;align-items:center;gap:10px}.pagination-btn{padding:6px 14px;border:1px solid var(--border-primary);background:var(--bg-card);color:var(--text-primary);border-radius:8px;cursor:pointer;font-size:14px;font-weight:500;transition:all .2s ease}.pagination-btn:hover:not(:disabled){background:var(--accent-primary);color:#fff;border-color:var(--accent-primary)}.pagination-btn:disabled{opacity:.4;cursor:not-allowed;background:var(--bg-secondary)}.pagination-size{display:flex;align-items:center;gap:8px;color:var(--text-primary);font-size:14px;font-weight:500}.pagination-select{padding:6px 10px;border:1px solid var(--border-primary);background:var(--bg-card);color:var(--text-primary);border-radius:8px;cursor:pointer;font-size:14px;outline:none;transition:all .2s ease}.pagination-select:hover{border-color:var(--accent-primary)}.pagination-select:focus{border-color:var(--accent-primary);box-shadow:0 0 0 2px #3498db1a}.toolbar{display:flex;justify-content:space-between;align-items:center;margin-bottom:16px}.theme-toggle{position:absolute;bottom:20px;left:20px;right:20px;display:flex;align-items:center;justify-content:space-between;gap:12px;flex-wrap:wrap}.sidebar-toggle-btn{background:none;border:none;color:var(--text-sidebar);cursor:pointer;padding:8px;border-radius:8px;transition:all .3s ease-out;display:flex;align-items:center;justify-content:center;font-size:18px;font-weight:700}.sidebar-toggle-btn:hover{background:#ffffff26;opacity:1}.github-link{background:none;border:none;color:var(--text-sidebar);cursor:pointer;padding:8px;border-radius:8px;transition:all .3s ease-out;display:flex;align-items:center;justify-content:center;opacity:.8;margin-left:auto}.github-link:hover{background:#ffffff26;opacity:1}.theme-toggle button{background:none;border:none;color:var(--text-sidebar);cursor:pointer;padding:8px;border-radius:8px;transition:all .3s ease-out;display:flex;align-items:center;justify-content:center}.version-info{font-size:12px;color:var(--text-sidebar);opacity:.7;font-family:Monaco,Menlo,monospace;font-weight:500;-webkit-user-select:none;user-select:none;transition:opacity .3s ease}.theme-toggle button:hover{background:#ffffff26}.theme-toggle button.active{background:linear-gradient(135deg,var(--accent-primary),var(--accent-secondary));color:#fff;animation:pulse 2s ease-in-out infinite}@keyframes pulse{0%,to{transform:scale(1)}50%{transform:scale(1.05)}}.version-info-wrapper{position:relative;display:flex;align-items:center}.version-info{font-size:12px;color:var(--text-sidebar);opacity:.7;font-family:Monaco,Menlo,monospace;font-weight:500;-webkit-user-select:none;user-select:none;transition:opacity .3s ease;position:relative}.version-info:hover{opacity:1}.version-badge{position:absolute;top:-2px;right:-6px;width:8px;height:8px;background:linear-gradient(135deg,#ef4444,#dc2626);border-radius:50%;border:2px solid var(--bg-sidebar);animation:pulse-badge 2s ease-in-out infinite;box-shadow:0 0 8px #ef444499}@keyframes pulse-badge{0%,to{transform:scale(1);opacity:1}50%{transform:scale(1.2);opacity:.8}}.version-update-popup{position:absolute;bottom:calc(100% + 12px);left:50%;transform:translate(-50%);background:#10b98126;border:1px solid rgba(16,185,129,.4);border-radius:12px;padding:12px;min-width:220px;box-shadow:0 4px 12px #0003;opacity:0;visibility:hidden;transition:all .3s ease-out;pointer-events:none;z-index:10000;text-decoration:none;-webkit-backdrop-filter:blur(10px);backdrop-filter:blur(10px)}.version-info-wrapper:hover .version-update-popup,.version-update-popup:hover{opacity:1;visibility:visible;pointer-events:auto;transform:translate(-50%) translateY(12px)}.version-update-popup:after{content:"";position:absolute;top:100%;left:50%;transform:translate(-50%);border:6px solid transparent;border-top-color:#10b98166}.update-popup-content{display:flex;align-items:flex-start;gap:10px}.update-icon{font-size:18px;animation:bounce 1s ease-in-out infinite;flex-shrink:0}@keyframes bounce{0%,to{transform:translateY(0)}50%{transform:translateY(-3px)}}.update-text{flex:1}.update-title{font-size:13px;font-weight:600;color:var(--text-sidebar);margin-bottom:4px}.update-versions{font-size:11px;color:#fffc;font-family:Monaco,Menlo,monospace;margin-bottom:6px}.update-message{font-size:10px;color:var(--text-sidebar);line-height:1.4}.update-message code{background:#ffffff26;padding:2px 4px;border-radius:4px;font-family:Monaco,Menlo,monospace;margin-top:2px;display:inline-block;color:#ffffffe6}@media (prefers-reduced-motion: reduce){.nav-menu a,.card,.btn,.form-group input,.form-group select,.form-group textarea,.modal-overlay,.modal,table tr{transition:none;animation:none}.nav-menu a:hover,.card:hover,.btn:hover,table tr:hover{transform:none}.btn:before{display:none}}.markdown-content{line-height:1.7;color:var(--text-primary);max-width:100%;overflow-x:auto}.markdown-content h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.8em;color:var(--text-primary);border-bottom:2px solid var(--border-color);padding-bottom:.3em}.markdown-content h2{font-size:1.6em;font-weight:600;margin-top:1.5em;margin-bottom:.6em;color:var(--text-primary);border-bottom:1px solid var(--border-color);padding-bottom:.25em}.markdown-content h3{font-size:1.3em;font-weight:600;margin-top:1.2em;margin-bottom:.5em;color:var(--text-primary)}.markdown-content h4{font-size:1.1em;font-weight:600;margin-top:1em;margin-bottom:.4em;color:var(--text-primary)}.markdown-content p{margin-bottom:1em;line-height:1.8}.markdown-content ul,.markdown-content ol{margin-bottom:1em;padding-left:2em}.markdown-content li{margin-bottom:.5em;line-height:1.7}.markdown-content code{background:var(--bg-secondary);padding:.2em .4em;border-radius:4px;font-size:.9em;font-family:Monaco,Menlo,Ubuntu Mono,Consolas,monospace;color:var(--accent-secondary);border:1px solid var(--border-color)}.markdown-content pre{background:var(--bg-secondary);padding:1em;border-radius:8px;overflow-x:auto;margin-bottom:1em;border:1px solid var(--border-color)}.markdown-content pre code{background:none;padding:0;border:none;color:var(--text-primary);font-size:.95em}.markdown-content blockquote{border-left:4px solid var(--accent-primary);padding-left:1em;margin-left:0;margin-bottom:1em;color:var(--text-secondary);font-style:italic}.markdown-content a{color:var(--accent-primary);text-decoration:none;border-bottom:1px solid var(--accent-secondary);transition:all .2s ease}.markdown-content a:hover{color:var(--accent-secondary)}.markdown-content table{width:100%;border-collapse:collapse;margin-bottom:1em}.markdown-content table th,.markdown-content table td{padding:.75em;text-align:left;border:1px solid var(--border-color)}.markdown-content table th{background:var(--bg-secondary);font-weight:600}.markdown-content hr{border:none;border-top:2px solid var(--border-color);margin:2em 0}.markdown-content strong{font-weight:600;color:var(--text-primary)}.markdown-content em{font-style:italic}.markdown-content img{max-width:100%;height:auto;border-radius:8px;margin:1em 0}.config-status-indicator{display:inline-block}.status-badge{display:inline-block;padding:4px 10px;border-radius:12px;font-size:11px;font-weight:600}.status-active{background:linear-gradient(135deg,#4caf50,#45a049);color:#fff}.status-backup{background:linear-gradient(135deg,#ff9800,#f57c00);color:#fff}.status-inactive{background:linear-gradient(135deg,#9e9e9e,#757575);color:#fff}.skills-header-row{display:flex;justify-content:space-between;align-items:flex-start;gap:16px;flex-wrap:wrap}.skills-header-actions{margin-top:8px}.skills-discover-btn{padding:18px 36px;font-size:16px;font-weight:700;border-radius:14px;box-shadow:0 4px 12px #00000026;position:relative}.skills-discover-btn:after{content:"";position:absolute;top:-6px;right:-6px;bottom:-6px;left:-6px;border-radius:18px;border:2px solid rgba(16,185,129,.35);opacity:.6}.skills-section-header{display:flex;flex-direction:column;gap:6px;margin-bottom:20px}.skills-section-header h2{margin:0;font-size:22px;color:var(--text-primary)}.skills-section-header span{color:var(--text-muted);font-size:14px}.skills-grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(360px,1fr));gap:16px}.skill-card{display:flex;flex-direction:column;gap:16px;min-height:180px}.skill-title{font-size:18px;font-weight:700;color:var(--text-primary);margin-bottom:8px}.skill-description{color:var(--text-muted);font-size:14px;line-height:1.6;flex:1}.skill-tags{display:flex;flex-wrap:wrap;gap:8px}.skills-discover{display:flex;flex-direction:column;gap:20px}.skills-search-card{display:flex;flex-direction:column;gap:16px}.skills-search-header h3{margin:0 0 6px;font-size:18px;color:var(--text-primary)}.skills-search-header p{margin:0;color:var(--text-muted);font-size:14px}.skills-search-input{width:100%;padding:12px 14px;border-radius:12px;border:1px solid var(--border-primary);background:var(--bg-secondary);color:var(--text-primary);resize:vertical;min-height:120px;font-size:14px;line-height:1.6}.skills-search-actions{display:flex;justify-content:flex-end}.skills-results{display:flex;flex-direction:column;gap:16px}.skills-result-actions{display:flex;justify-content:flex-end;gap:8px}.badge-secondary{background:#0f513226;color:var(--text-primary)}.switch{position:relative;width:44px;height:24px;border-radius:12px;border:none;cursor:pointer;background-color:#4b5563;transition:background-color .2s}.switch.active{background-color:var(--primary-color)}.switch:disabled{cursor:not-allowed;opacity:.6}.switch:focus{outline:none;box-shadow:0 0 0 2px var(--primary-color)}.switch::-moz-focus-inner{border:0}*{margin:0;padding:0;box-sizing:border-box}:root{--primary-color: #14532D;--error-color: #DC2626;--bg-primary: linear-gradient(135deg, #F7FEE7 0%, #F0FDF4 100%);--bg-secondary: rgba(255, 255, 255, .95);--bg-sidebar: linear-gradient(135deg, #0F5132 0%, #065F46 100%);--bg-card: rgba(255, 255, 255, .98);--bg-code: #f4fff7;--bg-table-header: linear-gradient(135deg, #A7F3D0 0%, #6EE7B7 100%);--text-primary: #14532D;--text-secondary: #064E3B;--text-muted: #065F46;--text-sidebar: #FFFFFF;--text-on-dark: #FFFFFF;--border-primary: rgba(15, 81, 50, .2);--border-secondary: rgba(6, 95, 70, .2);--accent-primary: #0F5132;--accent-secondary: #047857;--accent-light: #a1e9c7;--accent-success: #047857;--accent-warning: #D97706;--accent-danger: #DC2626;--shadow-primary: rgba(15, 81, 50, .3);--shadow-secondary: rgba(15, 81, 50, .15);--bg-route-item: rgba(248, 249, 250, .9);--bg-route-item-hover: rgba(230, 244, 234, .95);--bg-route-item-selected: rgba(161, 233, 199, .25);--text-route-muted: #6c757d;--text-info: #2faeee;--text-light: #018038;--bg-info-box: #f8f9fa;--border-info-box: #e0e0e0;--text-info-box: #666;--bg-info-blue: #e3f2fd;--border-info-blue: #2196f3;--bg-info-green: #f1f8e9;--border-info-green: #8bc34a;--bg-info-orange: #fff3e0;--border-info-orange: #ff9800;--bg-info-yellow: #fff8e1;--border-info-yellow: #ffc107;--bg-assembled-text: #f8f9fa;--bg-thinking-box: #fff9e6;--border-thinking-box: #e0e0e0;--text-thinking-title: #f39c12;--bg-thinking-content: #fff;--border-thinking-content: #f0e6d3;--text-thinking-content: #555;--text-reply-title: #333;--bg-reply-content: #fff;--border-reply-content: #ddd;--text-reply-content: #333;--bg-input-disabled: #e5e7eb;--text-input-disabled: #9ca3af}[data-theme=dark]{--bg-primary: linear-gradient(135deg, #0A1A0F 0%, #0F2415 100%);--bg-secondary: rgba(15, 36, 21, .9);--bg-sidebar: linear-gradient(135deg, #0F5132 0%, #065F46 100%);--bg-card: rgba(25, 51, 31, .95);--bg-code: #0f172a;--bg-table-header: linear-gradient(135deg, #0F5132 0%, #047857 100%);--text-primary: #ECFEF5;--text-secondary: #A7F3D0;--text-muted: #6EE7B7;--text-sidebar: #FFFFFF;--text-on-dark: #ECFEF5;--border-primary: rgba(15, 81, 50, .5);--border-secondary: rgba(6, 95, 70, .5);--accent-primary: #0F5132;--accent-secondary: #047857;--accent-light: #002d18;--accent-success: #10B981;--accent-warning: #F59E0B;--accent-danger: #EF4444;--shadow-primary: rgba(15, 81, 50, .4);--shadow-secondary: rgba(6, 95, 70, .3);--bg-route-item: rgba(20, 46, 28, .7);--bg-route-item-hover: rgba(30, 60, 38, .85);--bg-route-item-selected: rgba(16, 185, 129, .15);--text-route-muted: #A7F3D0;--text-light: #999999;--bg-info-box: rgba(20, 46, 28, .6);--border-info-box: rgba(161, 233, 199, .2);--text-info-box: #A7F3D0;--bg-info-blue: rgba(13, 71, 161, .25);--border-info-blue: #1976d2;--bg-info-green: rgba(27, 94, 32, .25);--border-info-green: #689f38;--bg-info-orange: rgba(230, 81, 0, .2);--border-info-orange: #f57c00;--bg-info-yellow: rgba(255, 193, 7, .15);--border-info-yellow: #ffb300;--bg-assembled-text: rgba(15, 23, 42, .5);--bg-thinking-box: rgba(230, 81, 0, .15);--border-thinking-box: rgba(245, 158, 11, .3);--text-thinking-title: #fbbf24;--bg-thinking-content: rgba(15, 23, 42, .4);--border-thinking-content: rgba(245, 158, 11, .2);--text-thinking-content: #d1d5db;--text-reply-title: #e5e7eb;--bg-reply-content: rgba(15, 23, 42, .4);--border-reply-content: rgba(161, 233, 199, .2);--text-reply-content: #e5e7eb;--bg-input-disabled: #2d3748;--text-input-disabled: #718096}body{font-family:Nunito,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Oxygen,Ubuntu,Cantarell,Fira Sans,Droid Sans,Helvetica Neue,sans-serif;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background:var(--bg-primary);color:var(--text-primary);transition:all .3s ease;min-height:100vh}code{font-family:source-code-pro,Menlo,Monaco,Consolas,Courier New,monospace}#root{width:100vw;height:100vh;overflow:visible}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-track{background:var(--bg-secondary);border-radius:6px}::-webkit-scrollbar-thumb{background:var(--accent-secondary);border-radius:6px;border:2px solid var(--bg-secondary)}::-webkit-scrollbar-thumb:hover{background:var(--accent-primary)}::-webkit-scrollbar-corner{background:var(--bg-secondary)}*{scrollbar-width:thin;scrollbar-color:var(--accent-primary) var(--bg-secondary)}::-webkit-scrollbar-button{display:none}html{scroll-behavior:smooth}.datetime-picker-input{cursor:pointer}.datetime-picker-input::-webkit-calendar-picker-indicator{cursor:pointer;opacity:1;filter:brightness(0);padding:2px 4px}.datetime-picker-input::-webkit-calendar-picker-indicator:hover{filter:brightness(.3)}.datetime-picker-input::-moz-calendar-picker-indicator{cursor:pointer;opacity:1;filter:brightness(0);padding:2px 4px}.datetime-picker-input::-moz-calendar-picker-indicator:hover{filter:brightness(.3)}[data-theme=dark] .datetime-picker-input::-webkit-calendar-picker-indicator{filter:brightness(0) invert(1)}[data-theme=dark] .datetime-picker-input::-webkit-calendar-picker-indicator:hover{filter:brightness(.2) invert(1)}[data-theme=dark] .datetime-picker-input::-moz-calendar-picker-indicator{filter:brightness(0) invert(1)}[data-theme=dark] .datetime-picker-input::-moz-calendar-picker-indicator:hover{filter:brightness(.2) invert(1)}