aicodeswitch 1.4.1 → 1.5.0

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.
@@ -0,0 +1,179 @@
1
+ import type { Vendor, APIService, Route, Rule, RequestLog, AccessLog, ErrorLog, AppConfig, AuthStatus, LoginResponse, Statistics } from '../../types';
2
+
3
+ interface BackendAPI {
4
+ // 鉴权相关
5
+ getAuthStatus: () => Promise<AuthStatus>;
6
+ login: (authCode: string) => Promise<LoginResponse>;
7
+
8
+ // 版本检查
9
+ checkVersion: () => Promise<{ hasUpdate: boolean; currentVersion: string | null; latestVersion: string | null }>;
10
+
11
+ getVendors: () => Promise<Vendor[]>;
12
+ createVendor: (vendor: Omit<Vendor, 'id' | 'createdAt' | 'updatedAt'>) => Promise<Vendor>;
13
+ updateVendor: (id: string, vendor: Partial<Vendor>) => Promise<boolean>;
14
+ deleteVendor: (id: string) => Promise<boolean>;
15
+
16
+ getAPIServices: (vendorId?: string) => Promise<APIService[]>;
17
+ createAPIService: (service: Omit<APIService, 'id' | 'createdAt' | 'updatedAt'>) => Promise<APIService>;
18
+ updateAPIService: (id: string, service: Partial<APIService>) => Promise<boolean>;
19
+ deleteAPIService: (id: string) => Promise<boolean>;
20
+
21
+ getRoutes: () => Promise<Route[]>;
22
+ createRoute: (group: Omit<Route, 'id' | 'createdAt' | 'updatedAt'>) => Promise<Route>;
23
+ updateRoute: (id: string, group: Partial<Route>) => Promise<boolean>;
24
+ deleteRoute: (id: string) => Promise<boolean>;
25
+ activateRoute: (id: string) => Promise<boolean>;
26
+ deactivateRoute: (id: string) => Promise<boolean>;
27
+
28
+ getRules: (routeId?: string) => Promise<Rule[]>;
29
+ createRule: (route: Omit<Rule, 'id' | 'createdAt' | 'updatedAt'>) => Promise<Rule>;
30
+ updateRule: (id: string, route: Partial<Rule>) => Promise<boolean>;
31
+ deleteRule: (id: string) => Promise<boolean>;
32
+
33
+ getLogs: (limit: number, offset: number) => Promise<RequestLog[]>;
34
+ clearLogs: () => Promise<boolean>;
35
+
36
+ getAccessLogs: (limit: number, offset: number) => Promise<AccessLog[]>;
37
+ clearAccessLogs: () => Promise<boolean>;
38
+
39
+ getErrorLogs: (limit: number, offset: number) => Promise<ErrorLog[]>;
40
+ clearErrorLogs: () => Promise<boolean>;
41
+
42
+ getConfig: () => Promise<AppConfig>;
43
+ updateConfig: (config: AppConfig) => Promise<boolean>;
44
+
45
+ exportData: (password: string) => Promise<string>;
46
+ importData: (encryptedData: string, password: string) => Promise<boolean>;
47
+
48
+ writeClaudeConfig: () => Promise<boolean>;
49
+ writeCodexConfig: () => Promise<boolean>;
50
+ restoreClaudeConfig: () => Promise<boolean>;
51
+ restoreCodexConfig: () => Promise<boolean>;
52
+ checkClaudeBackup: () => Promise<{ exists: boolean }>;
53
+ checkCodexBackup: () => Promise<{ exists: boolean }>;
54
+
55
+ getStatistics: (days?: number) => Promise<Statistics>;
56
+ }
57
+
58
+ const buildUrl = (
59
+ path: string,
60
+ query?: Record<string, string | number | undefined>
61
+ ): string => {
62
+ if (!query) {
63
+ return path;
64
+ }
65
+ const params = new URLSearchParams();
66
+ Object.entries(query).forEach(([key, value]) => {
67
+ if (value !== undefined && value !== null && value !== '') {
68
+ params.set(key, String(value));
69
+ }
70
+ });
71
+ const queryString = params.toString();
72
+ return queryString ? `${path}?${queryString}` : path;
73
+ };
74
+
75
+ const requestJson = async <T>(path: string, options: RequestInit = {}): Promise<T> => {
76
+ // 从 localStorage 读取 token
77
+ const token = localStorage.getItem('auth_token');
78
+
79
+ const response = await fetch(path, {
80
+ ...options,
81
+ headers: {
82
+ 'Content-Type': 'application/json',
83
+ ...(token ? { Authorization: `Bearer ${token}` } : {}),
84
+ ...(options.headers || {}),
85
+ },
86
+ });
87
+
88
+ if (!response.ok) {
89
+ // 如果是 401,清除本地 token 并抛出特殊错误
90
+ if (response.status === 401) {
91
+ localStorage.removeItem('auth_token');
92
+ const message = await response.text();
93
+ const error = new Error(message || response.statusText) as Error & { status: number };
94
+ error.status = 401;
95
+ throw error;
96
+ }
97
+
98
+ const message = await response.text();
99
+ throw new Error(message || response.statusText);
100
+ }
101
+
102
+ if (response.status === 204) {
103
+ return undefined as T;
104
+ }
105
+
106
+ const contentType = response.headers.get('content-type') || '';
107
+ if (contentType.includes('application/json')) {
108
+ return response.json() as Promise<T>;
109
+ }
110
+ return response.text() as Promise<T>;
111
+ };
112
+
113
+ export const api: BackendAPI = {
114
+ // 鉴权相关
115
+ getAuthStatus: () => requestJson(buildUrl('/api/auth/status')),
116
+ login: (authCode) => requestJson(buildUrl('/api/auth/login'), {
117
+ method: 'POST',
118
+ body: JSON.stringify({ authCode })
119
+ }),
120
+
121
+ // 版本检查
122
+ checkVersion: () => requestJson(buildUrl('/api/version/check')),
123
+
124
+ getVendors: () => requestJson(buildUrl('/api/vendors')),
125
+ createVendor: (vendor) => requestJson(buildUrl('/api/vendors'), { method: 'POST', body: JSON.stringify(vendor) }),
126
+ updateVendor: (id, vendor) => requestJson(buildUrl(`/api/vendors/${id}`), { method: 'PUT', body: JSON.stringify(vendor) }),
127
+ deleteVendor: (id) => requestJson(buildUrl(`/api/vendors/${id}`), { method: 'DELETE' }),
128
+
129
+ getAPIServices: (vendorId) => requestJson(buildUrl('/api/services', vendorId ? { vendorId } : undefined)),
130
+ createAPIService: (service) => requestJson(buildUrl('/api/services'), { method: 'POST', body: JSON.stringify(service) }),
131
+ updateAPIService: (id, service) => requestJson(buildUrl(`/api/services/${id}`), { method: 'PUT', body: JSON.stringify(service) }),
132
+ deleteAPIService: (id) => requestJson(buildUrl(`/api/services/${id}`), { method: 'DELETE' }),
133
+
134
+ getRoutes: () => requestJson(buildUrl('/api/routes')),
135
+ createRoute: (group) => requestJson(buildUrl('/api/routes'), { method: 'POST', body: JSON.stringify(group) }),
136
+ updateRoute: (id, group) => requestJson(buildUrl(`/api/routes/${id}`), { method: 'PUT', body: JSON.stringify(group) }),
137
+ deleteRoute: (id) => requestJson(buildUrl(`/api/routes/${id}`), { method: 'DELETE' }),
138
+ activateRoute: (id) => requestJson(buildUrl(`/api/routes/${id}/activate`), { method: 'POST' }),
139
+ deactivateRoute: (id) => requestJson(buildUrl(`/api/routes/${id}/deactivate`), { method: 'POST' }),
140
+
141
+ getRules: (routeId) => requestJson(buildUrl('/api/rules', routeId ? { routeId } : undefined)),
142
+ createRule: (route) => requestJson(buildUrl('/api/rules'), { method: 'POST', body: JSON.stringify(route) }),
143
+ updateRule: (id, route) => requestJson(buildUrl(`/api/rules/${id}`), { method: 'PUT', body: JSON.stringify(route) }),
144
+ deleteRule: (id) => requestJson(buildUrl(`/api/rules/${id}`), { method: 'DELETE' }),
145
+
146
+ getLogs: (limit, offset) => requestJson(buildUrl('/api/logs', { limit, offset })),
147
+ clearLogs: () => requestJson(buildUrl('/api/logs'), { method: 'DELETE' }),
148
+
149
+ getAccessLogs: (limit, offset) => requestJson(buildUrl('/api/access-logs', { limit, offset })),
150
+ clearAccessLogs: () => requestJson(buildUrl('/api/access-logs'), { method: 'DELETE' }),
151
+
152
+ getErrorLogs: (limit, offset) => requestJson(buildUrl('/api/error-logs', { limit, offset })),
153
+ clearErrorLogs: () => requestJson(buildUrl('/api/error-logs'), { method: 'DELETE' }),
154
+
155
+ getConfig: () => requestJson(buildUrl('/api/config')),
156
+ updateConfig: (config) => requestJson(buildUrl('/api/config'), { method: 'PUT', body: JSON.stringify(config) }),
157
+
158
+ exportData: async (password) => {
159
+ const result = await requestJson<{ data: string }>(
160
+ buildUrl('/api/export'),
161
+ { method: 'POST', body: JSON.stringify({ password }) }
162
+ );
163
+ return result.data;
164
+ },
165
+
166
+ importData: (encryptedData, password) => requestJson(buildUrl('/api/import'), {
167
+ method: 'POST',
168
+ body: JSON.stringify({ encryptedData, password }),
169
+ }),
170
+
171
+ writeClaudeConfig: () => requestJson(buildUrl('/api/write-config/claude'), { method: 'POST' }),
172
+ writeCodexConfig: () => requestJson(buildUrl('/api/write-config/codex'), { method: 'POST' }),
173
+ restoreClaudeConfig: () => requestJson(buildUrl('/api/restore-config/claude'), { method: 'POST' }),
174
+ restoreCodexConfig: () => requestJson(buildUrl('/api/restore-config/codex'), { method: 'POST' }),
175
+ checkClaudeBackup: () => requestJson(buildUrl('/api/check-backup/claude')),
176
+ checkCodexBackup: () => requestJson(buildUrl('/api/check-backup/codex')),
177
+
178
+ getStatistics: (days) => requestJson(buildUrl('/api/statistics', days ? { days } : undefined)),
179
+ };
@@ -0,0 +1,89 @@
1
+ import { useState } from 'react';
2
+
3
+ interface JSONViewerProps {
4
+ data: string | Record<string, any> | any[];
5
+ title?: string;
6
+ collapsed?: boolean;
7
+ }
8
+
9
+ const JSONViewer: React.FC<JSONViewerProps> = ({ data, title, collapsed = false }) => {
10
+ const [isExpanded, setIsExpanded] = useState(!collapsed);
11
+ const [copySuccess, setCopySuccess] = useState(false);
12
+
13
+ // 格式化 JSON 数据
14
+ const formatJSON = (input: string | Record<string, any> | any[]): string => {
15
+ try {
16
+ let jsonData: any;
17
+ if (typeof input === 'string') {
18
+ jsonData = JSON.parse(input);
19
+ } else {
20
+ jsonData = input;
21
+ }
22
+ return JSON.stringify(jsonData, null, 2);
23
+ } catch (error) {
24
+ return typeof input === 'string' ? input : String(input);
25
+ }
26
+ };
27
+
28
+ // 复制到剪贴板
29
+ const handleCopy = () => {
30
+ const formatted = formatJSON(data);
31
+ navigator.clipboard.writeText(formatted)
32
+ .then(() => {
33
+ setCopySuccess(true);
34
+ setTimeout(() => setCopySuccess(false), 2000);
35
+ })
36
+ .catch((error) => {
37
+ console.error('Failed to copy JSON:', error);
38
+ });
39
+ };
40
+
41
+ const formattedJSON = formatJSON(data);
42
+
43
+ return (
44
+ <div className="json-viewer">
45
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '8px' }}>
46
+ {title && (
47
+ <h4 style={{ margin: 0, fontSize: '14px', color: 'var(--text-primary)' }}>
48
+ {title}
49
+ </h4>
50
+ )}
51
+ <div style={{ display: 'flex', gap: '8px' }}>
52
+ <button
53
+ onClick={() => setIsExpanded(!isExpanded)}
54
+ className="btn btn-sm btn-secondary"
55
+ style={{ fontSize: '12px', padding: '2px 8px' }}
56
+ >
57
+ {isExpanded ? '折叠' : '展开'}
58
+ </button>
59
+ <button
60
+ onClick={handleCopy}
61
+ className="btn btn-sm btn-primary"
62
+ style={{ fontSize: '12px', padding: '2px 8px' }}
63
+ >
64
+ {copySuccess ? '已复制' : '复制'}
65
+ </button>
66
+ </div>
67
+ </div>
68
+ {isExpanded && (
69
+ <pre style={{
70
+ background: 'var(--bg-code)',
71
+ border: '1px solid var(--border-primary)',
72
+ borderRadius: '8px',
73
+ padding: '12px',
74
+ overflowX: 'auto',
75
+ fontSize: '12px',
76
+ lineHeight: '1.4',
77
+ color: 'var(--text-primary)',
78
+ maxHeight: '400px',
79
+ overflowY: 'auto',
80
+ margin: 0
81
+ }}>
82
+ <code>{formattedJSON}</code>
83
+ </pre>
84
+ )}
85
+ </div>
86
+ );
87
+ };
88
+
89
+ export default JSONViewer;
@@ -0,0 +1,4 @@
1
+ export const TARGET_TYPE = {
2
+ 'claude-code': 'Claude Code',
3
+ 'codex': 'Codex',
4
+ };
@@ -0,0 +1,13 @@
1
+ #### 🚀 智谱 GLM Coding Plan
2
+
3
+ > 价格便宜,国内速度快,支持多工具(Claude Code等 20+ 大编程工具),而且其中的GLM-4.7是目前国内最强编程模型。适合对模型有一定要求,同时又无法获取国外模型的朋友入手。
4
+ >
5
+ > 链接:[https://www.bigmodel.cn/glm-coding](https://www.bigmodel.cn/glm-coding?ic=5AH7ATEZSC)
6
+
7
+
8
+ #### 🗿 AI Code With
9
+
10
+ > 支持Claude、GPT、Gemni三大系列模型,可接入Claude Code、Codex,经实测,非常稳定,速度快,但是没有包月套餐,只能使用tokens流量,适合追求稳定的朋友。
11
+ >
12
+ > 链接:[https://aicodewith.com/](https://aicodewith.com/login?tab=register&invitation=QCA74W)
13
+
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import ReactDOM from 'react-dom/client';
3
+ import App from './App';
4
+ import './styles/index.css';
5
+
6
+ ReactDOM.createRoot(document.getElementById('root')!).render(
7
+ <React.StrictMode>
8
+ <App />
9
+ </React.StrictMode>,
10
+ );