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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "codex-endpoint-switcher",
3
- "version": "1.1.0",
3
+ "version": "1.2.0",
4
4
  "description": "用于切换 Codex URL 和 Key 的本地网页控制台与 npm CLI",
5
5
  "main": "src/main/main.js",
6
6
  "bin": {
@@ -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 || "").trim(),
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,
@@ -12,82 +12,226 @@
12
12
  </head>
13
13
  <body>
14
14
  <div class="page-shell">
15
- <header class="hero-card">
16
- <div class="hero-copy">
17
- <p class="eyebrow">CODEX ENDPOINT SWITCHER</p>
18
- <h1>只切换 URL 和 Key</h1>
19
- <p class="hero-text">
20
- 你只需要维护 <code>备注</code>、<code>URL</code>、<code>Key</code>。
21
- 切换时应用只会更新 <code>config.toml</code> 的 <code>base_url</code>
22
- <code>auth.json</code> 的 <code>OPENAI_API_KEY</code>,其它配置保持不动。
23
- </p>
24
- <div class="hero-actions">
25
- <button id="refreshButton" class="ghost-button">刷新状态</button>
26
- <button id="openCodexRootButton" class="ghost-button">打开 Codex 目录</button>
27
- <button id="openEndpointStoreButton" class="ghost-button">打开连接数据文件</button>
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
- <div class="hero-side">
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
- <main class="content-grid">
38
- <section class="panel current-panel">
39
- <div class="panel-header">
40
- <div>
41
- <p class="panel-kicker">当前状态</p>
42
- <h2>当前生效连接</h2>
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="current-grid">
47
- <article class="metric-card">
48
- <span class="metric-label">当前备注</span>
49
- <strong id="currentNote">-</strong>
50
- </article>
51
- <article class="metric-card">
52
- <span class="metric-label">提供商</span>
53
- <strong id="currentProvider">-</strong>
54
- </article>
55
- <article class="metric-card metric-card-wide">
56
- <span class="metric-label">当前 URL</span>
57
- <strong id="currentBaseUrl">-</strong>
58
- </article>
59
- <article class="metric-card">
60
- <span class="metric-label">当前 Key</span>
61
- <strong id="currentKeyMasked">-</strong>
62
- </article>
63
- <article class="metric-card">
64
- <span class="metric-label">模型</span>
65
- <strong id="currentModel">-</strong>
66
- </article>
67
- <article class="metric-card metric-card-wide">
68
- <span class="metric-label">热更新模式</span>
69
- <strong id="proxyModeStatus">未开启</strong>
70
- <p class="field-hint" id="proxyModeHint">
71
- 当前还是直连模式。开启后,后续同一 Codex 会话可在下一次请求时热更新到新 URL/Key。
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
- <div class="action-row metric-actions">
74
- <button id="enableProxyModeButton" type="button" class="secondary-button">
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
- </article>
187
+ </section>
79
188
  </div>
80
- </section>
81
189
 
82
- <section class="panel action-panel">
83
- <div class="panel-header">
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">编辑区</p>
86
- <h2>新增或编辑连接</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">清空表单</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
- </main>
270
+ </div>
219
271
  </div>
220
272
 
221
273
  <script src="./renderer.js"></script>