-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathindex.html
More file actions
730 lines (694 loc) · 38.3 KB
/
Copy pathindex.html
File metadata and controls
730 lines (694 loc) · 38.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Codex Dock</title>
<link rel="icon" href='data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 32 32"><rect width="32" height="32" rx="8" fill="%23f9f9f9"/><path d="M9 10h14M9 16h14M9 22h14" stroke="%230d0d0d" stroke-width="2" stroke-linecap="round"/></svg>' />
<link rel="stylesheet" href="styles.css?v=20260525-oauth-primary2" />
</head>
<body>
<div class="shell">
<aside class="sidebar" aria-label="主导航">
<div class="sidebar-top">
<div class="brand-lockup">
<div class="brand-mark" aria-label="Codex Dock">
<svg class="brand-logo" viewBox="0 0 32 32" aria-hidden="true">
<g fill="none" stroke="currentColor" stroke-width="1.65" stroke-linecap="round" stroke-linejoin="round">
<path d="M8 9h16"></path>
<path d="M8 16h16"></path>
<path d="M8 23h16"></path>
<path d="M12 6v20"></path>
<path d="M20 6v20"></path>
</g>
</svg>
</div>
<strong>Codex Dock</strong>
</div>
<button class="collapse-button" id="collapseSidebarBtn" type="button" aria-label="隐藏侧边栏" aria-expanded="true">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M15 6l-6 6 6 6"></path></svg>
</button>
</div>
<nav class="nav-list">
<button class="nav-item active" type="button" data-view="accounts">
<span class="nav-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M7 7h10"></path><path d="M7 12h10"></path><path d="M7 17h7"></path><circle cx="18" cy="17" r="2.2"></circle></svg>
</span>
<span>账号池</span>
</button>
<button class="nav-item" type="button" data-view="helper">
<span class="nav-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M5 6.5h14v8.5H5z"></path><path d="M9 19h6"></path><path d="M12 15v4"></path><path d="M10 10.7h4"></path></svg>
</span>
<span>Dock Agent</span>
</button>
<button class="nav-item" type="button" id="openSettingsNav">
<span class="nav-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M5 7h14"></path><path d="M5 12h14"></path><path d="M5 17h14"></path><path d="M9 5v4"></path><path d="M15 10v4"></path><path d="M11 15v4"></path></svg>
</span>
<span>设置</span>
</button>
<button class="nav-item admin-only" type="button" data-view="admin" hidden>
<span class="nav-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M7 7h4v4H7z"></path><path d="M13 7h4v4h-4z"></path><path d="M7 13h4v4H7z"></path><path d="M13 13h4v4h-4z"></path></svg>
</span>
<span>管理</span>
</button>
</nav>
<div class="sidebar-bottom">
<div class="sync-card" id="sidebarSyncCard">
<strong>快速切换</strong>
<span>安装 Dock Agent 后可一键切换。</span>
<button id="sidebarLoginBtn" type="button">登录以同步</button>
</div>
<div class="mini-status">
<span id="sideCloudStatus">云端:未登录</span>
<span id="sideHelperStatus">Agent:探测中</span>
</div>
<div class="sidebar-utility">
<a class="utility-button helper-download" href="/downloads/CodexDockHelper.exe" download title="下载 Dock Agent" aria-label="下载 Dock Agent">
<span class="nav-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M12 5v10"></path><path d="M8 11l4 4 4-4"></path><path d="M6 19h12"></path></svg>
</span>
<span>下载 Agent</span>
</a>
<button class="utility-button" type="button" id="sidebarSettingsBtn" title="设置" aria-label="设置">
<span class="nav-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M5 7h14"></path><path d="M5 12h14"></path><path d="M5 17h14"></path><path d="M9 5v4"></path><path d="M15 10v4"></path><path d="M11 15v4"></path></svg>
</span>
<span>设置</span>
</button>
</div>
</div>
</aside>
<div class="main-shell">
<header class="topbar">
<div class="view-title">
<h1 id="viewTitle">账号池</h1>
<p id="viewSubtitle">管理账号、查看额度并快速切换。</p>
</div>
<div class="top-actions">
<span class="status-pill" id="syncPill">本地</span>
<span class="status-pill" id="autoSwitchPill">自动切换未开启</span>
<span class="status-pill" id="codexPill">Codex 状态确认中</span>
<span class="status-pill" id="helperPill">Agent 探测中</span>
<button id="userMenuBtn" type="button">登录以同步</button>
</div>
</header>
<nav class="mobile-nav" aria-label="移动端导航">
<button class="nav-item mobile-nav-item active" type="button" data-view="accounts">账号</button>
<button class="nav-item mobile-nav-item" type="button" data-view="helper">Agent</button>
<button class="nav-item mobile-nav-item" type="button" id="mobileSettingsBtn">设置</button>
<button class="nav-item mobile-nav-item admin-only" type="button" data-view="admin" hidden>管理</button>
</nav>
<main class="content">
<section class="view active" id="accountsView">
<div class="account-home">
<div class="home-title">
<h2 id="homeHeadline">账号池</h2>
<p id="homeSubcopy">登录后可在多台设备同步账号池。</p>
</div>
<div class="workbench-command">
<div class="command-shell" id="commandShell" role="search">
<div class="command-main-row">
<button class="icon-button" id="openImportBtn" type="button" aria-label="导入账号" title="导入账号">
<svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 5v14"></path><path d="M5 12h14"></path></svg>
</button>
<input id="accountSearchInput" class="command-input" type="search" autocomplete="off" placeholder="搜索账号,或把 JSON 文件拖到这里" />
</div>
<div class="command-attachments" id="commandAttachments" hidden></div>
</div>
<div class="primary-actions">
<button id="importLocalAuthBtn" type="button">同步本机</button>
<button id="refreshAllUsageBtn" type="button">刷新额度</button>
<button id="smartSettingsBtn" type="button">切换设置</button>
<button class="command-primary" id="quickSwitchBtn" type="button">智能切换</button>
</div>
</div>
<div class="account-toolbar">
<div>
<strong id="vaultTitle">本地账号池 · 0 个账号</strong>
<span id="vaultCopy">安装 Dock Agent 后可一键切换;未安装时可下载 auth.json。</span>
</div>
<div class="toolbar-controls">
<div class="segmented" aria-label="账号筛选">
<button class="active" type="button" data-filter="all">全部</button>
<button type="button" data-filter="paid">Plus</button>
<button type="button" data-filter="attention">需处理</button>
</div>
<select id="accountSortSelect" aria-label="排序">
<option value="updated">最近更新</option>
<option value="last-switch">最近切换</option>
<option value="quota">额度余量</option>
<option value="token-expiry">Token 到期</option>
<option value="created">导入时间</option>
<option value="name">账号名</option>
</select>
<div class="segmented" aria-label="账号布局">
<button class="active" type="button" data-layout="list">列表</button>
<button type="button" data-layout="cards">卡片</button>
</div>
</div>
</div>
<details class="filter-panel" id="filterPanel" open>
<summary>高级筛选</summary>
<label>套餐
<select id="filterPlan">
<option value="all">全部</option>
<option value="paid">付费</option>
<option value="plus">Plus</option>
<option value="free">Free</option>
<option value="pro-team">Pro / Team</option>
<option value="unknown">未知</option>
</select>
</label>
<label>Token
<select id="filterToken">
<option value="all">全部</option>
<option value="rt">RT</option>
<option value="at">AT</option>
<option value="soon">即将到期</option>
<option value="expired">已失效</option>
<option value="missing">缺失</option>
</select>
</label>
<label>额度
<select id="filterUsage">
<option value="all">全部</option>
<option value="low5h">5H 低余量</option>
<option value="low7d">7D 低余量</option>
<option value="unrefreshed">未刷新</option>
<option value="failed">刷新失败</option>
</select>
</label>
<label>状态
<select id="filterStatus">
<option value="all">全部</option>
<option value="current">当前使用</option>
<option value="direct">可一键切换</option>
<option value="download">可下载 auth</option>
<option value="attention">需处理</option>
</select>
</label>
</details>
<div class="bulk-bar" id="bulkBar">
<span id="bulkCount">未选择账号</span>
<button id="selectAllFilteredBtn" type="button">选择当前结果</button>
<button id="selectInvalidBtn" type="button">选择需处理</button>
<button id="bulkRefreshBtn" type="button">刷新额度</button>
<button id="bulkExportBtn" type="button">导出</button>
<select id="bulkPrioritySelect" aria-label="批量设置偏好">
<option value="">设置偏好</option>
<option value="primary">优先使用</option>
<option value="normal">正常使用</option>
<option value="reserve">尽量少用</option>
</select>
<button class="subtle-danger" id="bulkDeleteBtn" type="button">删除</button>
<small id="bulkCleanupHint" class="bulk-hint"></small>
</div>
<div class="metrics-grid" id="metricsGrid" hidden></div>
<div id="accountGrid" class="account-list chat-list"></div>
</div>
</section>
<section class="view" id="helperView" hidden>
<div class="plain-layout">
<section class="account-surface">
<div class="surface-head">
<div>
<h2>Dock Agent</h2>
<p>本机执行代理,负责写入 auth、观察任务边界并安全切换。</p>
</div>
<div class="surface-actions">
<a class="button-link primary-link" href="/downloads/CodexDockHelper.exe" download>下载 Dock Agent</a>
<button id="refreshHelperBtn" type="button">重新探测</button>
</div>
</div>
<div id="devicePanel" class="device-panel"></div>
</section>
<section class="account-surface">
<div class="surface-head">
<div>
<h2>设备密钥</h2>
<p>用于绑定当前浏览器和本机 Agent。</p>
</div>
</div>
<div class="pair-box">
<span>设备 Key</span>
<code id="deviceKeyBox">未生成</code>
<div class="action-row loose">
<button id="rotateDeviceKeyBtn" type="button">重新生成</button>
<button id="openLocalStatusBtn" type="button">打开状态页</button>
<button id="repairTrayBtn" type="button">修复托盘图标</button>
</div>
</div>
</section>
</div>
</section>
<section class="view" id="adminView" hidden>
<div class="plain-layout">
<section class="account-surface">
<div class="surface-head">
<div>
<h2>管理概览</h2>
<p>查看用户、账号、设备和最近操作。</p>
</div>
<button id="refreshAdminBtn" type="button">刷新管理数据</button>
</div>
<div class="admin-summary" id="adminSummary"></div>
</section>
<section class="account-surface">
<div class="surface-head">
<div>
<h2>用户</h2>
<p>启用、禁用、重置密码或踢下线。</p>
</div>
</div>
<div class="admin-message" id="adminMessage" hidden></div>
<div class="admin-filter-row">
<input id="adminUserSearch" type="search" placeholder="搜索用户邮箱" />
<select id="adminRoleFilter">
<option value="">全部角色</option>
<option value="admin">管理员</option>
<option value="user">用户</option>
</select>
<select id="adminStatusFilter">
<option value="">全部状态</option>
<option value="active">可用</option>
<option value="disabled">已停用</option>
</select>
<button id="adminSelectAllBtn" type="button">选择结果</button>
<button id="adminBulkDisableBtn" type="button">批量禁用</button>
<button id="adminBulkEnableBtn" type="button">批量启用</button>
<button id="adminBulkKickBtn" type="button">批量踢下线</button>
</div>
<div id="adminUsers" class="admin-table-wrap"></div>
</section>
<section class="account-surface">
<div class="surface-head">
<div>
<h2>设备与 Agent 版本</h2>
<p>识别离线设备与需要升级的本机代理。</p>
</div>
</div>
<div id="adminDevices" class="admin-table-wrap"></div>
</section>
<section class="account-surface">
<div class="surface-head">
<div>
<h2>审计记录</h2>
<p>查看导入、切换和管理操作。</p>
</div>
</div>
<div class="admin-filter-row">
<input id="adminAuditSearch" type="search" placeholder="搜索用户或账号" />
<select id="adminAuditActionFilter">
<option value="">全部动作</option>
<option value="import">导入</option>
<option value="switch">切换</option>
<option value="switch-payload">生成 auth</option>
<option value="admin-update-user">用户调整</option>
<option value="admin-reset-password">重置密码</option>
<option value="admin-delete-sessions">踢下线</option>
</select>
</div>
<div id="adminAudit" class="audit-list wide"></div>
</section>
</div>
</section>
</main>
</div>
</div>
<div class="modal" id="authModal" aria-hidden="true">
<div class="modal-backdrop" data-close-modal="authModal"></div>
<section class="auth-card" role="dialog" aria-modal="true" aria-labelledby="authTitle">
<button class="modal-close" type="button" data-close-modal="authModal" aria-label="关闭">×</button>
<h2 id="authTitle">登录或注册</h2>
<p id="authCopy">登录后可把本地账号池同步到云端,并在其他设备继续使用。</p>
<form id="authForm" class="auth-form">
<label>
<span>电子邮件地址</span>
<input id="authEmail" autocomplete="email" type="email" required />
</label>
<label>
<span>密码</span>
<input id="authPassword" autocomplete="current-password" type="password" minlength="8" required />
</label>
<button class="primary full" id="authSubmitBtn" type="submit">继续</button>
</form>
<button class="text-link" id="toggleAuthModeBtn" type="button">没有账号?创建一个</button>
</section>
</div>
<div class="modal" id="syncModal" aria-hidden="true">
<div class="modal-backdrop"></div>
<section class="sync-dialog" role="dialog" aria-modal="true" aria-labelledby="syncTitle">
<h2 id="syncTitle">同步确认</h2>
<p>检测到本地账号池和云端账号池。选择这台设备接下来怎么处理。</p>
<div class="sync-stats" id="syncStats"></div>
<div class="sync-actions">
<button class="primary" id="mergeSyncBtn" type="button">合并并同步</button>
<button id="localOnlyBtn" type="button">只使用本地</button>
<button class="danger" id="cloudOverwriteBtn" type="button">拉取云端覆盖本地</button>
</div>
</section>
</div>
<div class="modal" id="cleanupModal" aria-hidden="true">
<div class="modal-backdrop" data-close-modal="cleanupModal"></div>
<section class="cleanup-dialog" role="dialog" aria-modal="true" aria-labelledby="cleanupTitle">
<button class="modal-close" type="button" data-close-modal="cleanupModal" aria-label="关闭">×</button>
<span class="eyebrow">批量清理</span>
<h2 id="cleanupTitle">清理所选账号</h2>
<p id="cleanupSummary">删除前会先展示风险摘要。</p>
<div class="cleanup-stats" id="cleanupStats"></div>
<div id="cleanupRisk"></div>
<div class="cleanup-list" id="cleanupList"></div>
<div class="cleanup-actions">
<button type="button" data-close-modal="cleanupModal">取消</button>
<button class="danger" id="cleanupConfirmBtn" type="button">确认清理</button>
</div>
</section>
</div>
<div class="modal" id="accountDetailModal" aria-hidden="true">
<div class="modal-backdrop" data-close-modal="accountDetailModal"></div>
<section class="account-detail-card" role="dialog" aria-modal="true" aria-labelledby="detailTitle">
<div class="detail-top">
<div>
<span class="eyebrow">账号详情</span>
<h2 id="detailTitle">当前选择</h2>
<p id="selectedState">未选择账号</p>
</div>
<button class="modal-close" type="button" data-close-modal="accountDetailModal" aria-label="关闭">×</button>
</div>
<div class="detail-scroll">
<div id="selectedAccountPanel" class="selected-panel"></div>
<section class="detail-section">
<button class="section-toggle" type="button" data-toggle="auditBody">运行记录</button>
<div class="section-body" id="auditBody">
<div class="audit-list" id="auditList"></div>
<button class="ghost" id="clearAuditBtn" type="button">刷新</button>
</div>
</section>
<section class="detail-section">
<button class="section-toggle" type="button" data-toggle="authPreviewBody">高级信息</button>
<div class="section-body collapsed" id="authPreviewBody">
<div class="warning" id="tokenWarning" hidden></div>
<pre class="output" id="authPreview">选择账号后显示摘要。</pre>
</div>
</section>
</div>
<div class="detail-action-row">
<button class="primary" id="switchBtn" type="button">立即切换</button>
<button id="copyAuthBtn" type="button">复制 auth</button>
</div>
</section>
</div>
<div class="modal" id="manualSwitchRiskModal" aria-hidden="true">
<div class="modal-backdrop" data-close-modal="manualSwitchRiskModal"></div>
<section class="switch-risk-dialog" role="dialog" aria-modal="true" aria-labelledby="manualSwitchRiskTitle">
<button class="modal-close" type="button" data-close-modal="manualSwitchRiskModal" aria-label="关闭">×</button>
<span class="eyebrow">任务保护</span>
<h2 id="manualSwitchRiskTitle">当前任务还不能安全切换</h2>
<p id="manualSwitchRiskSummary">当前任务仍在运行,立即切换可能中断本轮并浪费剩余额度。</p>
<div class="cleanup-stats switch-risk-stats" id="manualSwitchRiskStats"></div>
<div id="manualSwitchRiskDetail"></div>
<div class="switch-risk-actions">
<button id="manualSwitchWaitBtn" type="button">等待安全边界后切换</button>
<button class="danger" id="manualSwitchForceBtn" type="button">仍然立即切换</button>
</div>
</section>
</div>
<div class="modal" id="settingsModal" aria-hidden="true">
<div class="modal-backdrop" data-close-modal="settingsModal"></div>
<section class="settings-card" role="dialog" aria-modal="true" aria-labelledby="settingsTitle">
<div class="settings-head">
<h2 id="settingsTitle">设置</h2>
<button class="modal-close" type="button" data-close-modal="settingsModal" aria-label="关闭">×</button>
</div>
<div class="settings-layout">
<nav class="settings-tabs">
<button class="active" type="button" data-settings-tab="account">账号</button>
<button type="button" data-settings-tab="data">数据</button>
<button type="button" data-settings-tab="smart">智能切换</button>
<button type="button" data-settings-tab="helper">Dock Agent</button>
<button type="button" data-settings-tab="appearance">外观</button>
<button class="admin-only" type="button" data-settings-tab="admin" hidden>管理员</button>
</nav>
<div class="settings-panels">
<section class="settings-panel active" data-panel="account">
<h3>账号</h3>
<div id="settingsAccountState" class="setting-box"></div>
<form id="changePasswordForm" class="stack-form">
<input class="visually-hidden-field" type="text" autocomplete="username" aria-hidden="true" tabindex="-1" />
<label>
<span>当前密码</span>
<input id="currentPassword" type="password" autocomplete="current-password" />
</label>
<label>
<span>新密码</span>
<input id="nextPassword" type="password" autocomplete="new-password" minlength="8" />
</label>
<button type="submit">修改密码</button>
</form>
<section class="account-danger-zone" id="deleteCloudAccountPanel" aria-labelledby="deleteCloudAccountTitle">
<h4 id="deleteCloudAccountTitle">删除云端账号</h4>
<p>永久删除云端账号、账号池、额度记录、设备授权和审计记录。本机浏览器中的离线账号不受影响。</p>
<form id="deleteCloudAccountForm" class="stack-form">
<label>
<span>确认邮箱</span>
<input id="deleteAccountEmail" type="email" autocomplete="email" placeholder="输入当前登录邮箱" />
</label>
<label>
<span>当前密码</span>
<input id="deleteAccountPassword" type="password" autocomplete="current-password" />
</label>
<button class="danger" type="submit">永久删除云端账号</button>
</form>
</section>
</section>
<section class="settings-panel" data-panel="data">
<h3>数据</h3>
<div id="backupCloudState" class="setting-box"></div>
<div id="usageRefreshState" class="setting-list"></div>
<div class="setting-actions">
<button id="settingsImportBtn" type="button">导入 JSON</button>
<button id="pullCloudLocalBtn" type="button">拉取云端到本机</button>
<button id="exportLocalBtn" type="button">导出本地</button>
<button id="migrateLegacyCacheBtn" type="button">从旧本地缓存迁移</button>
<button class="danger" id="clearLocalBtn" type="button">清空本地缓存</button>
</div>
</section>
<section class="settings-panel" data-panel="smart">
<h3>智能切换</h3>
<div id="smartSettingsState" class="setting-list"></div>
</section>
<section class="settings-panel" data-panel="helper">
<h3>Dock Agent</h3>
<div id="settingsHelperState" class="setting-box"></div>
<div class="setting-actions">
<button id="settingsRefreshHelperBtn" type="button">重新探测</button>
<button id="settingsOpenLocalStatusBtn" type="button">打开状态页</button>
<button id="settingsRepairTrayBtn" type="button">修复托盘图标</button>
<a class="button-link" href="/downloads/CodexDockHelper.exe" download>下载 Agent</a>
</div>
</section>
<section class="settings-panel" data-panel="appearance">
<h3>外观</h3>
<div class="setting-box">
<strong>主题</strong>
<span>当前跟随浅色系统界面。深色主题先预留,不影响核心切换链路。</span>
</div>
</section>
<section class="settings-panel" data-panel="admin">
<h3>管理员</h3>
<div class="setting-box">
<strong>用户管理</strong>
<span>管理用户、角色、状态和在线会话。</span>
<button id="goAdminBtn" type="button">进入管理页</button>
</div>
</section>
</div>
</div>
</section>
</div>
<div class="import-drawer" id="importDrawer" aria-hidden="true">
<div class="drawer-backdrop" id="drawerBackdrop"></div>
<aside class="drawer-panel" role="dialog" aria-modal="true" aria-labelledby="drawerTitle">
<div class="drawer-head">
<div>
<h2 id="drawerTitle">导入账号</h2>
<p>先解析预览,确认无误后再导入。</p>
</div>
<button class="ghost" id="closeImportBtn" type="button">关闭</button>
</div>
<div id="accountForm" class="import-form">
<div class="import-source-grid primary-import-grid" role="tablist" aria-label="推荐导入方式">
<button class="import-source-card primary-source active" type="button" data-import-mode="oauth">
<span class="source-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M6.5 8.5A6.5 6.5 0 0 1 18 6.8"></path><path d="M18 4v3h-3"></path><path d="M17.5 15.5A6.5 6.5 0 0 1 6 17.2"></path><path d="M6 20v-3h3"></path></svg>
</span>
<strong>登录导入 RT 账号</strong>
<small>推荐路径。Agent 在线时自动接收回调、换取 RT 并导入,可直接用于 Codex 切换。</small>
<span class="method-tag">推荐</span>
</button>
</div>
<details class="advanced-import-panel">
<summary>
<strong>高级导入方式</strong>
<span>仅用于登记 AT、导入当前设备或批量文件。</span>
</summary>
<div class="import-source-grid advanced-source-grid" role="tablist" aria-label="高级导入方式">
<button class="import-source-card" type="button" data-import-mode="session">
<span class="source-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M8 7h8"></path><path d="M7 12h10"></path><path d="M8 17h8"></path><path d="M5 5v14"></path><path d="M19 5v14"></path></svg>
</span>
<strong>Session JSON(仅登记)</strong>
<small>多数只含 AT。导入后可识别账号,但不能用于 Codex 切换。</small>
<span class="method-tag warn">仅登记</span>
</button>
<button class="import-source-card" type="button" data-import-mode="device">
<span class="source-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M6 7h12v9H6z"></path><path d="M10 19h4"></path><path d="M12 16v3"></path><path d="M12 13V8"></path><path d="M9.5 10.5 12 8l2.5 2.5"></path></svg>
</span>
<strong>当前设备账号</strong>
<small>只导入这台电脑当前 Codex 已登录账号,不用于修复其它账号。</small>
</button>
<button class="import-source-card" type="button" data-import-mode="file">
<span class="source-icon" aria-hidden="true">
<svg viewBox="0 0 24 24"><path d="M7 4h7l3 3v13H7z"></path><path d="M14 4v4h4"></path><path d="M12 10v6"></path><path d="M9.5 13.5 12 16l2.5-2.5"></path></svg>
</span>
<strong>批量上传文件</strong>
<small>高级批量导入 auth / CPA / Sub JSON。</small>
</button>
</div>
</details>
<section class="import-mode-panel active" data-import-panel="oauth">
<div class="auth-method-panel oauth-panel">
<div>
<strong>登录导入 RT 账号</strong>
<span>完成网页登录后,Agent 会自动接收回调、换取 RT 并导入账号;手动链接仅作备用。</span>
</div>
<div class="oauth-primary-action">
<button class="primary" id="openOauthAuthBtn" type="button">打开授权页面</button>
<span>授权完成后,本页会自动进入导入流程。</span>
</div>
<details class="oauth-fallback-panel">
<summary>手动回调备用</summary>
<div class="oauth-fallback-body">
<label>
<span>授权链接</span>
<input id="oauthAuthUrl" readonly />
</label>
<label>
<span>回调链接(备用)</span>
<textarea id="oauthCallbackInput" spellcheck="false" placeholder="自动接收失败时,可粘贴完整回调地址,或只粘贴 code=...&state=..."></textarea>
</label>
<div class="auth-acquire-actions">
<button id="copyOauthUrlBtn" type="button">复制授权链接</button>
<button id="parseOauthCallbackBtn" type="button">解析回调</button>
</div>
</div>
</details>
</div>
</section>
<section class="import-mode-panel" data-import-panel="session">
<div class="auth-method-panel">
<div>
<strong>Session JSON(仅登记)</strong>
<span>ChatGPT Session 页面目前通常只能拿到 AT。导入后可登记账号、识别套餐和邮箱,但缺少 RT,当前不能用于 Codex 切换;要可用请重新登录 Codex 获取 RT。</span>
</div>
<div class="auth-acquire-actions">
<button type="button" data-auth-action="open-chatgpt-login">打开 ChatGPT 登录</button>
<button type="button" data-auth-action="open-session-json">打开 Session JSON</button>
<button type="button" data-auth-action="copy-session-url">复制 Session 地址</button>
</div>
</div>
<label>
<span>Session / auth JSON</span>
<textarea id="sessionInput" spellcheck="false" placeholder="粘贴 auth.json、/api/auth/session、CPA JSON、Sub JSON,或包含 accounts/sessions/items/data 的数组结构。Session JSON 多数只含 AT,导入后需重新登录 Codex 获取 RT 才能用于 Codex。"></textarea>
</label>
<button id="previewImportBtn" type="button">解析粘贴内容</button>
</section>
<section class="import-mode-panel" data-import-panel="device">
<div class="auth-method-panel">
<div>
<strong>导入当前设备登录</strong>
<span>只用于导入当前 Codex 正在登录的账号。不要用当前本机 auth 去更新另一个 AT-only 账号;那会变成错误账号。</span>
</div>
<div class="auth-acquire-actions">
<button type="button" data-auth-action="sync-local-auth">导入当前 auth</button>
<button type="button" data-auth-action="copy-codex-login">复制登录命令</button>
</div>
</div>
</section>
<section class="import-mode-panel" data-import-panel="file">
<div class="file-import-row">
<button id="pickJsonBtn" type="button">选择 JSON 文件</button>
<input id="jsonFileInput" type="file" accept=".json,application/json" multiple hidden />
<span>可一次选择多个文件。登录后导入的账号自动保存到云端。</span>
</div>
</section>
<div class="import-result" id="importResult" hidden></div>
<div class="import-preview-head">
<strong id="importPreviewSummary">还没有待导入账号</strong>
<span>解析后会自动合并重复项,并标出新增、更新和失败。</span>
</div>
<div class="import-preview-list" id="importPreviewList"></div>
<div class="action-row loose">
<button class="primary" id="confirmImportBtn" type="button" disabled>确认导入</button>
<button id="finishImportBtn" type="button">完成</button>
<button id="clearFormBtn" type="button">继续导入</button>
</div>
</div>
<div class="oauth-flow-overlay" id="oauthFlowOverlay" hidden>
<div class="oauth-flow-card" role="status" aria-live="polite">
<div class="oauth-flow-topline">
<span class="oauth-flow-icon" id="oauthFlowIcon">↻</span>
<div>
<strong id="oauthFlowTitle">等待授权</strong>
<span id="oauthFlowDetail">请在新打开的浏览器页面完成登录授权。</span>
</div>
</div>
<div class="oauth-flow-meter"><i id="oauthFlowMeter"></i></div>
<div class="oauth-flow-meta">
<span id="oauthFlowCountdown">剩余 03:00</span>
<span id="oauthFlowHint">完成后本页会自动接收并导入账号。</span>
</div>
<div class="auth-acquire-actions oauth-flow-actions">
<button id="oauthFlowCancelBtn" type="button">取消</button>
<button id="oauthFlowRetryBtn" type="button">重新打开授权页面</button>
<button class="primary" id="oauthFlowDoneBtn" type="button">完成</button>
</div>
</div>
</div>
</aside>
</div>
<div class="modal" id="progressModal" aria-hidden="true">
<div class="modal-backdrop"></div>
<section class="progress-card" role="dialog" aria-modal="true" aria-labelledby="progressTitle">
<h2 id="progressTitle">正在处理</h2>
<p id="progressSummary">请稍候。</p>
<div class="progress-meter"><i id="progressMeterBar" style="width:0%"></i></div>
<div class="progress-list" id="progressList"></div>
<div class="action-row loose">
<button id="progressCloseBtn" type="button" disabled>完成</button>
</div>
</section>
</div>
<div class="toast" id="toast" hidden></div>
<script src="account-core.js?v=20260525-oauth-primary2"></script>
<script src="platform-clients.js?v=20260525-oauth-primary2"></script>
<script src="format-core.js?v=20260525-oauth-primary2"></script>
<script src="progress-ui.js?v=20260525-oauth-primary2"></script>
<script src="shell-ui.js?v=20260525-oauth-primary2"></script>
<script src="dialog-ui.js?v=20260525-oauth-primary2"></script>
<script src="settings-ui.js?v=20260525-oauth-primary2"></script>
<script src="account-list-ui.js?v=20260525-oauth-primary2"></script>
<script src="account-detail-ui.js?v=20260525-oauth-primary2"></script>
<script src="audit-core.js?v=20260525-oauth-primary2"></script>
<script src="admin-ui.js?v=20260525-oauth-primary2"></script>
<script src="panels-ui.js?v=20260525-oauth-primary2"></script>
<script src="import-core.js?v=20260525-oauth-primary2"></script>
<script src="import-ui.js?v=20260525-oauth-primary2"></script>
<script src="oauth-core.js?v=20260525-oauth-primary2"></script>
<script src="app.js?v=20260525-oauth-primary2"></script>
</body>
</html>