codex-endpoint-switcher 1.1.0 → 1.2.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.
- package/package.json +1 -1
- package/src/main/cloud-sync-client.js +12 -7
- package/src/renderer/index.html +208 -156
- package/src/renderer/renderer.js +223 -41
- package/src/renderer/styles.css +571 -120
- package/src/web/server.js +26 -1
package/package.json
CHANGED
|
@@ -29,6 +29,14 @@ function normalizeServerUrl(value) {
|
|
|
29
29
|
return parsedUrl.toString().replace(/\/+$/, "");
|
|
30
30
|
}
|
|
31
31
|
|
|
32
|
+
function getDefaultServerUrl() {
|
|
33
|
+
return normalizeServerUrl(
|
|
34
|
+
process.env.CODEX_SYNC_SERVER_URL ||
|
|
35
|
+
process.env.SYNC_SERVER_PUBLIC_URL ||
|
|
36
|
+
"http://cd.xdo.icu:18245",
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
32
40
|
function normalizeUsername(value) {
|
|
33
41
|
const username = String(value || "").trim();
|
|
34
42
|
if (!username) {
|
|
@@ -69,7 +77,7 @@ async function ensureStorage() {
|
|
|
69
77
|
cloudSyncConfigPath,
|
|
70
78
|
`${JSON.stringify(
|
|
71
79
|
{
|
|
72
|
-
serverUrl:
|
|
80
|
+
serverUrl: getDefaultServerUrl(),
|
|
73
81
|
username: "",
|
|
74
82
|
authToken: "",
|
|
75
83
|
tokenExpiresAt: "",
|
|
@@ -90,7 +98,7 @@ async function readConfig() {
|
|
|
90
98
|
const config = JSON.parse(content);
|
|
91
99
|
|
|
92
100
|
return {
|
|
93
|
-
serverUrl: String(config.serverUrl ||
|
|
101
|
+
serverUrl: String(config.serverUrl || getDefaultServerUrl()).trim(),
|
|
94
102
|
username: String(config.username || "").trim(),
|
|
95
103
|
authToken: String(config.authToken || "").trim(),
|
|
96
104
|
tokenExpiresAt: String(config.tokenExpiresAt || "").trim(),
|
|
@@ -180,7 +188,7 @@ async function getCloudSyncStatus() {
|
|
|
180
188
|
}
|
|
181
189
|
|
|
182
190
|
async function authenticate(mode, payload) {
|
|
183
|
-
const serverUrl = normalizeServerUrl(payload.serverUrl);
|
|
191
|
+
const serverUrl = normalizeServerUrl(payload.serverUrl || getDefaultServerUrl());
|
|
184
192
|
const username = normalizeUsername(payload.username);
|
|
185
193
|
const password = normalizePassword(payload.password);
|
|
186
194
|
const endpointPath = mode === "register" ? "/api/auth/register" : "/api/auth/login";
|
|
@@ -244,10 +252,6 @@ async function logoutAccount() {
|
|
|
244
252
|
|
|
245
253
|
async function requireLoggedInConfig() {
|
|
246
254
|
const config = await readConfig();
|
|
247
|
-
if (!config.serverUrl) {
|
|
248
|
-
throw new Error("请先填写并登录同步服务器。");
|
|
249
|
-
}
|
|
250
|
-
|
|
251
255
|
if (!config.authToken) {
|
|
252
256
|
throw new Error("当前还没有登录同步账号。");
|
|
253
257
|
}
|
|
@@ -319,6 +323,7 @@ async function pullRemoteConfig(mode = "merge") {
|
|
|
319
323
|
|
|
320
324
|
module.exports = {
|
|
321
325
|
cloudSyncConfigPath,
|
|
326
|
+
getDefaultServerUrl,
|
|
322
327
|
getCloudSyncStatus,
|
|
323
328
|
loginAccount,
|
|
324
329
|
logoutAccount,
|
package/src/renderer/index.html
CHANGED
|
@@ -12,82 +12,226 @@
|
|
|
12
12
|
</head>
|
|
13
13
|
<body>
|
|
14
14
|
<div class="page-shell">
|
|
15
|
-
<
|
|
16
|
-
<div class="
|
|
17
|
-
<
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
<
|
|
15
|
+
<section id="authGate" class="auth-gate">
|
|
16
|
+
<div class="auth-card panel">
|
|
17
|
+
<div class="auth-form-card">
|
|
18
|
+
<div class="panel-header auth-panel-header">
|
|
19
|
+
<div>
|
|
20
|
+
<p class="eyebrow">CODEX ENDPOINT SWITCHER</p>
|
|
21
|
+
<p class="panel-kicker">Account Access</p>
|
|
22
|
+
<h2>账号登录</h2>
|
|
23
|
+
</div>
|
|
24
|
+
<span class="panel-tag" id="authAccessTag">未登录</span>
|
|
25
|
+
</div>
|
|
26
|
+
|
|
27
|
+
<form id="authForm" class="stack-form auth-form">
|
|
28
|
+
<label class="field">
|
|
29
|
+
<span>账号</span>
|
|
30
|
+
<input
|
|
31
|
+
id="authUsername"
|
|
32
|
+
type="text"
|
|
33
|
+
placeholder="输入你的账号"
|
|
34
|
+
autocomplete="username"
|
|
35
|
+
/>
|
|
36
|
+
</label>
|
|
37
|
+
<label class="field">
|
|
38
|
+
<span>密码</span>
|
|
39
|
+
<input
|
|
40
|
+
id="authPassword"
|
|
41
|
+
type="password"
|
|
42
|
+
placeholder="输入你的密码"
|
|
43
|
+
autocomplete="current-password"
|
|
44
|
+
/>
|
|
45
|
+
</label>
|
|
46
|
+
<p class="field-hint">
|
|
47
|
+
系统会自动连接同步服务。第一次使用先注册账号,之后直接登录。
|
|
48
|
+
</p>
|
|
49
|
+
<div class="action-row">
|
|
50
|
+
<button id="authLoginButton" type="submit" class="primary-button">登录进入</button>
|
|
51
|
+
<button id="authRegisterButton" type="button" class="secondary-button">
|
|
52
|
+
注册账号
|
|
53
|
+
</button>
|
|
54
|
+
</div>
|
|
55
|
+
</form>
|
|
56
|
+
|
|
57
|
+
<div id="authStatusBox" class="status-box info auth-status">
|
|
58
|
+
正在检查登录状态。
|
|
59
|
+
</div>
|
|
28
60
|
</div>
|
|
29
61
|
</div>
|
|
30
|
-
|
|
31
|
-
<div class="orb orb-a"></div>
|
|
32
|
-
<div class="orb orb-b"></div>
|
|
33
|
-
<div class="hero-badge" id="heroBadge">读取中</div>
|
|
34
|
-
</div>
|
|
35
|
-
</header>
|
|
62
|
+
</section>
|
|
36
63
|
|
|
37
|
-
<
|
|
38
|
-
<
|
|
39
|
-
<div class="
|
|
40
|
-
<div>
|
|
41
|
-
<p class="
|
|
42
|
-
<
|
|
64
|
+
<div id="appShell" class="app-shell" hidden>
|
|
65
|
+
<header class="workspace-bar panel">
|
|
66
|
+
<div class="workspace-main">
|
|
67
|
+
<div class="workspace-copy">
|
|
68
|
+
<p class="eyebrow">CODEX ENDPOINT SWITCHER</p>
|
|
69
|
+
<div class="workspace-title-row">
|
|
70
|
+
<h1>连接控制台</h1>
|
|
71
|
+
<div class="hero-badge workspace-badge" id="heroBadge">读取中</div>
|
|
72
|
+
</div>
|
|
73
|
+
<p class="workspace-text">
|
|
74
|
+
这里只管理 <code>备注</code>、<code>URL</code>、<code>Key</code>。切换时只更新
|
|
75
|
+
Codex 当前使用的 <code>base_url</code> 和 <code>OPENAI_API_KEY</code>。
|
|
76
|
+
</p>
|
|
77
|
+
</div>
|
|
78
|
+
|
|
79
|
+
<div class="workspace-actions">
|
|
80
|
+
<button id="refreshButton" class="ghost-button">刷新状态</button>
|
|
81
|
+
<button id="openCodexRootButton" class="ghost-button">打开 Codex 目录</button>
|
|
82
|
+
<button id="openEndpointStoreButton" class="ghost-button">打开连接数据文件</button>
|
|
43
83
|
</div>
|
|
44
|
-
<span class="panel-tag" id="activeEndpointTag">未识别</span>
|
|
45
84
|
</div>
|
|
46
|
-
<div class="
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
<
|
|
52
|
-
<
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
85
|
+
<div id="statusBox" class="status-box info workspace-status">应用已启动,正在读取当前连接。</div>
|
|
86
|
+
</header>
|
|
87
|
+
|
|
88
|
+
<main class="content-grid">
|
|
89
|
+
<div class="left-column">
|
|
90
|
+
<section class="panel current-panel">
|
|
91
|
+
<div class="panel-header">
|
|
92
|
+
<div>
|
|
93
|
+
<p class="panel-kicker">当前状态</p>
|
|
94
|
+
<h2>当前生效连接</h2>
|
|
95
|
+
</div>
|
|
96
|
+
<span class="panel-tag" id="activeEndpointTag">未识别</span>
|
|
97
|
+
</div>
|
|
98
|
+
<div class="current-grid">
|
|
99
|
+
<article class="metric-card">
|
|
100
|
+
<span class="metric-label">当前备注</span>
|
|
101
|
+
<strong id="currentNote">-</strong>
|
|
102
|
+
</article>
|
|
103
|
+
<article class="metric-card">
|
|
104
|
+
<span class="metric-label">提供商</span>
|
|
105
|
+
<strong id="currentProvider">-</strong>
|
|
106
|
+
</article>
|
|
107
|
+
<article class="metric-card">
|
|
108
|
+
<span class="metric-label">当前 Key</span>
|
|
109
|
+
<strong id="currentKeyMasked">-</strong>
|
|
110
|
+
</article>
|
|
111
|
+
<article class="metric-card">
|
|
112
|
+
<span class="metric-label">模型</span>
|
|
113
|
+
<strong id="currentModel">-</strong>
|
|
114
|
+
</article>
|
|
115
|
+
<article class="metric-card metric-card-wide">
|
|
116
|
+
<span class="metric-label">当前 URL</span>
|
|
117
|
+
<strong id="currentBaseUrl">-</strong>
|
|
118
|
+
</article>
|
|
119
|
+
<article class="metric-card metric-card-wide">
|
|
120
|
+
<span class="metric-label">热更新模式</span>
|
|
121
|
+
<strong id="proxyModeStatus">未开启</strong>
|
|
122
|
+
<p class="field-hint" id="proxyModeHint">
|
|
123
|
+
当前还是直连模式。开启后,后续同一 Codex 会话可在下一次请求时热更新到新 URL/Key。
|
|
124
|
+
</p>
|
|
125
|
+
<div class="action-row metric-actions">
|
|
126
|
+
<button id="enableProxyModeButton" type="button" class="secondary-button">
|
|
127
|
+
开启热更新模式
|
|
128
|
+
</button>
|
|
129
|
+
</div>
|
|
130
|
+
</article>
|
|
131
|
+
</div>
|
|
132
|
+
</section>
|
|
133
|
+
|
|
134
|
+
<section class="panel account-panel">
|
|
135
|
+
<div class="panel-header">
|
|
136
|
+
<div>
|
|
137
|
+
<p class="panel-kicker">Account Center</p>
|
|
138
|
+
<h2>账号与同步</h2>
|
|
139
|
+
</div>
|
|
140
|
+
<span class="panel-tag" id="cloudAccessTag">未登录</span>
|
|
141
|
+
</div>
|
|
142
|
+
|
|
143
|
+
<p class="field-hint account-hint">
|
|
144
|
+
账号空间保存连接配置,支持推送上传和拉取同步。
|
|
72
145
|
</p>
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
146
|
+
|
|
147
|
+
<div class="account-primary-grid">
|
|
148
|
+
<article class="account-primary-card">
|
|
149
|
+
<span class="metric-label">当前账号</span>
|
|
150
|
+
<strong id="cloudAccountName">-</strong>
|
|
151
|
+
</article>
|
|
152
|
+
<article class="account-primary-card">
|
|
153
|
+
<span class="metric-label">会话状态</span>
|
|
154
|
+
<strong id="cloudSessionStatus">未登录</strong>
|
|
155
|
+
</article>
|
|
156
|
+
</div>
|
|
157
|
+
|
|
158
|
+
<div class="account-inline-grid">
|
|
159
|
+
<article class="account-inline-card">
|
|
160
|
+
<span class="metric-label">同步服务</span>
|
|
161
|
+
<strong id="cloudServerStatus">自动连接</strong>
|
|
162
|
+
</article>
|
|
163
|
+
<article class="account-inline-card account-inline-card-wide">
|
|
164
|
+
<span class="metric-label">同步记录</span>
|
|
165
|
+
<strong id="cloudLastSyncStatus">还没有推送或拉取记录</strong>
|
|
166
|
+
</article>
|
|
167
|
+
</div>
|
|
168
|
+
|
|
169
|
+
<div class="account-actions-grid account-actions-grid-compact">
|
|
170
|
+
<button id="openSwitchAccountButton" type="button" class="ghost-button">
|
|
171
|
+
切换账号
|
|
172
|
+
</button>
|
|
173
|
+
<button id="cloudLogoutButton" type="button" class="danger-button">退出登录</button>
|
|
174
|
+
</div>
|
|
175
|
+
|
|
176
|
+
<div class="account-actions-grid account-actions-grid-sync">
|
|
177
|
+
<button id="cloudPushButton" type="button" class="secondary-button">
|
|
178
|
+
推送当前连接
|
|
179
|
+
</button>
|
|
180
|
+
<button id="cloudPullMergeButton" type="button" class="primary-button">
|
|
181
|
+
合并拉取
|
|
182
|
+
</button>
|
|
183
|
+
<button id="cloudPullReplaceButton" type="button" class="danger-button">
|
|
184
|
+
覆盖拉取
|
|
76
185
|
</button>
|
|
77
186
|
</div>
|
|
78
|
-
</
|
|
187
|
+
</section>
|
|
79
188
|
</div>
|
|
80
|
-
</section>
|
|
81
189
|
|
|
82
|
-
|
|
83
|
-
|
|
190
|
+
<section class="panel profiles-panel">
|
|
191
|
+
<div class="panel-header">
|
|
192
|
+
<div>
|
|
193
|
+
<p class="panel-kicker">Endpoints</p>
|
|
194
|
+
<h2>连接列表</h2>
|
|
195
|
+
</div>
|
|
196
|
+
<div class="panel-header-actions">
|
|
197
|
+
<span class="profile-count" id="endpointCount">0 条</span>
|
|
198
|
+
<button id="openCreateEndpointButton" type="button" class="primary-button">
|
|
199
|
+
新增连接
|
|
200
|
+
</button>
|
|
201
|
+
</div>
|
|
202
|
+
</div>
|
|
203
|
+
<div id="endpointsList" class="profiles-list"></div>
|
|
204
|
+
</section>
|
|
205
|
+
</main>
|
|
206
|
+
</div>
|
|
207
|
+
|
|
208
|
+
<div id="endpointModal" class="modal-shell" hidden>
|
|
209
|
+
<div class="modal-backdrop" data-modal-close="true"></div>
|
|
210
|
+
<section
|
|
211
|
+
class="modal-card"
|
|
212
|
+
role="dialog"
|
|
213
|
+
aria-modal="true"
|
|
214
|
+
aria-labelledby="endpointModalTitle"
|
|
215
|
+
>
|
|
216
|
+
<div class="modal-header">
|
|
84
217
|
<div>
|
|
85
|
-
<p class="panel-kicker"
|
|
86
|
-
<h2
|
|
218
|
+
<p class="panel-kicker">Endpoint Editor</p>
|
|
219
|
+
<h2 id="endpointModalTitle">新增连接</h2>
|
|
220
|
+
<p id="endpointModalText" class="modal-text">
|
|
221
|
+
填写备注、URL、Key,保存后就能直接加入切换列表。
|
|
222
|
+
</p>
|
|
87
223
|
</div>
|
|
224
|
+
<button
|
|
225
|
+
id="closeEndpointModalButton"
|
|
226
|
+
type="button"
|
|
227
|
+
class="icon-button"
|
|
228
|
+
aria-label="关闭弹窗"
|
|
229
|
+
>
|
|
230
|
+
×
|
|
231
|
+
</button>
|
|
88
232
|
</div>
|
|
89
233
|
|
|
90
|
-
<form id="endpointForm" class="stack-form">
|
|
234
|
+
<form id="endpointForm" class="stack-form endpoint-form">
|
|
91
235
|
<input id="endpointId" type="hidden" />
|
|
92
236
|
<label class="field">
|
|
93
237
|
<span>备注</span>
|
|
@@ -119,103 +263,11 @@
|
|
|
119
263
|
<p class="field-hint">连接数据仅保存在本机 `~/.codex/endpoint-presets.json`。</p>
|
|
120
264
|
<div class="action-row">
|
|
121
265
|
<button id="saveEndpointButton" type="submit" class="primary-button">新增连接</button>
|
|
122
|
-
<button id="clearFormButton" type="button" class="ghost-button"
|
|
266
|
+
<button id="clearFormButton" type="button" class="ghost-button">重置表单</button>
|
|
123
267
|
</div>
|
|
124
268
|
</form>
|
|
125
|
-
|
|
126
|
-
<div class="stack-form stack-form-alt">
|
|
127
|
-
<div>
|
|
128
|
-
<p class="panel-kicker">Cloud Sync</p>
|
|
129
|
-
<h3>账号配置同步</h3>
|
|
130
|
-
</div>
|
|
131
|
-
<label class="field">
|
|
132
|
-
<span>同步服务器</span>
|
|
133
|
-
<input
|
|
134
|
-
id="cloudServerUrl"
|
|
135
|
-
type="text"
|
|
136
|
-
placeholder="例如:http://127.0.0.1:3190"
|
|
137
|
-
autocomplete="off"
|
|
138
|
-
/>
|
|
139
|
-
</label>
|
|
140
|
-
<label class="field">
|
|
141
|
-
<span>账号</span>
|
|
142
|
-
<input
|
|
143
|
-
id="cloudUsername"
|
|
144
|
-
type="text"
|
|
145
|
-
placeholder="例如:yourname"
|
|
146
|
-
autocomplete="off"
|
|
147
|
-
/>
|
|
148
|
-
</label>
|
|
149
|
-
<label class="field">
|
|
150
|
-
<span>密码</span>
|
|
151
|
-
<input
|
|
152
|
-
id="cloudPassword"
|
|
153
|
-
type="password"
|
|
154
|
-
placeholder="输入同步账号密码"
|
|
155
|
-
autocomplete="off"
|
|
156
|
-
/>
|
|
157
|
-
</label>
|
|
158
|
-
<p class="field-hint">
|
|
159
|
-
账号空间里保存的是你的连接配置。推送是把本机连接上传到账号,拉取是把账号里的连接同步回本机。
|
|
160
|
-
</p>
|
|
161
|
-
<div class="sync-status-grid">
|
|
162
|
-
<article class="sync-status-card">
|
|
163
|
-
<span class="metric-label">登录状态</span>
|
|
164
|
-
<strong id="cloudAuthStatus">未登录</strong>
|
|
165
|
-
</article>
|
|
166
|
-
<article class="sync-status-card">
|
|
167
|
-
<span class="metric-label">服务器</span>
|
|
168
|
-
<strong id="cloudRemoteStatus">未设置</strong>
|
|
169
|
-
</article>
|
|
170
|
-
<article class="sync-status-card sync-status-card-wide">
|
|
171
|
-
<span class="metric-label">同步记录</span>
|
|
172
|
-
<strong id="cloudLastSyncStatus">还没有推送或拉取记录</strong>
|
|
173
|
-
</article>
|
|
174
|
-
</div>
|
|
175
|
-
<div class="action-row">
|
|
176
|
-
<button id="cloudRegisterButton" type="button" class="secondary-button">
|
|
177
|
-
注册账号
|
|
178
|
-
</button>
|
|
179
|
-
<button id="cloudLoginButton" type="button" class="primary-button">
|
|
180
|
-
登录账号
|
|
181
|
-
</button>
|
|
182
|
-
<button id="cloudLogoutButton" type="button" class="ghost-button">退出登录</button>
|
|
183
|
-
</div>
|
|
184
|
-
<div class="action-row">
|
|
185
|
-
<button id="cloudPushButton" type="button" class="secondary-button">
|
|
186
|
-
推送当前连接
|
|
187
|
-
</button>
|
|
188
|
-
<button id="cloudPullMergeButton" type="button" class="primary-button">
|
|
189
|
-
合并拉取
|
|
190
|
-
</button>
|
|
191
|
-
<button id="cloudPullReplaceButton" type="button" class="danger-button">
|
|
192
|
-
覆盖拉取
|
|
193
|
-
</button>
|
|
194
|
-
</div>
|
|
195
|
-
</div>
|
|
196
|
-
</section>
|
|
197
|
-
|
|
198
|
-
<section class="panel profiles-panel">
|
|
199
|
-
<div class="panel-header">
|
|
200
|
-
<div>
|
|
201
|
-
<p class="panel-kicker">Endpoints</p>
|
|
202
|
-
<h2>连接列表</h2>
|
|
203
|
-
</div>
|
|
204
|
-
<span class="profile-count" id="endpointCount">0 条</span>
|
|
205
|
-
</div>
|
|
206
|
-
<div id="endpointsList" class="profiles-list"></div>
|
|
207
|
-
</section>
|
|
208
|
-
|
|
209
|
-
<section class="panel footer-panel">
|
|
210
|
-
<div class="panel-header">
|
|
211
|
-
<div>
|
|
212
|
-
<p class="panel-kicker">输出</p>
|
|
213
|
-
<h2>执行结果</h2>
|
|
214
|
-
</div>
|
|
215
|
-
</div>
|
|
216
|
-
<div id="statusBox" class="status-box info">应用已启动,正在读取当前连接。</div>
|
|
217
269
|
</section>
|
|
218
|
-
</
|
|
270
|
+
</div>
|
|
219
271
|
</div>
|
|
220
272
|
|
|
221
273
|
<script src="./renderer.js"></script>
|