codex-endpoint-switcher 1.2.0 → 1.3.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.2.0",
3
+ "version": "1.3.0",
4
4
  "description": "用于切换 Codex URL 和 Key 的本地网页控制台与 npm CLI",
5
5
  "main": "src/main/main.js",
6
6
  "bin": {
@@ -58,13 +58,13 @@
58
58
  "x64"
59
59
  ]
60
60
  }
61
- ],
62
- "artifactName": "CodexProfileDesktop-${version}-portable.${ext}"
61
+ ]
63
62
  },
64
63
  "portable": {
65
64
  "artifactName": "CodexProfileDesktop-${version}-portable.${ext}"
66
65
  },
67
66
  "nsis": {
67
+ "artifactName": "CodexProfileDesktop-${version}-installer.${ext}",
68
68
  "oneClick": false,
69
69
  "allowToChangeInstallationDirectory": true
70
70
  }
@@ -33,7 +33,7 @@ function getDefaultServerUrl() {
33
33
  return normalizeServerUrl(
34
34
  process.env.CODEX_SYNC_SERVER_URL ||
35
35
  process.env.SYNC_SERVER_PUBLIC_URL ||
36
- "http://cd.xdo.icu:18245",
36
+ "https://codexqh.zhang-fy.top",
37
37
  );
38
38
  }
39
39
 
@@ -62,29 +62,6 @@
62
62
  </section>
63
63
 
64
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>
83
- </div>
84
- </div>
85
- <div id="statusBox" class="status-box info workspace-status">应用已启动,正在读取当前连接。</div>
86
- </header>
87
-
88
65
  <main class="content-grid">
89
66
  <div class="left-column">
90
67
  <section class="panel current-panel">
@@ -95,6 +72,17 @@
95
72
  </div>
96
73
  <span class="panel-tag" id="activeEndpointTag">未识别</span>
97
74
  </div>
75
+ <div class="current-summary">
76
+ <p class="current-copy">
77
+ 这里只管理 <code>备注</code>、<code>URL</code>、<code>Key</code>。切换时只更新
78
+ Codex 当前使用的 <code>base_url</code> 和 <code>OPENAI_API_KEY</code>。
79
+ </p>
80
+ </div>
81
+ <div class="current-tools">
82
+ <button id="refreshButton" class="ghost-button">刷新状态</button>
83
+ <button id="openCodexRootButton" class="ghost-button">打开 Codex 目录</button>
84
+ <button id="openEndpointStoreButton" class="ghost-button">打开连接数据文件</button>
85
+ </div>
98
86
  <div class="current-grid">
99
87
  <article class="metric-card">
100
88
  <span class="metric-label">当前备注</span>
@@ -112,22 +100,6 @@
112
100
  <span class="metric-label">模型</span>
113
101
  <strong id="currentModel">-</strong>
114
102
  </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
103
  </div>
132
104
  </section>
133
105
 
@@ -160,7 +132,7 @@
160
132
  <span class="metric-label">同步服务</span>
161
133
  <strong id="cloudServerStatus">自动连接</strong>
162
134
  </article>
163
- <article class="account-inline-card account-inline-card-wide">
135
+ <article class="account-inline-card">
164
136
  <span class="metric-label">同步记录</span>
165
137
  <strong id="cloudLastSyncStatus">还没有推送或拉取记录</strong>
166
138
  </article>
@@ -109,6 +109,15 @@ function $(selector) {
109
109
  return document.querySelector(selector);
110
110
  }
111
111
 
112
+ function setText(selector, value) {
113
+ const element = $(selector);
114
+ if (!element) {
115
+ return;
116
+ }
117
+
118
+ element.textContent = value;
119
+ }
120
+
112
121
  function formatDateTime(value) {
113
122
  if (!value) {
114
123
  return "-";
@@ -126,23 +135,18 @@ function formatDateTime(value) {
126
135
  }
127
136
 
128
137
  function setStatus(message, type = "info") {
129
- const statusBox = $("#statusBox");
130
- if (!statusBox) {
131
- return;
132
- }
133
-
134
- statusBox.textContent = message;
135
- statusBox.className = `status-box ${type}`;
138
+ void message;
139
+ void type;
136
140
  }
137
141
 
138
142
  function setAuthStatus(message, type = "info") {
139
- const statusBox = $("#authStatusBox");
140
- if (!statusBox) {
143
+ const authStatusElement = $("#authStatusBox");
144
+ if (!authStatusElement) {
141
145
  return;
142
146
  }
143
147
 
144
- statusBox.textContent = message;
145
- statusBox.className = `status-box ${type} auth-status`;
148
+ authStatusElement.textContent = message;
149
+ authStatusElement.className = `status-box ${type} auth-status`;
146
150
  }
147
151
 
148
152
  function syncFieldValue(selector, value) {
@@ -228,25 +232,11 @@ function renderCurrent() {
228
232
  return;
229
233
  }
230
234
 
231
- $("#activeEndpointTag").textContent = current.currentNote || "未识别";
232
- $("#currentNote").textContent = current.currentNote || "未识别";
233
- $("#currentProvider").textContent = current.provider || "-";
234
- $("#currentBaseUrl").textContent = current.currentUrl || "-";
235
- $("#currentKeyMasked").textContent = current.currentKeyMasked || "-";
236
- $("#currentModel").textContent = current.model || "-";
237
- $("#proxyModeStatus").textContent = current.proxyModeEnabled
238
- ? `已开启,固定代理:${current.proxyBaseUrl}`
239
- : "未开启";
240
- $("#proxyModeHint").textContent = current.proxyModeEnabled
241
- ? "当前 Codex 只要已经接入本地代理,后续同一会话内切换连接会在下一次请求时热更新。"
242
- : "当前还是直连模式。开启后需要把已打开的旧 Codex 会话重开一次;之后同一会话即可热更新。";
243
- $("#enableProxyModeButton").disabled = current.proxyModeEnabled;
244
- $("#enableProxyModeButton").textContent = current.proxyModeEnabled
245
- ? "热更新模式已开启"
246
- : "开启热更新模式";
247
- $("#heroBadge").textContent = current.currentNote
248
- ? `${current.proxyModeEnabled ? "热更新中" : "当前连接"}:${current.currentNote}`
249
- : "当前未匹配到已保存连接";
235
+ setText("#activeEndpointTag", current.currentNote || "未识别");
236
+ setText("#currentNote", current.currentNote || "未识别");
237
+ setText("#currentProvider", current.provider || "-");
238
+ setText("#currentKeyMasked", current.currentKeyMasked || "-");
239
+ setText("#currentModel", current.model || "-");
250
240
  }
251
241
 
252
242
  function renderCloudStatus() {
@@ -258,22 +248,12 @@ function renderCloudStatus() {
258
248
  syncFieldValue("#authUsername", cloud.username);
259
249
 
260
250
  const accountName = cloud.remoteUser || cloud.username || "-";
261
- const serverText = !cloud.serverUrl
262
- ? "未配置"
263
- : cloud.loggedIn
264
- ? "已自动连接"
265
- : cloud.lastError
266
- ? "连接异常"
267
- : "等待登录";
268
- const sessionText = !cloud.serverUrl
269
- ? "未配置服务器"
270
- : cloud.loggedIn
271
- ? cloud.tokenExpiresAt
272
- ? `已登录 · 到期 ${formatDateTime(cloud.tokenExpiresAt)}`
273
- : "已登录"
274
- : cloud.hasToken
275
- ? "登录态失效,请重新登录"
276
- : "未登录";
251
+ const serverText = cloud.loggedIn ? "已连接" : cloud.lastError ? "连接异常" : "等待登录";
252
+ const sessionText = cloud.loggedIn
253
+ ? "已登录"
254
+ : cloud.hasToken
255
+ ? "登录态失效,请重新登录"
256
+ : "未登录";
277
257
 
278
258
  const syncStatusParts = [];
279
259
  if (cloud.lastPushAt) {
@@ -483,29 +463,6 @@ async function handleSwitchEndpoint(id, note) {
483
463
  }
484
464
  }
485
465
 
486
- async function handleEnableProxyMode() {
487
- try {
488
- setStatus("正在切到热更新代理模式 ...", "info");
489
- const result = await bridge.enableProxyMode();
490
- await refreshDashboard(false);
491
- if (result.oneTimeRestartRequired) {
492
- setStatus(
493
- "热更新模式已开启。请把当前已经打开着的旧 Codex 会话重开一次;之后同一会话内即可热更新。",
494
- "success",
495
- );
496
- } else {
497
- setStatus("热更新模式已开启。当前会话后续请求可直接热更新到新连接。", "success");
498
- }
499
- } catch (error) {
500
- if (isUnauthorizedError(error)) {
501
- await handleAccessRevoked(error.message);
502
- return;
503
- }
504
-
505
- setStatus(`开启热更新模式失败:${error.message}`, "error");
506
- }
507
- }
508
-
509
466
  function getAuthFormPayload() {
510
467
  return {
511
468
  username: $("#authUsername").value.trim(),
@@ -712,7 +669,6 @@ function bindEvents() {
712
669
  resetForm();
713
670
  setStatus("已重置表单,可作为新连接保存。", "info");
714
671
  });
715
- $("#enableProxyModeButton").addEventListener("click", handleEnableProxyMode);
716
672
  $("#cloudLogoutButton").addEventListener("click", handleCloudLogout);
717
673
  $("#openSwitchAccountButton").addEventListener("click", handleCloudLogout);
718
674
  $("#cloudPushButton").addEventListener("click", handleCloudPush);
@@ -61,13 +61,7 @@ code {
61
61
 
62
62
  .app-shell {
63
63
  height: 100%;
64
- display: grid;
65
- grid-template-rows: auto minmax(0, 1fr);
66
- gap: 16px;
67
- }
68
-
69
- .mobile-section-nav {
70
- display: none;
64
+ display: block;
71
65
  }
72
66
 
73
67
  .app-shell[hidden],
@@ -75,7 +69,6 @@ code {
75
69
  display: none;
76
70
  }
77
71
 
78
- .workspace-bar,
79
72
  .panel {
80
73
  backdrop-filter: blur(18px);
81
74
  -webkit-backdrop-filter: blur(18px);
@@ -93,7 +86,6 @@ code {
93
86
  font-weight: 700;
94
87
  }
95
88
 
96
- .workspace-copy h1,
97
89
  .panel-header h2,
98
90
  .profile-heading h3,
99
91
  .modal-header h2 {
@@ -143,67 +135,6 @@ code {
143
135
  margin-top: 2px;
144
136
  }
145
137
 
146
- .workspace-bar {
147
- display: grid;
148
- gap: 14px;
149
- padding: 18px 20px;
150
- border-radius: var(--radius-xl);
151
- background:
152
- linear-gradient(135deg, rgba(255, 252, 248, 0.94), rgba(247, 238, 230, 0.9)),
153
- var(--panel);
154
- }
155
-
156
- .workspace-main {
157
- display: grid;
158
- grid-template-columns: minmax(0, 1fr) auto;
159
- gap: 18px;
160
- align-items: start;
161
- }
162
-
163
- .workspace-copy h1 {
164
- font-size: clamp(1.5rem, 2.2vw, 2.2rem);
165
- line-height: 1.05;
166
- }
167
-
168
- .workspace-title-row {
169
- display: flex;
170
- align-items: center;
171
- justify-content: space-between;
172
- gap: 12px;
173
- }
174
-
175
- .workspace-text {
176
- max-width: 760px;
177
- margin: 10px 0 0;
178
- color: var(--muted);
179
- font-size: 0.94rem;
180
- line-height: 1.55;
181
- }
182
-
183
- .workspace-actions {
184
- display: flex;
185
- flex-wrap: wrap;
186
- justify-content: flex-end;
187
- gap: 10px;
188
- }
189
-
190
- .hero-badge {
191
- display: inline-flex;
192
- align-items: center;
193
- padding: 12px 18px;
194
- border-radius: 999px;
195
- background: rgba(255, 255, 255, 0.82);
196
- border: 1px solid rgba(47, 36, 30, 0.08);
197
- color: var(--text);
198
- font-weight: 700;
199
- }
200
-
201
- .workspace-badge {
202
- flex: 0 0 auto;
203
- padding: 10px 14px;
204
- white-space: nowrap;
205
- }
206
-
207
138
  .content-grid {
208
139
  display: grid;
209
140
  grid-template-columns: 0.84fr 1.16fr;
@@ -216,7 +147,7 @@ code {
216
147
  .left-column {
217
148
  min-height: 0;
218
149
  display: grid;
219
- grid-template-rows: minmax(0, 0.9fr) minmax(0, 1.1fr);
150
+ grid-template-rows: auto minmax(0, 1fr);
220
151
  gap: 20px;
221
152
  overflow: hidden;
222
153
  }
@@ -259,30 +190,28 @@ code {
259
190
  font-weight: 700;
260
191
  }
261
192
 
262
- .mobile-section-button {
263
- height: 40px;
264
- padding: 0 14px;
265
- border: 1px solid rgba(47, 36, 30, 0.1);
266
- border-radius: 999px;
267
- background: rgba(255, 255, 255, 0.68);
268
- color: var(--text);
269
- cursor: pointer;
270
- transition:
271
- background 0.18s ease,
272
- color 0.18s ease,
273
- box-shadow 0.18s ease;
193
+ .current-grid {
194
+ display: grid;
195
+ grid-template-columns: repeat(4, minmax(0, 1fr));
196
+ gap: 6px;
274
197
  }
275
198
 
276
- .mobile-section-button.is-active {
277
- background: linear-gradient(135deg, #d86135, #b1431d);
278
- color: #fff;
279
- box-shadow: 0 12px 28px rgba(179, 67, 29, 0.24);
199
+ .current-summary {
200
+ margin-bottom: 6px;
280
201
  }
281
202
 
282
- .current-grid {
203
+ .current-copy {
204
+ margin: 0;
205
+ color: var(--muted);
206
+ font-size: 0.84rem;
207
+ line-height: 1.4;
208
+ }
209
+
210
+ .current-tools {
283
211
  display: grid;
284
- grid-template-columns: repeat(4, minmax(0, 1fr));
212
+ grid-template-columns: repeat(3, minmax(0, 1fr));
285
213
  gap: 8px;
214
+ margin-bottom: 6px;
286
215
  }
287
216
 
288
217
  .metric-card {
@@ -300,30 +229,32 @@ code {
300
229
  }
301
230
 
302
231
  .current-panel .panel-header {
303
- margin-bottom: 8px;
232
+ margin-bottom: 6px;
233
+ }
234
+
235
+ .current-panel {
236
+ align-self: start;
237
+ display: grid;
238
+ align-content: start;
304
239
  }
305
240
 
306
241
  .current-panel .metric-card {
307
- padding: 6px 9px;
242
+ padding: 5px 8px;
308
243
  }
309
244
 
310
245
  .current-panel .metric-card strong {
311
- margin-top: 3px;
312
- font-size: 0.9rem;
246
+ margin-top: 2px;
247
+ font-size: 0.88rem;
313
248
  }
314
249
 
315
250
  .current-panel .metric-label {
316
- font-size: 0.78rem;
251
+ font-size: 0.76rem;
317
252
  }
318
253
 
319
254
  .current-panel .secondary-button {
320
255
  height: 32px;
321
256
  }
322
257
 
323
- .metric-card-wide {
324
- grid-column: 1 / -1;
325
- }
326
-
327
258
  .metric-label {
328
259
  color: var(--muted);
329
260
  font-size: 0.88rem;
@@ -452,7 +383,7 @@ input[readonly] {
452
383
 
453
384
  .account-inline-grid {
454
385
  display: grid;
455
- grid-template-columns: 180px minmax(0, 1fr);
386
+ grid-template-columns: repeat(2, minmax(0, 1fr));
456
387
  gap: 8px;
457
388
  }
458
389
 
@@ -558,16 +489,20 @@ input[readonly] {
558
489
 
559
490
  .profiles-list {
560
491
  display: grid;
492
+ grid-auto-rows: minmax(190px, auto);
493
+ align-content: start;
561
494
  flex: 1 1 auto;
562
495
  gap: 14px;
563
496
  min-height: 0;
564
497
  overflow: auto;
565
498
  padding-right: 6px;
499
+ padding-bottom: 6px;
566
500
  }
567
501
 
568
502
  .profiles-panel {
569
503
  display: flex;
570
504
  flex-direction: column;
505
+ min-height: 0;
571
506
  overflow: hidden;
572
507
  }
573
508
 
@@ -592,11 +527,14 @@ input[readonly] {
592
527
 
593
528
  .profile-card {
594
529
  display: grid;
530
+ grid-template-rows: auto auto;
595
531
  gap: 14px;
532
+ min-height: 100%;
596
533
  padding: 18px;
597
534
  border-radius: var(--radius-md);
598
535
  background: var(--panel-strong);
599
536
  border: 1px solid rgba(47, 36, 30, 0.08);
537
+ overflow: visible;
600
538
  }
601
539
 
602
540
  .profile-card.active {
@@ -621,6 +559,7 @@ input[readonly] {
621
559
  .profile-heading {
622
560
  display: grid;
623
561
  gap: 8px;
562
+ min-width: 0;
624
563
  }
625
564
 
626
565
  .profile-badges {
@@ -646,7 +585,10 @@ input[readonly] {
646
585
 
647
586
  .profile-meta {
648
587
  display: grid;
588
+ grid-template-rows: repeat(3, auto);
649
589
  gap: 8px;
590
+ min-height: 0;
591
+ align-content: start;
650
592
  }
651
593
 
652
594
  .profile-meta span {
@@ -654,10 +596,14 @@ input[readonly] {
654
596
  gap: 4px;
655
597
  color: var(--muted);
656
598
  font-size: 0.92rem;
599
+ min-width: 0;
657
600
  }
658
601
 
659
602
  .profile-meta strong {
660
603
  color: var(--text);
604
+ white-space: nowrap;
605
+ overflow: hidden;
606
+ text-overflow: ellipsis;
661
607
  }
662
608
 
663
609
  .profile-path {
@@ -700,12 +646,6 @@ input[readonly] {
700
646
  color: var(--error);
701
647
  }
702
648
 
703
- .workspace-status {
704
- min-height: 0;
705
- padding: 12px 14px;
706
- background: rgba(255, 255, 255, 0.72);
707
- }
708
-
709
649
  .account-panel {
710
650
  display: grid;
711
651
  gap: 7px;
@@ -787,10 +727,6 @@ input[readonly] {
787
727
  margin-top: 0;
788
728
  }
789
729
 
790
- #proxyModeHint {
791
- display: none;
792
- }
793
-
794
730
  @media (max-width: 1160px) {
795
731
  body {
796
732
  overflow: auto;
@@ -806,7 +742,6 @@ input[readonly] {
806
742
  height: auto;
807
743
  }
808
744
 
809
- .workspace-main,
810
745
  .content-grid {
811
746
  grid-template-columns: 1fr;
812
747
  }
@@ -816,12 +751,6 @@ input[readonly] {
816
751
  overflow: visible;
817
752
  }
818
753
 
819
- #proxyModeHint {
820
- display: block;
821
- }
822
-
823
- .workspace-title-row,
824
- .workspace-actions,
825
754
  .panel-header-actions {
826
755
  justify-content: flex-start;
827
756
  }
@@ -869,28 +798,21 @@ input[readonly] {
869
798
  }
870
799
 
871
800
  .auth-form-card,
872
- .workspace-bar,
873
801
  .panel {
874
802
  border-radius: 20px;
875
803
  padding: 14px;
876
804
  }
877
805
 
878
- .workspace-title-row {
879
- flex-direction: column;
880
- align-items: flex-start;
881
- }
882
-
883
- .workspace-text,
884
806
  .account-hint {
885
807
  font-size: 0.82rem;
886
808
  line-height: 1.4;
887
809
  }
888
810
 
889
- .workspace-actions {
890
- width: 100%;
811
+ .current-tools {
812
+ grid-template-columns: repeat(2, minmax(0, 1fr));
891
813
  }
892
814
 
893
- .workspace-actions button,
815
+ .current-tools button,
894
816
  .action-row button,
895
817
  .account-actions-grid button,
896
818
  .card-actions button {
@@ -902,6 +824,12 @@ input[readonly] {
902
824
  gap: 12px;
903
825
  }
904
826
 
827
+ .current-tools {
828
+ display: grid;
829
+ grid-template-columns: repeat(2, minmax(0, 1fr));
830
+ gap: 8px;
831
+ }
832
+
905
833
  .current-panel,
906
834
  .account-panel,
907
835
  .profiles-panel {
@@ -941,6 +869,7 @@ input[readonly] {
941
869
  .profile-card {
942
870
  gap: 10px;
943
871
  padding: 14px;
872
+ min-height: 0;
944
873
  }
945
874
 
946
875
  .profile-topline {
@@ -964,6 +893,30 @@ input[readonly] {
964
893
  }
965
894
  }
966
895
 
896
+ @media (max-width: 560px) {
897
+ .current-tools {
898
+ grid-template-columns: 1fr;
899
+ }
900
+
901
+ .profiles-list {
902
+ grid-auto-rows: auto;
903
+ }
904
+
905
+ .profile-card {
906
+ height: auto;
907
+ }
908
+
909
+ .profile-meta {
910
+ grid-template-rows: none;
911
+ }
912
+
913
+ .profile-meta strong {
914
+ white-space: normal;
915
+ overflow: visible;
916
+ text-overflow: clip;
917
+ }
918
+ }
919
+
967
920
  @media (max-height: 920px) and (min-width: 1161px) {
968
921
  .page-shell {
969
922
  padding: 12px;
@@ -974,18 +927,10 @@ input[readonly] {
974
927
  padding: 16px;
975
928
  }
976
929
 
977
- .workspace-bar {
978
- padding: 16px 18px;
979
- }
980
-
981
930
  .panel {
982
931
  padding: 16px;
983
932
  }
984
933
 
985
- .workspace-copy h1 {
986
- font-size: clamp(1.35rem, 1.8vw, 1.9rem);
987
- }
988
-
989
934
  .metric-card {
990
935
  padding: 12px;
991
936
  }