llm-switcher 1.6.7 → 1.6.9

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/ui.html CHANGED
@@ -866,6 +866,200 @@
866
866
  .row { padding: 0.75rem 1rem; flex-wrap: wrap; }
867
867
  .pgrid { grid-template-columns: 1fr; }
868
868
  }
869
+
870
+ /* Documentation Tab Styles */
871
+ .docs-container {
872
+ display: grid;
873
+ grid-template-columns: 240px minmax(0, 1fr);
874
+ gap: 1.5rem;
875
+ align-items: start;
876
+ }
877
+ @media (max-width: 980px) {
878
+ .docs-container { grid-template-columns: 1fr; }
879
+ .docs-toc { position: static !important; max-height: none !important; }
880
+ }
881
+ .docs-toc {
882
+ position: sticky;
883
+ top: 5.5rem;
884
+ background: var(--card);
885
+ border: 1px solid var(--line);
886
+ border-radius: 12px;
887
+ padding: 0.85rem;
888
+ max-height: calc(100vh - 7rem);
889
+ overflow-y: auto;
890
+ display: flex;
891
+ flex-direction: column;
892
+ gap: 0.25rem;
893
+ box-shadow: var(--shadow);
894
+ }
895
+ .docs-toc-title {
896
+ font-size: 0.74rem;
897
+ font-weight: 700;
898
+ color: var(--muted);
899
+ text-transform: uppercase;
900
+ letter-spacing: 0.05em;
901
+ padding: 0.25rem 0.5rem 0.5rem;
902
+ border-bottom: 1px solid var(--line);
903
+ margin-bottom: 0.35rem;
904
+ display: flex;
905
+ align-items: center;
906
+ gap: 0.4rem;
907
+ }
908
+ .docs-toc-link {
909
+ display: flex;
910
+ align-items: center;
911
+ gap: 0.5rem;
912
+ padding: 0.4rem 0.6rem;
913
+ border-radius: 6px;
914
+ color: var(--muted);
915
+ text-decoration: none;
916
+ font-size: 0.82rem;
917
+ font-weight: 500;
918
+ transition: background 0.15s, color 0.15s;
919
+ white-space: nowrap;
920
+ overflow: hidden;
921
+ text-overflow: ellipsis;
922
+ }
923
+ .docs-toc-link:hover {
924
+ color: var(--fg);
925
+ background: var(--card2);
926
+ }
927
+ .docs-toc-link.active {
928
+ color: var(--accent);
929
+ background: var(--accent-soft);
930
+ font-weight: 600;
931
+ }
932
+ .docs-content {
933
+ min-width: 0;
934
+ display: flex;
935
+ flex-direction: column;
936
+ gap: 1.25rem;
937
+ }
938
+ .doc-section {
939
+ background: var(--card);
940
+ border: 1px solid var(--line);
941
+ border-radius: 12px;
942
+ padding: 1.35rem 1.5rem;
943
+ scroll-margin-top: 5.5rem;
944
+ }
945
+ .doc-section-h {
946
+ display: flex;
947
+ align-items: center;
948
+ justify-content: space-between;
949
+ gap: 0.75rem;
950
+ padding-bottom: 0.85rem;
951
+ margin-bottom: 1.15rem;
952
+ border-bottom: 1px solid var(--line);
953
+ flex-wrap: wrap;
954
+ }
955
+ .doc-section-h h2 {
956
+ font-size: 1.15rem;
957
+ font-weight: 700;
958
+ color: var(--fg);
959
+ display: flex;
960
+ align-items: center;
961
+ gap: 0.55rem;
962
+ margin: 0;
963
+ }
964
+ .doc-section-h h2 svg {
965
+ width: 20px;
966
+ height: 20px;
967
+ color: var(--accent);
968
+ flex: none;
969
+ }
970
+ .doc-prose {
971
+ font-size: 0.88rem;
972
+ line-height: 1.65;
973
+ color: var(--fg);
974
+ }
975
+ .doc-prose p { margin-bottom: 0.85rem; }
976
+ .doc-prose p:last-child { margin-bottom: 0; }
977
+ .doc-prose h3 {
978
+ font-size: 0.96rem;
979
+ font-weight: 650;
980
+ color: var(--fg);
981
+ margin: 1.25rem 0 0.5rem;
982
+ display: flex;
983
+ align-items: center;
984
+ gap: 0.45rem;
985
+ }
986
+ .doc-prose ul, .doc-prose ol {
987
+ margin: 0.4rem 0 0.85rem 1.4rem;
988
+ }
989
+ .doc-prose li {
990
+ margin-bottom: 0.35rem;
991
+ }
992
+ .doc-codebox {
993
+ background: var(--bg);
994
+ border: 1px solid var(--line);
995
+ border-radius: 8px;
996
+ margin: 0.75rem 0 1rem;
997
+ overflow: hidden;
998
+ }
999
+ .doc-codebox-bar {
1000
+ display: flex;
1001
+ align-items: center;
1002
+ justify-content: space-between;
1003
+ padding: 0.35rem 0.75rem;
1004
+ background: var(--card2);
1005
+ border-bottom: 1px solid var(--line);
1006
+ font-size: 0.75rem;
1007
+ color: var(--muted);
1008
+ font-family: var(--font-mono);
1009
+ }
1010
+ .doc-codebox pre {
1011
+ margin: 0;
1012
+ padding: 0.85rem 1rem;
1013
+ overflow-x: auto;
1014
+ font-family: var(--font-mono);
1015
+ font-size: 0.82rem;
1016
+ line-height: 1.5;
1017
+ color: var(--fg);
1018
+ }
1019
+ .doc-callout {
1020
+ border-left: 3px solid;
1021
+ border-radius: 0 8px 8px 0;
1022
+ padding: 0.75rem 1rem;
1023
+ margin: 0.85rem 0;
1024
+ font-size: 0.84rem;
1025
+ line-height: 1.55;
1026
+ background: var(--card2);
1027
+ }
1028
+ .doc-callout.info {
1029
+ border-color: var(--accent);
1030
+ background: var(--accent-soft);
1031
+ }
1032
+ .doc-callout.warning {
1033
+ border-color: var(--warn);
1034
+ background: var(--warn-soft);
1035
+ }
1036
+ .doc-callout.success {
1037
+ border-color: var(--ok);
1038
+ background: var(--ok-soft);
1039
+ }
1040
+ .doc-callout.danger {
1041
+ border-color: var(--err);
1042
+ background: var(--err-soft);
1043
+ }
1044
+ .doc-table {
1045
+ width: 100%;
1046
+ border-collapse: collapse;
1047
+ margin: 0.85rem 0 1.15rem;
1048
+ font-size: 0.82rem;
1049
+ }
1050
+ .doc-table th, .doc-table td {
1051
+ border: 1px solid var(--line);
1052
+ padding: 0.55rem 0.75rem;
1053
+ text-align: left;
1054
+ }
1055
+ .doc-table th {
1056
+ background: var(--card2);
1057
+ font-weight: 600;
1058
+ color: var(--muted);
1059
+ }
1060
+ .doc-table tr:hover td {
1061
+ background: rgba(255, 255, 255, 0.02);
1062
+ }
869
1063
  </style>
870
1064
  </head>
871
1065
  <body>
@@ -906,26 +1100,30 @@
906
1100
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 12h16M12 4v16"/><circle cx="12" cy="12" r="8"/></svg>
907
1101
  Routes & Profiles<span class="ct" id="ct-routes"></span>
908
1102
  </a>
909
- <a class="nav-link" href="#/compact" data-nav="compact">
910
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 14h6m0 0v6m0-6L3 21"/><path d="M20 10h-6m0 0V4m0 6l7-7"/><circle cx="12" cy="12" r="2"/></svg>
911
- Idle Compaction<span class="ct" id="ct-compact"></span>
912
- </a>
913
- <a class="nav-link" href="#/goal-check" data-nav="goal-check">
914
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/></svg>
915
- Goal Check<span class="ct" id="ct-goal-check"></span>
1103
+ <a class="nav-link" href="#/logs" data-nav="logs">
1104
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="m9 15 2 2 4-4"/></svg>
1105
+ Request Inspector<span class="ct" id="ct-logs"></span>
916
1106
  </a>
917
1107
  <a class="nav-link" href="#/models" data-nav="models">
918
1108
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><rect x="3" y="4" width="18" height="6" rx="1.5"/><rect x="3" y="14" width="18" height="6" rx="1.5"/></svg>
919
1109
  Model Catalog<span class="ct" id="ct-models"></span>
920
1110
  </a>
921
- <a class="nav-link" href="#/logs" data-nav="logs">
922
- <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><path d="M14 2v6h6"/><path d="m9 15 2 2 4-4"/></svg>
923
- Request Inspector<span class="ct" id="ct-logs"></span>
924
- </a>
925
1111
  <a class="nav-link" href="#/doctor" data-nav="doctor">
926
1112
  <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><circle cx="12" cy="12" r="9"/><path d="m9 12 2 2 4-4"/></svg>
927
1113
  System Doctor
928
1114
  </a>
1115
+ <a class="nav-link" href="#/goal-check" data-nav="goal-check">
1116
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/><path d="m9 12 2 2 4-4"/></svg>
1117
+ Goal Check<span class="ct" id="ct-goal-check"></span>
1118
+ </a>
1119
+ <a class="nav-link" href="#/compact" data-nav="compact">
1120
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 14h6m0 0v6m0-6L3 21"/><path d="M20 10h-6m0 0V4m0 6l7-7"/><circle cx="12" cy="12" r="2"/></svg>
1121
+ Idle Compaction<span class="ct" id="ct-compact"></span>
1122
+ </a>
1123
+ <a class="nav-link" href="#/docs" data-nav="docs">
1124
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.8"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1-2.5-2.5Z"/><path d="M6 6h10M6 10h10M6 14h7"/></svg>
1125
+ Docs & Guide
1126
+ </a>
929
1127
  </nav>
930
1128
 
931
1129
  <div class="spacer"></div>
@@ -1536,6 +1734,1317 @@
1536
1734
  </div>
1537
1735
  </div>
1538
1736
  </div>
1737
+
1738
+ <!-- VIEW 7: Documentation & Guide -->
1739
+ <div id="view-docs" class="view-panel" style="display:none;">
1740
+ <div class="docs-container">
1741
+ <!-- Sticky Quick Index TOC -->
1742
+ <nav class="docs-toc" id="docsToc" aria-label="Documentation Index">
1743
+ <div class="docs-toc-title">
1744
+ <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M4 19.5v-15A2.5 2.5 0 0 1 6.5 2H20v20H6.5a2.5 2.5 0 0 1-2.5-2.5Z"/><path d="M6 6h10M6 10h10"/></svg>
1745
+ <span>Mục Lục Hướng Dẫn</span>
1746
+ </div>
1747
+ <a class="docs-toc-link active" href="#doc-overview">1. Tổng Quan & Kiến Trúc</a>
1748
+ <a class="docs-toc-link" href="#doc-install">2. Cài Đặt LLM Switcher</a>
1749
+ <a class="docs-toc-link" href="#doc-intact">3. intact Gateway & Pooler</a>
1750
+ <a class="docs-toc-link" href="#doc-cli">4. Lệnh CLI & Vận Hành</a>
1751
+ <a class="docs-toc-link" href="#doc-blindfold">5. Blindfold Mode & CA</a>
1752
+ <a class="docs-toc-link" href="#doc-agy">6. Antigravity CLI (agy)</a>
1753
+ <a class="docs-toc-link" href="#doc-optimizers">7. Tối Ưu Token & Healer</a>
1754
+ <a class="docs-toc-link" href="#doc-agent-mcp">8. Agent Skill & MCP Server</a>
1755
+ <a class="docs-toc-link" href="#doc-advanced">9. Compaction & Goal Check</a>
1756
+ <a class="docs-toc-link" href="#doc-config">10. Cấu Hình config.json</a>
1757
+ <a class="docs-toc-link" href="#doc-faq">11. Xử Lý Sự Cố (FAQ)</a>
1758
+ </nav>
1759
+
1760
+ <!-- Main Documentation Content -->
1761
+ <div class="docs-content">
1762
+ <!-- 1. OVERVIEW & ARCHITECTURE -->
1763
+ <section class="doc-section" id="doc-overview">
1764
+ <div class="doc-section-h">
1765
+ <h2>
1766
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M12 16v-4M12 8h.01"/></svg>
1767
+ 1. Tổng Quan & Kiến Trúc Cốt Lõi
1768
+ </h2>
1769
+ <span class="badge" style="background:var(--accent-soft); color:var(--accent); font-weight:600;">v1.6.8 · Zero-Dependency · Multi-Protocol Edge Gateway</span>
1770
+ </div>
1771
+ <div class="doc-prose">
1772
+ <p>
1773
+ <strong>LLM Switcher</strong> là cổng ngõ biên (Edge Gateway) và bộ điều phối chuyển đổi đa giao thức LLM siêu nhẹ, hoàn toàn <strong>Zero-Dependency</strong> (xây dựng 100% bằng thư viện chuẩn của Node.js: <code>http</code>, <code>fs</code>, <code>os</code>, <code>path</code>, <code>crypto</code>, <code>fetch</code>; không cần <code>npm install</code>, thời gian khởi động lạnh &lt;50ms). Hệ thống đóng vai trò cầu nối thông minh và trong suốt giữa các AI Coding CLI hàng đầu (<strong>Claude Code</strong>, <strong>OpenAI Codex</strong>, <strong>Antigravity CLI <code>agy</code></strong>) với mọi nhà cung cấp mô hình LLM (OpenAI-compatible, Anthropic Native, Vertex Native, Google Code Assist).
1774
+ </p>
1775
+
1776
+ <div class="doc-callout info">
1777
+ <strong>Tại sao bạn cần LLM Switcher?</strong> Các proxy trung gian thông thường thường làm biến dạng giao thức phản hồi: làm mất khối suy luận <code>thinking_delta</code>, cắt vụn arguments của tool calls, làm lệch cache hoặc khiến Claude Code / Codex hiện banner cảnh báo "custom API endpoint". LLM Switcher chạy ngay trên máy cục bộ (<code>127.0.0.1:3456</code> và <code>:3457</code>), thực hiện <strong>100% Native Emulation</strong> và tự chữa lành (Self-Healing) payload mà tuyệt đối không sửa file cấu hình gốc của công cụ.
1778
+ </div>
1779
+
1780
+ <h3>Kiến Trúc Luồng Dữ Liệu</h3>
1781
+ <div class="doc-codebox">
1782
+ <div class="doc-codebox-bar"><span>Topology · Luồng Xử Lý Yêu Cầu</span></div>
1783
+ <pre>[Claude Code / Codex / agy CLI]
1784
+ │ (1. Khởi chạy qua Launcher Shim: nạp HTTPS_PROXY & CA in-memory)
1785
+ ▼
1786
+ [Blindfold Interceptor :3457] ──(TLS giải mã nội bộ cho anthropic/openai/chatgpt)
1787
+ │ (2. Bóc tách credential client, chuyển tiếp /v1/messages, /v1/responses)
1788
+ ▼
1789
+ [LLM Switcher Edge Gateway :3456]
1790
+ │ ├── Healer Engine (Tự sửa schema rỗng {}, cứu tool_result mồ côi, gộp lượt)
1791
+ │ ├── Protocol IR Engine (Chuyển đổi 2 chiều Anthropic &lt;=&gt; OpenAI &lt;=&gt; Vertex)
1792
+ │ └── Bifrost Engine (Chuyển tiếp nguyên vẹn nếu gọi đúng model của hãng qua intact)
1793
+ ▼
1794
+ [Upstream Provider: intact (Khuyên dùng) / 9Router / Vertex / DeepSeek / Direct]</pre>
1795
+ </div>
1796
+
1797
+ <h3>4 Trụ Cột Đột Phá Của Hệ Thống</h3>
1798
+ <ul>
1799
+ <li><strong>100% Native Emulation:</strong> Chuyển đổi mọi phản hồi từ upstream thành luồng Anthropic SSE chuẩn (<code>thinking_delta</code> + <code>tool_use</code>) cho Claude Code, và Responses API events chuẩn cho Codex. CLI nhận được response native và hoàn toàn tin cậy server.</li>
1800
+ <li><strong>Zero Config Mutation:</strong> Tuyệt đối không đọc/ghi <code>~/.claude/settings.json</code> hay <code>~/.codex/config.toml</code>, và không ghi biến môi trường nào mà công cụ đọc như cấu hình. Không bao giờ xuất hiện banner cảnh báo "Custom API endpoint configured".</li>
1801
+ <li><strong>Cửa Sổ Context Theo Model Thực Tế:</strong> Tự động phát hiện cửa sổ ngữ cảnh chính thức từ upstream (ví dụ 1M tokens), truyền đúng <code>max_context_window</code> và <code>context_window</code> trong <code>/v1/models</code> để Codex tự tin lập kế hoạch và nén lịch sử chính xác. Claude Code tự ước lượng phiên theo model nó gọi.</li>
1802
+ <li><strong>Bifrost Passthrough:</strong> Khi bạn dùng tài khoản của chính nhà cung cấp đó trên pool của <a href="https://github.com/louisphamdev/intact" target="_blank" style="color:var(--accent);">intact</a>, request sẽ đi xuyên qua nguyên vẹn 100% byte-for-byte, bỏ qua bộ dịch giao thức và Healer, chỉ thay đổi API key an toàn.</li>
1803
+ </ul>
1804
+
1805
+ <h3>Thư Viện Sơ Đồ Tương Tác Archify</h3>
1806
+ <table class="doc-table">
1807
+ <thead>
1808
+ <tr>
1809
+ <th style="width:30%;">Sơ đồ</th>
1810
+ <th>Mô tả luồng</th>
1811
+ <th style="width:25%;">Liên kết xem tương tác</th>
1812
+ </tr>
1813
+ </thead>
1814
+ <tbody>
1815
+ <tr>
1816
+ <td><strong>System Topology</strong></td>
1817
+ <td>Kiến trúc tổng thể: Dev Clients ➔ Optimizers ➔ Gateway & Healer Core ➔ Upstream Providers.</td>
1818
+ <td><a href="docs/diagrams/system-architecture.html" target="_blank" style="color:var(--accent);">📊 Mở Interactive Viewer</a></td>
1819
+ </tr>
1820
+ <tr>
1821
+ <td><strong>IR Healer Pipeline</strong></td>
1822
+ <td>Chuẩn hóa request, sửa schema lỗi, tổng hợp stream SSE và cơ chế abort propagation.</td>
1823
+ <td><a href="docs/diagrams/ir-translation-pipeline.html" target="_blank" style="color:var(--accent);">🔄 Mở Interactive Viewer</a></td>
1824
+ </tr>
1825
+ <tr>
1826
+ <td><strong>Codex Blindfold Routing</strong></td>
1827
+ <td>Luồng TLS CONNECT proxy, bóc tách credential và định tuyến an toàn theo host.</td>
1828
+ <td><a href="docs/diagrams/blindfold-request-routing.html" target="_blank" style="color:var(--accent);">🛡️ Mở Interactive Viewer</a></td>
1829
+ </tr>
1830
+ <tr>
1831
+ <td><strong>Switch Lifecycle</strong></td>
1832
+ <td>Vòng đời chuyển đổi profile zero-downtime, ghi cấu hình CAS và sync interceptor.</td>
1833
+ <td><a href="docs/diagrams/blindfold-switch-lifecycle.html" target="_blank" style="color:var(--accent);">⚡ Mở Interactive Viewer</a></td>
1834
+ </tr>
1835
+ <tr>
1836
+ <td><strong>Model Name Resolution</strong></td>
1837
+ <td>Cơ chế ánh xạ tên mô hình công khai và phân giải slot Codex.</td>
1838
+ <td><a href="docs/diagrams/codex-model-name-resolution.html" target="_blank" style="color:var(--accent);">🏷️ Mở Interactive Viewer</a></td>
1839
+ </tr>
1840
+ </tbody>
1841
+ </table>
1842
+ </div>
1843
+ </section>
1844
+
1845
+ <!-- 2. INSTALLATION GUIDE -->
1846
+ <section class="doc-section" id="doc-install">
1847
+ <div class="doc-section-h">
1848
+ <h2>
1849
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4M7 10l5 5 5-5M12 15V3"/></svg>
1850
+ 2. Hướng Dẫn Cài Đặt Chi Tiết & Toàn Diện (Đa Nền Tảng)
1851
+ </h2>
1852
+ <span class="badge" style="background:var(--ok-soft); color:var(--ok); font-weight:600;">Linux · macOS · Windows</span>
1853
+ </div>
1854
+ <div class="doc-prose">
1855
+ <p>
1856
+ <strong>Yêu cầu hệ thống:</strong> Node.js phiên bản <strong>18.17 trở lên</strong> (LTS). Gateway không yêu cầu bất kỳ thư viện npm phụ thuộc nào bên ngoài (Zero Dependencies).
1857
+ </p>
1858
+
1859
+ <h3>Cách 1: Cài Đặt Toàn Cục Qua npm (Khuyên dùng cho Người dùng cuối)</h3>
1860
+ <div class="doc-codebox">
1861
+ <div class="doc-codebox-bar">
1862
+ <span>Terminal · npm global install</span>
1863
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'npm install -g llm-switcher\nmkdir -p -m 700 ~/.llm-switcher\ncp &quot;$(npm root -g)/llm-switcher/config.example.json&quot; ~/.llm-switcher/config.json')">Copy</button>
1864
+ </div>
1865
+ <pre>npm install -g llm-switcher
1866
+
1867
+ # Khởi tạo thư mục dữ liệu với quyền bảo mật 700 và sao chép cấu hình mẫu:
1868
+ mkdir -p -m 700 ~/.llm-switcher
1869
+ cp "$(npm root -g)/llm-switcher/config.example.json" ~/.llm-switcher/config.json</pre>
1870
+ </div>
1871
+ <p>
1872
+ <em>Ghi chú:</em> Khi cài đặt qua npm, file cấu hình <code>config.json</code>, khóa bảo mật <code>admin.token</code> và các file launcher được lưu trữ an toàn trong <code>~/.llm-switcher</code>. Khi bạn chạy lệnh nâng cấp (<code>switch update</code>), npm chỉ ghi đè thư mục package code, cấu hình và token của bạn được giữ nguyên vẹn.
1873
+ </p>
1874
+
1875
+ <h3>Cách 2: Cài Đặt Từ Git Clone (Dành cho Developer & Tự build)</h3>
1876
+ <div class="doc-codebox">
1877
+ <div class="doc-codebox-bar">
1878
+ <span>Terminal · Git Checkout</span>
1879
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'git clone https://github.com/louisphamdev/llm-switcher.git\ncd llm-switcher\ncp config.example.json config.json')">Copy</button>
1880
+ </div>
1881
+ <pre>git clone https://github.com/louisphamdev/llm-switcher.git
1882
+ cd llm-switcher
1883
+
1884
+ # Sao chép file cấu hình mẫu (config.json được gitignore bảo vệ an toàn):
1885
+ cp config.example.json config.json</pre>
1886
+ </div>
1887
+ <p>
1888
+ Với bản git checkout, thư mục dữ liệu nằm ngay tại thư mục checkout. Nếu muốn chỉ định một thư mục dữ liệu khác bất kỳ, hãy đặt biến môi trường <code>LLM_SWITCHER_HOME=/path/to/data</code>.
1889
+ </p>
1890
+
1891
+ <h3>Cài Đặt Launcher Shims (Bắt buộc để định tuyến trong suốt)</h3>
1892
+ <p>
1893
+ Lệnh <code>switch shim install</code> tạo các wrapper siêu nhẹ trong <code>~/.llm-switcher/bin</code> để tiêm biến môi trường (<code>HTTPS_PROXY</code>, chứng chỉ CA, <code>CLOUD_CODE_URL</code>) vào từng tiến trình CLI độc lập mà không can thiệp vào shell hay file cài đặt của hệ thống.
1894
+ </p>
1895
+ <div class="doc-codebox">
1896
+ <div class="doc-codebox-bar">
1897
+ <span>Terminal · Cấu hình PATH cho Shims</span>
1898
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'switch shim install\n# Linux & macOS: Thêm vào ~/.bashrc hoặc ~/.zshrc:\nexport PATH=&quot;$HOME/.llm-switcher/bin:$PATH&quot;')">Copy</button>
1899
+ </div>
1900
+ <pre>switch shim install
1901
+
1902
+ # Linux & macOS (Bash/Zsh): Thêm dòng sau vào ~/.bashrc hoặc ~/.zshrc rồi mở terminal mới:
1903
+ export PATH="$HOME/.llm-switcher/bin:$PATH"
1904
+
1905
+ # Windows (Command Prompt / PowerShell):
1906
+ # Thêm %USERPROFILE%\.llm-switcher\bin lên ĐẦU trong biến môi trường PATH của User:
1907
+ # (System Properties ➔ Environment Variables ➔ User Variables ➔ Path)</pre>
1908
+ </div>
1909
+
1910
+ <div class="doc-callout warning">
1911
+ <strong>Tại sao Launcher Shims là bắt buộc?</strong> Khi bạn mở một terminal mới và chạy <code>claude --resume</code>, nếu không có shim, shell sẽ chạy binary gốc và gọi thẳng tới server của Anthropic mà không đi qua Gateway (bỏ qua Healer và pool quota). Shim nằm ở đầu <code>PATH</code> sẽ chặn trước, nạp đúng biến định tuyến in-memory cho tiến trình đó và dọn sạch các biến rác cũ.
1912
+ </div>
1913
+
1914
+ <h3>Cài Đặt Dịch Vụ Nền Tự Khởi Động (OS Background Service)</h3>
1915
+ <p>
1916
+ Tự động đăng ký dịch vụ chạy ngầm cùng hệ điều hành: <code>systemd</code> (Linux), <code>launchd</code> (macOS) hoặc <code>Task Scheduler</code> (Windows).
1917
+ </p>
1918
+ <div class="doc-codebox">
1919
+ <div class="doc-codebox-bar">
1920
+ <span>Terminal · Service Install & Quản trị Daemon</span>
1921
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'switch service install --autoupdate\nswitch service status\nswitch service uninstall')">Copy</button>
1922
+ </div>
1923
+ <pre># Cài đặt daemon chạy ngầm, tự động cập nhật bản mới nhất mỗi khi đăng nhập:
1924
+ switch service install --autoupdate
1925
+
1926
+ # Kiểm tra trạng thái dịch vụ nền:
1927
+ switch service status
1928
+
1929
+ # Gỡ bỏ dịch vụ nền khi không cần nữa:
1930
+ switch service uninstall</pre>
1931
+ </div>
1932
+ <div class="doc-callout info">
1933
+ <strong>Ghi chú cho Linux / Server SSH:</strong> Dịch vụ trên Linux là <code>systemd --user</code> service. Để gateway tiếp tục chạy sau khi bạn thoát phiên SSH, hãy chạy lệnh: <code>loginctl enable-linger $USER</code> một lần duy nhất.
1934
+ </div>
1935
+
1936
+ <h3>Nâng Cấp Phiên Bản Mới (Seamless Hot Update)</h3>
1937
+ <div class="doc-codebox">
1938
+ <div class="doc-codebox-bar">
1939
+ <span>Terminal · switch update</span>
1940
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'switch update')">Copy</button>
1941
+ </div>
1942
+ <pre>switch update</pre>
1943
+ </div>
1944
+ <div class="doc-callout info">
1945
+ <strong>💡 Thiết lập Upstream Pooler:</strong> Sau khi cài đặt LLM Switcher trên máy trạm, bạn nên cài đặt thêm <strong>intact Gateway</strong> (chạy trên server riêng hoặc ngay trên localhost) để quản lý đa tài khoản, xoay vòng key và kích hoạt tính năng <strong>Bifrost Passthrough</strong>. Xem cẩm nang chi tiết tại mục kế tiếp: <a href="#doc-intact" style="color:var(--accent); font-weight:600;">3. intact Gateway — Cài Đặt & Vận Hành</a>.
1946
+ </div>
1947
+ </div>
1948
+ </section>
1949
+
1950
+ <!-- 3. INTACT GATEWAY GUIDE -->
1951
+ <section class="doc-section" id="doc-intact">
1952
+ <div class="doc-section-h">
1953
+ <h2>
1954
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="2" y="2" width="20" height="8" rx="2"/><rect x="2" y="14" width="20" height="8" rx="2"/><line x1="6" y1="6" x2="6.01" y2="6"/><line x1="6" y1="18" x2="6.01" y2="18"/></svg>
1955
+ 3. intact Gateway — Cài Đặt & Vận Hành Upstream Pooler (Khuyên Dùng)
1956
+ </h2>
1957
+ <span class="badge" style="background:var(--accent-soft); color:var(--accent); font-weight:600;">Recommended Companion · Multi-Account Pooler · Zero-CGO</span>
1958
+ </div>
1959
+ <div class="doc-prose">
1960
+ <p>
1961
+ <strong>intact</strong> là cổng ủy quyền lưu trữ credential (Credential Proxy) và bộ điều phối pooling đa tài khoản LLM hiệu năng cao được xây dựng chuyên biệt để làm upstream cho <strong>LLM Switcher</strong>. Được viết bằng ngôn ngữ Go thuần (Single Static Binary, <strong>Zero-CGO</strong> với driver SQLite Pure-Go, nhúng toàn bộ Web UI quản trị), intact khởi động tức thì, tiêu tốn cực ít RAM (&lt;30MB) và phục vụ toàn bộ các tài khoản của bạn sau một Base URL thống nhất <code>/v1</code>.
1962
+ </p>
1963
+
1964
+ <div class="doc-callout success">
1965
+ <strong>Mối quan hệ cộng sinh hoàn hảo giữa LLM Switcher và intact:</strong>
1966
+ <ul>
1967
+ <li><strong>LLM Switcher (Client Edge :3456 / :3457):</strong> Chạy trên từng máy dev, bóc tách TLS blindfold không cảnh báo, bảo vệ file cấu hình gốc (Zero-Mutation), tự động vá lỗi schema và cứu tool-result mồ côi bằng Healer Engine, duy trì thinking budget.</li>
1968
+ <li><strong>intact Gateway (Server Pooler :20142):</strong> Chạy trên máy chủ tập trung (hoặc localhost), lưu trữ tập trung và an toàn các API Key / OAuth Token nhạy cảm, cân bằng tải xoay vòng nhiều tài khoản (Round Robin / Fallback), xử lý 429 thông minh (429 Retry Shorter), gắn kết phiên (Conversation stickiness) tối ưu hóa Prompt Cache, tự động lọc lỗi bằng Blacklist và đối chiếu Contract Lab.</li>
1969
+ </ul>
1970
+ </div>
1971
+
1972
+ <h3>2 Tính Năng Đột Phá Độc Quyền Khi Dùng Cặp Đôi Này</h3>
1973
+ <ul>
1974
+ <li>
1975
+ <strong>Bifrost Passthrough Tối Thượng:</strong> Khi một AI Coding CLI (Claude Code, OpenAI Codex, Antigravity CLI <code>agy</code>) gọi đến đúng tài khoản của chính nhà cung cấp đó trên pool intact, request sẽ đi xuyên qua nguyên vẹn <strong>100% byte-for-byte</strong>. Chỉ có token xác thực là được thay thế an toàn. Bộ chuyển đổi giao thức, Healer Engine và Blacklist đều được bỏ qua. intact tự động cấp cờ <code>bifrost_ua</code> trong <code>GET /v1/models/{model}</code> để Switcher kích hoạt chế độ này hoàn toàn tự động.
1976
+ </li>
1977
+ <li>
1978
+ <strong>Chu Trình Tự Cải Thiện 2 Chiều (Two-Loop Self-Improvement):</strong>
1979
+ <ul>
1980
+ <li><em>Vòng 1 (intact sửa lỗi upstream từ chối):</em> Tự động phát hiện lỗi 429 giả mạo từ upstream, sử dụng thuật toán chia đôi (binary search halving) system prompt để cô lập chính xác câu văn bị nhà cung cấp chặn (ví dụ các câu <em>"You are Codex, an agent based on GPT-5"</em> hay <em>"You are a Claude agent..."</em> bị Google Antigravity chặn bằng fake 429), rồi lưu thành bộ lọc blacklist trong cơ sở dữ liệu SQLite. Toàn bộ máy dev nhận bản vá tức thì mà không cần cập nhật gateway.</li>
1981
+ <li><em>Vòng 2 (Switcher sửa lỗi converter làm mất):</em> Khi bật Contract Lab, Switcher gửi các mẫu trao đổi đã che dữ liệu người dùng (giữ nguyên độ dài chuỗi và enum) lên intact. intact so khớp và ghi nhận từng field bị mất. Lệnh <code>switch contract-check</code> tự động tạo test case đỏ và bản sửa nằm trong bộ converter.</li>
1982
+ </ul>
1983
+ </li>
1984
+ </ul>
1985
+
1986
+ <h3>Hướng Dẫn Cài Đặt intact Chi Tiết</h3>
1987
+ <p>
1988
+ intact được đóng gói sẵn thành file binary tĩnh duy nhất cho mọi kiến trúc hệ điều hành: Linux (x64, arm64), macOS (Apple Silicon M-series, Intel), Windows (x64, arm64).
1989
+ </p>
1990
+
1991
+ <h4>Cách 1: Cài Đặt Toàn Cục Qua npm (Khuyên dùng)</h4>
1992
+ <div class="doc-codebox">
1993
+ <div class="doc-codebox-bar">
1994
+ <span>Terminal · npm global install</span>
1995
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'npm install -g intact-gateway\n# Khởi động intact lần đầu:\nintact -db /opt/intact/intact.db -addr 127.0.0.1:20142')">Copy</button>
1996
+ </div>
1997
+ <pre>npm install -g intact-gateway
1998
+
1999
+ # Khởi tạo và chạy intact với file database SQLite chỉ định:
2000
+ intact -db /opt/intact/intact.db -addr 127.0.0.1:20142</pre>
2001
+ </div>
2002
+
2003
+ <h4>Cách 2: Build Từ Mã Nguồn Go (Zero-CGO, Không Cần Toolchain C)</h4>
2004
+ <div class="doc-codebox">
2005
+ <div class="doc-codebox-bar">
2006
+ <span>Terminal · Go Build</span>
2007
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'git clone https://github.com/louisphamdev/intact.git\ncd intact\nCGO_ENABLED=0 go build -o intact ./cmd/intact\n# Cross-compile cho Linux server từ macOS / Windows:\nGOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags=&quot;-s -w&quot; -o intact-linux ./cmd/intact')">Copy</button>
2008
+ </div>
2009
+ <pre>git clone https://github.com/louisphamdev/intact.git
2010
+ cd intact
2011
+
2012
+ # Biên dịch binary tĩnh:
2013
+ CGO_ENABLED=0 go build -o intact ./cmd/intact
2014
+
2015
+ # Hoặc cross-compile cho Linux Server từ máy macOS / Windows:
2016
+ GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build -ldflags="-s -w" -o intact-linux ./cmd/intact</pre>
2017
+ </div>
2018
+
2019
+ <h3>Bảng Tra Cứu Tham Số Dòng Lệnh (CLI Flags) Của intact</h3>
2020
+ <table class="doc-table">
2021
+ <thead>
2022
+ <tr>
2023
+ <th style="width:28%;">Cờ dòng lệnh</th>
2024
+ <th style="width:25%;">Giá trị mặc định</th>
2025
+ <th>Mô tả chức năng chi tiết</th>
2026
+ </tr>
2027
+ </thead>
2028
+ <tbody>
2029
+ <tr>
2030
+ <td><code>-addr &lt;host:port&gt;</code></td>
2031
+ <td><code>127.0.0.1:20130</code></td>
2032
+ <td>Địa chỉ IP và cổng lắng nghe cho proxy và dashboard (ví dụ: <code>127.0.0.1:20142</code>).</td>
2033
+ </tr>
2034
+ <tr>
2035
+ <td><code>-db &lt;file.db&gt;</code></td>
2036
+ <td><code>intact.db</code></td>
2037
+ <td>Đường dẫn tới tệp SQLite database. Tự động tạo bảng lần đầu và tự migrate schema ở các lần khởi động sau.</td>
2038
+ </tr>
2039
+ <tr>
2040
+ <td><code>-show-totp</code></td>
2041
+ <td><em>(Không)</em></td>
2042
+ <td>In khóa bí mật TOTP của bản cài đặt này và các bước nhập vào app xác thực rồi thoát.</td>
2043
+ </tr>
2044
+ <tr>
2045
+ <td><code>-enroll</code></td>
2046
+ <td><em>(Không)</em></td>
2047
+ <td>Sinh một khóa bí mật TOTP hoàn toàn mới và in ra URI <code>otpauth://</code> để nạp vào <code>INTACT_TOTP_SECRET</code>.</td>
2048
+ </tr>
2049
+ <tr>
2050
+ <td><code>-insecure-no-auth</code></td>
2051
+ <td><em>(Không)</em></td>
2052
+ <td>Khởi động không cần cổng đăng nhập (chỉ cho phép kết nối từ loopback Host để ngăn chặn DNS rebinding).</td>
2053
+ </tr>
2054
+ </tbody>
2055
+ </table>
2056
+
2057
+ <h3>Bảng Tra Cứu Biến Môi Trường (Environment Variables) Của intact</h3>
2058
+ <table class="doc-table">
2059
+ <thead>
2060
+ <tr>
2061
+ <th style="width:34%;">Biến môi trường</th>
2062
+ <th>Mô tả ý nghĩa & cách áp dụng</th>
2063
+ </tr>
2064
+ </thead>
2065
+ <tbody>
2066
+ <tr>
2067
+ <td><code>INTACT_TOTP_SECRET</code></td>
2068
+ <td>Chuỗi Base32 bí mật cho xác thực TOTP trên Dashboard. Khi để trống, intact dùng secret được lưu trong SQLite.</td>
2069
+ </tr>
2070
+ <tr>
2071
+ <td><code>INTACT_API_TOKEN</code></td>
2072
+ <td>Master token. Cho phép gọi toàn quyền vào <code>/v1</code>, <code>/api/*</code> và <code>/mcp</code> mà không cần session cookie.</td>
2073
+ </tr>
2074
+ <tr>
2075
+ <td><code>INTACT_SESSION_KEY</code></td>
2076
+ <td>Khóa ký HMAC cho cookie phiên dashboard. Khi để trống, intact sinh khóa ngẫu nhiên trong RAM mỗi lần khởi động.</td>
2077
+ </tr>
2078
+ <tr>
2079
+ <td><code>INTACT_SESSION_TTL</code></td>
2080
+ <td>Thời gian sống của phiên đăng nhập dashboard tính bằng giây (mặc định: <code>43200</code> = 12 giờ).</td>
2081
+ </tr>
2082
+ <tr>
2083
+ <td><code>INTACT_OWNER_LOOPBACK</code></td>
2084
+ <td>Đặt <code>1</code> để cho phép chủ sở hữu kết nối qua SSH Tunnel loopback không bị giới hạn số lần thử mã đăng nhập.</td>
2085
+ </tr>
2086
+ <tr>
2087
+ <td><code>INTACT_ANTIGRAVITY_CLIENT_SECRET</code></td>
2088
+ <td>Google OAuth client secret của app Antigravity. intact tự động tải lần đầu; chỉ đặt khi lookup tự động bị chặn.</td>
2089
+ </tr>
2090
+ <tr>
2091
+ <td><code>INTACT_ARENA_URL</code></td>
2092
+ <td>Mirror của Hugging Face datasets server để tải bảng xếp hạng LMArena định kỳ (mặc định: <code>https://datasets-server.huggingface.co</code>).</td>
2093
+ </tr>
2094
+ </tbody>
2095
+ </table>
2096
+
2097
+ <h3>Triển Khai Dịch Vụ Nền Production (Systemd Service trên Linux)</h3>
2098
+ <p>
2099
+ Tạo file cấu hình dịch vụ tại <code>/etc/systemd/system/intact.service</code>:
2100
+ </p>
2101
+ <div class="doc-codebox">
2102
+ <div class="doc-codebox-bar">
2103
+ <span>/etc/systemd/system/intact.service</span>
2104
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, '[Unit]\nDescription=intact LLM Credential Proxy and Account Pooler\nAfter=network.target\n\n[Service]\nUMask=0077\nEnvironmentFile=/opt/intact/intact.env\nExecStart=/opt/intact/intact -db /opt/intact/intact.db -addr 127.0.0.1:20142\nRestart=on-failure\nWorkingDirectory=/opt/intact\n\n[Install]\nWantedBy=multi-user.target')">Copy</button>
2105
+ </div>
2106
+ <pre>[Unit]
2107
+ Description=intact LLM Credential Proxy and Account Pooler
2108
+ After=network.target
2109
+
2110
+ [Service]
2111
+ UMask=0077
2112
+ EnvironmentFile=/opt/intact/intact.env
2113
+ ExecStart=/opt/intact/intact -db /opt/intact/intact.db -addr 127.0.0.1:20142
2114
+ Restart=on-failure
2115
+ WorkingDirectory=/opt/intact
2116
+
2117
+ [Install]
2118
+ WantedBy=multi-user.target</pre>
2119
+ </div>
2120
+ <p>
2121
+ Tạo thư mục làm việc, file biến môi trường bảo mật <code>/opt/intact/intact.env</code> với quyền <code>0600</code> và kích hoạt dịch vụ:
2122
+ </p>
2123
+ <div class="doc-codebox">
2124
+ <div class="doc-codebox-bar">
2125
+ <span>Terminal · Kích hoạt service</span>
2126
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'sudo mkdir -p -m 700 /opt/intact\nsudo touch /opt/intact/intact.env\nsudo chmod 600 /opt/intact/intact.env\nsudo systemctl daemon-reload\nsudo systemctl enable --now intact')">Copy</button>
2127
+ </div>
2128
+ <pre>sudo mkdir -p -m 700 /opt/intact
2129
+ sudo touch /opt/intact/intact.env
2130
+ sudo chmod 600 /opt/intact/intact.env
2131
+ sudo systemctl daemon-reload
2132
+ sudo systemctl enable --now intact</pre>
2133
+ </div>
2134
+
2135
+ <h3>Đưa intact Ra Internet An Toàn Qua Cloudflare Tunnel (Zero-Trust Ingress)</h3>
2136
+ <p>
2137
+ Không cần mở bất kỳ cổng firewall nào trên VPS! Sử dụng Cloudflare Tunnel trỏ tên miền của bạn về cổng loopback của intact:
2138
+ </p>
2139
+ <div class="doc-codebox">
2140
+ <div class="doc-codebox-bar">
2141
+ <span>/etc/cloudflared/config.yml (Ingress snippet)</span>
2142
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'ingress:\n - hostname: intact.yourdomain.com\n service: http://127.0.0.1:20142\n - service: http_status:404')">Copy</button>
2143
+ </div>
2144
+ <pre>ingress:
2145
+ - hostname: intact.yourdomain.com
2146
+ service: http://127.0.0.1:20142
2147
+ - service: http_status:404</pre>
2148
+ </div>
2149
+
2150
+ <h3>Sao Lưu & Nâng Cấp An Toàn (Database Integrity Guarantee)</h3>
2151
+ <p>
2152
+ Vì SQLite sử dụng chế độ WAL (Write-Ahead Logging), <strong>tuyệt đối không dùng lệnh <code>cp</code> sao chép file trực tiếp</strong> vì có thể làm mất dữ liệu chưa kịp flush vào đĩa. Hãy dùng API Online Backup chuẩn:
2153
+ </p>
2154
+ <div class="doc-codebox">
2155
+ <div class="doc-codebox-bar">
2156
+ <span>Terminal · SQLite Online Backup</span>
2157
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'install -d -m 0700 /opt/intact/backup\n(umask 077 && sqlite3 /opt/intact/intact.db &quot;.backup /opt/intact/backup/intact-$(date +%F-%H%M).db&quot;)')">Copy</button>
2158
+ </div>
2159
+ <pre>install -d -m 0700 /opt/intact/backup
2160
+ (umask 077 && sqlite3 /opt/intact/intact.db ".backup /opt/intact/backup/intact-$(date +%F-%H%M).db")</pre>
2161
+ </div>
2162
+ <p>
2163
+ Để nâng cấp: giữ lại file binary cũ <code>intact.prev</code>, tải bản mới thay thế và chạy <code>sudo systemctl restart intact</code>. Schema database sẽ tự động được nâng cấp tương thích ngược.
2164
+ </p>
2165
+
2166
+ <h3>Cẩm Nang Sử Dụng & Vận Hành intact Chi Tiết Nhất</h3>
2167
+ <ol>
2168
+ <li>
2169
+ <strong>Xác Thực TOTP & Đăng Nhập Lần Đầu (Zero-Password):</strong>
2170
+ <p>
2171
+ Chạy lệnh <code>intact -db /opt/intact/intact.db -show-totp</code> (hoặc kiểm tra log <code>journalctl -u intact</code>). Mở Google Authenticator, 1Password hoặc Aegis trên điện thoại, chọn <em>"Enter a setup key"</em>, nhập tên <code>intact</code> và khóa bí mật in ra. Truy cập giao diện Web Dashboard và nhập mã 6 số để đăng nhập.
2172
+ </p>
2173
+ </li>
2174
+ <li>
2175
+ <strong>Thêm Nhà Cung Cấp & Kết Nối (Providers & Connections):</strong>
2176
+ <p>
2177
+ Vào mục <strong>Providers → Add</strong> trên thanh menu của intact:
2178
+ </p>
2179
+ <ul>
2180
+ <li><strong>API-Key Providers:</strong> Groq, OpenRouter, Vertex AI, NVIDIA NIM, DeepSeek, Cloudflare Workers AI... (chỉ cần dán API key).</li>
2181
+ <li><strong>Coding Tool OAuth Sign-ins (Class A):</strong> Claude Code, OpenAI Codex, Antigravity (Google), GitHub Copilot (đăng nhập bằng browser OAuth hoặc device-code).</li>
2182
+ </ul>
2183
+ <div class="doc-callout info">
2184
+ <strong>Lưu ý về Antigravity:</strong> Lần đăng nhập tài khoản Antigravity đầu tiên có thể mất khoảng 1 phút do intact tự động liên hệ Google để tải về OAuth client secret.
2185
+ </div>
2186
+ </li>
2187
+ <li>
2188
+ <strong>Chiến Lược Điều Phối Tải & Xoay Vòng (Rotation & Failover):</strong>
2189
+ <ul>
2190
+ <li><strong>Round Robin (1 req/turn):</strong> Chia đều tải qua từng request giữa các tài khoản đang hoạt động.</li>
2191
+ <li><strong>Round Robin (N reqs/turn):</strong> Phục vụ liên tục N request trên một tài khoản trước khi chuyển sang tài khoản kế tiếp. <em>Đây là chế độ lý tưởng để giữ ấm Prompt Cache (Anthropic/OpenAI prompt cache hits), giúp tiết kiệm 50-90% chi phí và giảm độ trễ tối đa.</em></li>
2192
+ <li><strong>Fallback:</strong> Luôn ưu tiên tài khoản chính (Primary). Chỉ chuyển sang tài khoản dự phòng khi tài khoản chính gặp lỗi 429/500/503 hoặc cạn quota.</li>
2193
+ <li><strong>Standby Account:</strong> Đánh dấu tài khoản dự phòng ngủ đông, chỉ được kích hoạt khi toàn bộ tài khoản chính gặp sự cố.</li>
2194
+ <li><strong>Conversation Stickiness (Gắn kết phiên):</strong> intact tự động nhận diện phiên hội thoại theo Session ID (<code>X-Claude-Code-Session-Id</code>, <code>session_id</code>) và ghim hội thoại vào tài khoản phục vụ trước đó trong vòng 1 giờ để tối đa hoá cache.</li>
2195
+ <li><strong>429 Retry Shorter Đột Phá:</strong> Khi một cuộc trò chuyện dài gặp lỗi rate limit 429 token-per-minute, intact tự động cắt ngắn lịch sử ngữ cảnh và gửi thử lại ngay lập tức trên tài khoản tiếp theo, bảo vệ phiên làm việc của bạn không bị crash giữa chừng.</li>
2196
+ </ul>
2197
+ </li>
2198
+ <li>
2199
+ <strong>Tạo API Key Cho Client:</strong>
2200
+ <p>
2201
+ Vào <strong>Endpoint → API keys → Create</strong>, đặt tên và sao chép mã khóa có định dạng <code>sk-intact-...</code>. Key này sẽ được dán vào cấu hình của LLM Switcher hoặc các ứng dụng bên thứ ba.
2202
+ </p>
2203
+ </li>
2204
+ <li>
2205
+ <strong>Quy Tắc Định Tuyến Mô Hình (Model Routing Syntax):</strong>
2206
+ <ul>
2207
+ <li><strong>Chỉ định rõ provider:</strong> <code>groq/llama-3.3-70b-versatile</code>, <code>antigravity/gemini-3.8-flash-high</code>, <code>codex/gpt-5.5</code>.</li>
2208
+ <li><strong>Tên model trần (bare id):</strong> <code>llama-3.3-70b-versatile</code> (intact sẽ tự động gộp tất cả các tài khoản của mọi provider hỗ trợ mô hình đó thành một pool chung).</li>
2209
+ </ul>
2210
+ </li>
2211
+ <li>
2212
+ <strong>Thử Nghiệm & Giám Sát Với Contract Lab:</strong>
2213
+ <p>
2214
+ Chạy lệnh <code>switch contract-probe</code> từ máy trạm để gửi bộ 6 biến thể dữ liệu mẫu tới intact, kiểm tra độ tương thích của model. Chạy <code>switch contract-check</code> để kéo về các finding sai lệch schema và tự động sinh test case.
2215
+ </p>
2216
+ </li>
2217
+ </ol>
2218
+
2219
+ <h3>Mẫu Cấu Hình Tích Hợp Hoàn Hảo Trong LLM Switcher</h3>
2220
+ <p>
2221
+ Cấu hình <code>config.json</code> trên máy trạm để chuyển tiếp lưu lượng Claude Code, Codex và agy qua intact:
2222
+ </p>
2223
+ <div class="doc-codebox">
2224
+ <div class="doc-codebox-bar">
2225
+ <span>config.json (Tích hợp intact)</span>
2226
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, document.getElementById('code-intact-config-sample').textContent)">Copy</button>
2227
+ </div>
2228
+ <pre id="code-intact-config-sample">{
2229
+ "port": 3456,
2230
+ "activeProfiles": {
2231
+ "claude": "intact-claude",
2232
+ "codex": "intact-codex",
2233
+ "agy": "agy-intact"
2234
+ },
2235
+ "profiles": {
2236
+ "intact-claude": {
2237
+ "name": "Claude via intact",
2238
+ "tool": "claude",
2239
+ "mode": "convert",
2240
+ "outFormat": "openai-chat",
2241
+ "baseURL": "https://intact.yourdomain.com/v1",
2242
+ "apiKey": "sk-intact-...",
2243
+ "thinkingMode": "auto"
2244
+ },
2245
+ "intact-codex": {
2246
+ "name": "Codex via intact",
2247
+ "tool": "codex",
2248
+ "mode": "convert",
2249
+ "outFormat": "openai-chat",
2250
+ "baseURL": "https://intact.yourdomain.com/v1",
2251
+ "apiKey": "sk-intact-...",
2252
+ "defaultModels": {
2253
+ "main": "gemini-3.8-flash",
2254
+ "review": "gemini-3.7-flash-medium",
2255
+ "subagent": "gemini-3.6-flash-low"
2256
+ }
2257
+ },
2258
+ "agy-intact": {
2259
+ "name": "agy on intact",
2260
+ "tool": "agy",
2261
+ "mode": "convert",
2262
+ "outFormat": "openai-chat",
2263
+ "baseURL": "https://intact.yourdomain.com/v1",
2264
+ "apiKey": "sk-intact-...",
2265
+ "defaultModels": { "main": "antigravity/*" } // Kích hoạt Bifrost passthrough
2266
+ }
2267
+ },
2268
+ "contractLab": {
2269
+ "url": "https://intact.yourdomain.com",
2270
+ "apiKey": "sk-intact-...",
2271
+ "enabled": true
2272
+ }
2273
+ }</pre>
2274
+ </div>
2275
+ <div class="doc-callout info">
2276
+ <strong>Mẹo cấu hình nhanh trên Web UI:</strong> Tại tab <strong>Routes & Profiles</strong>, nhấn <strong>+ Add Profile</strong> và chọn nút <strong>Template Quick-Fill: intact</strong> hoặc <strong>agy on intact</strong> để tự động điền sẵn toàn bộ tham số tối ưu chỉ với 1 click!
2277
+ </div>
2278
+ </div>
2279
+ </section>
2280
+
2281
+ <!-- 4. CLI & OPERATION GUIDE -->
2282
+ <section class="doc-section" id="doc-cli">
2283
+ <div class="doc-section-h">
2284
+ <h2>
2285
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polyline points="4 17 10 11 4 5"/><line x1="12" y1="19" x2="20" y2="19"/></svg>
2286
+ 4. Lệnh CLI & Cẩm Nang Vận Hành
2287
+ </h2>
2288
+ <span class="badge" style="background:var(--card2); color:var(--muted);">Bảng Tra Cứu Toàn Diện (Cheatsheet)</span>
2289
+ </div>
2290
+ <div class="doc-prose">
2291
+ <p>
2292
+ CLI <code>switch</code> cung cấp giao diện quản trị đồng nhất trên toàn bộ các hệ điều hành:
2293
+ </p>
2294
+ <table class="doc-table">
2295
+ <thead>
2296
+ <tr>
2297
+ <th style="width:36%;">Lệnh CLI & Cờ</th>
2298
+ <th>Mô tả chức năng chi tiết</th>
2299
+ </tr>
2300
+ </thead>
2301
+ <tbody>
2302
+ <tr>
2303
+ <td><code>switch ui</code> / <code>web</code> / <code>gui</code></td>
2304
+ <td>Mở giao diện Web Dashboard trên trình duyệt mặc định kèm access token an toàn.</td>
2305
+ </tr>
2306
+ <tr>
2307
+ <td><code>switch status</code> / <code>st</code></td>
2308
+ <td>Hiển thị trạng thái hoạt động, cổng kết nối và cấu hình routing của cả 3 công cụ CLI.</td>
2309
+ </tr>
2310
+ <tr>
2311
+ <td><code>switch version</code> / <code>-v</code></td>
2312
+ <td>Hiển thị phiên bản hiện tại và kiểm tra xem trên npm có bản phát hành mới hơn không.</td>
2313
+ </tr>
2314
+ <tr>
2315
+ <td><code>switch doctor</code> / <code>audit</code></td>
2316
+ <td>Quét chẩn đoán: kiểm tra liveness, xung đột port, rò rỉ biến môi trường, shims và phiên CLI đang chạy.</td>
2317
+ </tr>
2318
+ <tr>
2319
+ <td><code>switch update</code> / <code>upgrade</code></td>
2320
+ <td>Tải và nâng cấp lên bản phát hành mới nhất từ npm/git, khởi động lại gateway không làm rớt phiên.</td>
2321
+ </tr>
2322
+ <tr>
2323
+ <td><code>switch on [profile]</code> / <code>start</code></td>
2324
+ <td>Khởi động Gateway và kích hoạt profile (mặc định nếu không truyền tên) cho tất cả target tương thích.</td>
2325
+ </tr>
2326
+ <tr>
2327
+ <td><code>switch &lt;profile&gt;</code></td>
2328
+ <td>Kích hoạt cùng một profile cho các công cụ tương thích.</td>
2329
+ </tr>
2330
+ <tr>
2331
+ <td><code>switch claude &lt;profile&gt;</code></td>
2332
+ <td>Kích hoạt profile chỉ định riêng cho <strong>Claude Code</strong>.</td>
2333
+ </tr>
2334
+ <tr>
2335
+ <td><code>switch codex &lt;profile&gt;</code></td>
2336
+ <td>Kích hoạt profile chỉ định riêng cho <strong>OpenAI Codex</strong>.</td>
2337
+ </tr>
2338
+ <tr>
2339
+ <td><code>switch agy &lt;profile&gt;</code></td>
2340
+ <td>Kích hoạt profile chỉ định riêng cho <strong>Antigravity CLI (agy)</strong>.</td>
2341
+ </tr>
2342
+ <tr>
2343
+ <td><code>switch port &lt;số&gt;</code></td>
2344
+ <td>Đổi cổng lắng nghe của Gateway (mặc định: 3456) và tự động khởi động lại service.</td>
2345
+ </tr>
2346
+ <tr>
2347
+ <td><code>switch service install [--autoupdate]</code></td>
2348
+ <td>Cài đặt dịch vụ nền tự khởi động cùng OS (systemd / launchd / Task Scheduler).</td>
2349
+ </tr>
2350
+ <tr>
2351
+ <td><code>switch service uninstall</code></td>
2352
+ <td>Gỡ bỏ hoàn toàn dịch vụ nền tự khởi động của hệ điều hành.</td>
2353
+ </tr>
2354
+ <tr>
2355
+ <td><code>switch shim install</code></td>
2356
+ <td>Cài đặt các launcher shim vào <code>~/.llm-switcher/bin</code> để định tuyến trong suốt.</td>
2357
+ </tr>
2358
+ <tr>
2359
+ <td><code>switch shim status</code></td>
2360
+ <td>Kiểm tra tình trạng các launcher shim và phát hiện phiên CLI chạy lọt ra ngoài.</td>
2361
+ </tr>
2362
+ <tr>
2363
+ <td><code>switch shim uninstall</code></td>
2364
+ <td>Gỡ bỏ các launcher shim khỏi thư mục bin.</td>
2365
+ </tr>
2366
+ <tr>
2367
+ <td><code>switch plugin install</code></td>
2368
+ <td>Cài đặt thông báo trạng thái định tuyến (hook notice) bên trong Claude Code, Codex và agy.</td>
2369
+ </tr>
2370
+ <tr>
2371
+ <td><code>switch plugin status</code></td>
2372
+ <td>Kiểm tra tình trạng các plugin hook thông báo trạng thái.</td>
2373
+ </tr>
2374
+ <tr>
2375
+ <td><code>switch plugin uninstall</code></td>
2376
+ <td>Gỡ bỏ các plugin hook thông báo khỏi cấu hình các công cụ.</td>
2377
+ </tr>
2378
+ <tr>
2379
+ <td><code>switch off [claude|codex|agy]</code></td>
2380
+ <td>Khôi phục endpoint chính hãng (cho toàn bộ công cụ nếu không truyền tham số, hoặc cho một công cụ cụ thể).</td>
2381
+ </tr>
2382
+ <tr>
2383
+ <td><code>switch models [--refresh]</code></td>
2384
+ <td>Xem danh mục mô hình phát hiện được cho Claude Code & Codex; <code>--refresh</code> để cập nhật từ upstream.</td>
2385
+ </tr>
2386
+ <tr>
2387
+ <td><code>switch compact on</code> / <code>off</code></td>
2388
+ <td>Bật / tắt tính năng nén prefix ngữ cảnh khi nhàn rỗi cho Claude Code.</td>
2389
+ </tr>
2390
+ <tr>
2391
+ <td><code>switch compact codex on</code> / <code>off</code></td>
2392
+ <td>Bật / tắt nén prefix ngữ cảnh trên đường truyền cho Codex Responses.</td>
2393
+ </tr>
2394
+ <tr>
2395
+ <td><code>switch compact model &lt;id|none&gt;</code></td>
2396
+ <td>Chỉ định model tóm tắt chạy ngầm (hoặc <code>none</code> để dùng thuật toán cắt tỉa tất định không tốn token).</td>
2397
+ </tr>
2398
+ <tr>
2399
+ <td><code>switch compact idle &lt;phút&gt;</code></td>
2400
+ <td>Đặt ngưỡng thời gian nhàn rỗi (1 – 1440 phút, mặc định: 15) trước khi kích hoạt nén cache.</td>
2401
+ </tr>
2402
+ <tr>
2403
+ <td><code>switch compact min &lt;KB&gt;</code></td>
2404
+ <td>Đặt ngưỡng kích thước payload tối thiểu (8 – 4096 KB, mặc định: 64) để áp dụng nén.</td>
2405
+ </tr>
2406
+ <tr>
2407
+ <td><code>switch compact status</code></td>
2408
+ <td>Hiển thị chi tiết cấu hình và chính sách Idle Compaction hiện tại.</td>
2409
+ </tr>
2410
+ <tr>
2411
+ <td><code>switch contract-probe [--model m]</code></td>
2412
+ <td>Gửi tổ hợp 6 test request kiểm tra hợp đồng chuyển đổi qua Gateway lên intact.</td>
2413
+ </tr>
2414
+ <tr>
2415
+ <td><code>switch contract-check</code></td>
2416
+ <td>Lấy các trường dữ liệu bị mất từ intact và tự động sinh test case kiểm thử độ chính xác.</td>
2417
+ </tr>
2418
+ <tr>
2419
+ <td><code>--port &lt;n&gt;</code> / <code>-p &lt;n&gt;</code></td>
2420
+ <td>Cờ toàn cục để ghi đè tạm thời cổng Gateway cho lệnh đang chạy.</td>
2421
+ </tr>
2422
+ </tbody>
2423
+ </table>
2424
+
2425
+ <h3>Cơ Chế Thông Báo Trạng Thái (Launch Notice)</h3>
2426
+ <p>
2427
+ Để bạn luôn biết phiên làm việc có đang qua Gateway hay không mà không cần mở <code>settings.json</code>:
2428
+ </p>
2429
+ <ul>
2430
+ <li><strong>1. Thông báo từ Shim (Mặc định luôn bật khi có route):</strong> Khi bạn chạy lệnh công cụ, shim in một dòng tóm tắt trên stderr (Linux/macOS) hoặc hiện Windows Toast notification: <code>claude -&gt; intact-claude | intact.example.com | antigravity/gemini-3.8-flash | 1M</code>. Khi công cụ tắt, shim hoàn toàn im lặng.</li>
2431
+ <li><strong>2. Thông báo bên trong Tool (Tùy chọn, cài bằng <code>switch plugin install</code>):</strong> Tạo file hook trực tiếp trong thư mục skills/hooks của tool (Claude Code plugin tại <code>~/.claude/skills/llm-switcher-status/</code>, Codex tại <code>~/.codex/hooks.json</code>, agy tại <code>~/.gemini/antigravity-cli/hooks.json</code>). Báo rõ trạng thái kết nối hoặc cảnh báo nếu Gateway chưa bật.</li>
2432
+ </ul>
2433
+
2434
+ <div class="doc-callout warning">
2435
+ <strong>Quy tắc quan trọng về môi trường:</strong> Tuyệt đối <em>không</em> thêm <code>source env.sh</code> hay <code>call env.cmd</code> vào file rc của shell. Các biến định tuyến được tự động tiêm độc lập vào từng phiên CLI thông qua các wrapper shim trong <code>~/.llm-switcher/bin</code>.
2436
+ </div>
2437
+ </div>
2438
+ </section>
2439
+
2440
+ <!-- 5. BLINDFOLD MODE & CA SECURITY -->
2441
+ <section class="doc-section" id="doc-blindfold">
2442
+ <div class="doc-section-h">
2443
+ <h2>
2444
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>
2445
+ 5. Blindfold Mode & Bảo Mật Chứng Chỉ CA
2446
+ </h2>
2447
+ <span class="badge" style="background:var(--mint); color:#042f2e; font-weight:600;">Port :3457 · Zero-Warning Interceptor</span>
2448
+ </div>
2449
+ <div class="doc-prose">
2450
+ <p>
2451
+ Trên Codex CLI, khi bạn ghi đè Base URL theo cách thông thường (<code>--config openai_base_url=...</code>), giao diện <code>/model</code> sẽ hiển thị một dòng cảnh báo phiền toái: <em>"base URL is overridden to http://... Selecting models may not be supported or work properly."</em>.
2452
+ </p>
2453
+ <p>
2454
+ <strong>Blindfold Mode</strong> giải quyết triệt để vấn đề này. Codex và Claude Code tiếp tục gửi request đến endpoint chính hãng, trong khi LLM Switcher đánh chặn ở tầng mạng thông qua proxy trung gian <code>HTTPS_PROXY=http://127.0.0.1:3457</code>. File cấu hình của tool hoàn toàn nguyên vẹn.
2455
+ </p>
2456
+
2457
+ <h3>Bảng Định Tuyến Cố Định Của Interceptor (:3457)</h3>
2458
+ <table class="doc-table">
2459
+ <thead>
2460
+ <tr>
2461
+ <th style="width:28%;">Domain Đích (CONNECT Host)</th>
2462
+ <th style="width:38%;">Đường Dẫn Bị Đánh Chặn (Về Gateway :3456)</th>
2463
+ <th>Đường Dẫn Còn Lại (Tunnel Ra Internet Thật)</th>
2464
+ </tr>
2465
+ </thead>
2466
+ <tbody>
2467
+ <tr>
2468
+ <td><code>api.anthropic.com</code></td>
2469
+ <td><code>/v1/messages</code>, <code>/v1/messages/*</code></td>
2470
+ <td>Đăng nhập OAuth, telemetry, billing (giữ nguyên vẹn)</td>
2471
+ </tr>
2472
+ <tr>
2473
+ <td><code>api.openai.com</code></td>
2474
+ <td><code>/v1/responses</code>, <code>/v1/models</code></td>
2475
+ <td>Tài khoản, đăng nhập, thanh toán (giữ nguyên vẹn)</td>
2476
+ </tr>
2477
+ <tr>
2478
+ <td><code>chatgpt.com</code></td>
2479
+ <td><code>/backend-api/codex/*</code> (được chuẩn hóa thành <code>/v1</code>)</td>
2480
+ <td>Mọi request web và session khác (giữ nguyên vẹn)</td>
2481
+ </tr>
2482
+ <tr>
2483
+ <td><em>Mọi domain khác (Google, GitHub, Web)</em></td>
2484
+ <td><em>Không chặn</em></td>
2485
+ <td>Tunnel thẳng ra Internet dạng raw TCP, không đọc dữ liệu</td>
2486
+ </tr>
2487
+ <tr>
2488
+ <td><em>Địa chỉ nội bộ (127.0.0.1, private IP)</em></td>
2489
+ <td><em>Bị từ chối (HTTP 403/421)</em></td>
2490
+ <td>Chống tấn công SSRF và lạm dụng mạng nội bộ</td>
2491
+ </tr>
2492
+ </tbody>
2493
+ </table>
2494
+
2495
+ <h3>🔒 Cam Kết An Toàn & Cô Lập Chứng Chỉ CA</h3>
2496
+ <ul>
2497
+ <li><strong>Tự sinh 100% tại máy cục bộ:</strong> Cặp khóa <code>ca.pem</code> và <code>ca.key</code> được tự động tạo lần đầu bằng thư viện crypto chuẩn của Node.js. Private key được bảo vệ với quyền nghiêm ngặt <code>0600</code>. Tuyệt đối không tải key từ Internet.</li>
2498
+ <li><strong>Không can thiệp vào Hệ Điều Hành:</strong> Tuyệt đối <em>không</em> cài chứng chỉ vào Windows Certificate Store, macOS Keychain hay Linux <code>/etc/ssl/certs</code>. Bạn <strong>không cần quyền root hay Administrator</strong>.</li>
2499
+ <li><strong>Chỉ tin cậy trong phạm vi tiến trình (Process-Scoped):</strong> Chứng chỉ chỉ được tiêm vào bộ nhớ RAM của <code>claude</code> (qua <code>NODE_EXTRA_CA_CERTS</code>) và <code>codex</code> (qua <code>CODEX_CA_CERTIFICATE</code>). Trình duyệt web và ứng dụng khác hoàn toàn không tin cậy chứng chỉ này.</li>
2500
+ <li><strong>Ràng buộc tên miền mật mã học (X.509 Name Constraints):</strong> CA được cấu hình bắt buộc chỉ cho phép ký duy nhất cho 3 domain: <code>api.anthropic.com</code>, <code>api.openai.com</code>, và <code>chatgpt.com</code>. Ngay cả khi lộ private key, mọi kết nối tới ngân hàng, Google, GitHub đều bị từ chối ngay lập tức.</li>
2501
+ <li><strong>Bảo toàn chứng chỉ VPN / Doanh nghiệp:</strong> Nếu máy bạn có sẵn CA công ty trong <code>NODE_EXTRA_CA_CERTS</code>, script <code>ensure-ca-bundle.mjs</code> sẽ tự động gộp cả 2 chứng chỉ vào một bundle tạm thời, không bao giờ làm đứt mạng nội bộ.</li>
2502
+ <li><strong>Lưu ý tạo chứng chỉ trên Windows:</strong> Chạy lệnh <code>bash blindfold/make-certs.sh</code> trong <strong>Git Bash</strong>. Tuyệt đối không dùng <code>New-SelfSignedCertificate</code> trong PowerShell vì lệnh này thiếu extendedKeyUsage <code>serverAuth</code> gây lỗi handshake.</li>
2503
+ </ul>
2504
+ </div>
2505
+ </section>
2506
+
2507
+ <!-- 6. ANTIGRAVITY CLI (AGY) -->
2508
+ <section class="doc-section" id="doc-agy">
2509
+ <div class="doc-section-h">
2510
+ <h2>
2511
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><polygon points="12 2 2 7 12 12 22 7 12 2"/><polyline points="2 17 12 22 22 17"/><polyline points="2 12 12 17 22 12"/></svg>
2512
+ 6. Antigravity CLI (agy) & Relay Proof An Toàn
2513
+ </h2>
2514
+ <span class="badge" style="background:var(--warn-soft); color:var(--warn); font-weight:600;">CLOUD_CODE_URL · Relay Identity Challenge</span>
2515
+ </div>
2516
+ <div class="doc-prose">
2517
+ <p>
2518
+ <strong>Antigravity CLI (<code>agy</code>)</strong> là client dòng lệnh của Google Antigravity. agy đọc biến môi trường <code>CLOUD_CODE_URL</code> và gửi các cuộc gọi Code Assist tới địa chỉ đó. Nhờ vậy, agy không cần thiết lập proxy mạng và không cần chứng chỉ TLS.
2519
+ </p>
2520
+
2521
+ <h3>Quy Trình Thiết Lập Cho agy</h3>
2522
+ <ol>
2523
+ <li>Đăng nhập agy bằng tài khoản Google của bạn (agy cần phiên đăng nhập này cho các tác vụ nội bộ).</li>
2524
+ <li>Tạo một profile có <code>"tool": "agy"</code> trong tab Routes (hoặc dùng template có sẵn <strong>agy on intact</strong>).</li>
2525
+ <li>Kích hoạt định tuyến và khởi chạy:
2526
+ <div class="doc-codebox">
2527
+ <div class="doc-codebox-bar">
2528
+ <span>Terminal · agy setup</span>
2529
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'switch shim install\nswitch agy &lt;profile&gt;\nagy')">Copy</button>
2530
+ </div>
2531
+ <pre>switch shim install
2532
+ switch agy &lt;profile&gt;
2533
+ agy</pre>
2534
+ </div>
2535
+ </li>
2536
+ </ol>
2537
+
2538
+ <h3>Phân Luồng Yêu Cầu Của agy</h3>
2539
+ <table class="doc-table">
2540
+ <thead>
2541
+ <tr>
2542
+ <th style="width:30%;">Loại Cuộc Gọi</th>
2543
+ <th style="width:40%;">Đích Đến Xử Lý</th>
2544
+ <th>Ghi Chú Xác Thực & Bảo Mật</th>
2545
+ </tr>
2546
+ </thead>
2547
+ <tbody>
2548
+ <tr>
2549
+ <td>Agent turn (<code>requestType: "agent"</code>)</td>
2550
+ <td>Profile của agy trên Switcher (Bifrost intact hoặc convert sang upstream)</td>
2551
+ <td>Mang API key của profile, không bao giờ mang token Google</td>
2552
+ </tr>
2553
+ <tr>
2554
+ <td>Checkpoint summary, model list, quota, đăng nhập</td>
2555
+ <td>Google Code Assist chính thức (<code>daily-cloudcode-pa.googleapis.com</code>)</td>
2556
+ <td>Giữ nguyên vẹn token và headers Google của bạn</td>
2557
+ </tr>
2558
+ <tr>
2559
+ <td>Mọi cuộc gọi khi agy đang tắt (<code>switch off agy</code>)</td>
2560
+ <td>Google Code Assist chính thức</td>
2561
+ <td>Gọi thẳng trực tiếp</td>
2562
+ </tr>
2563
+ </tbody>
2564
+ </table>
2565
+
2566
+ <h3>Cấu Hình Model Trong Slot <code>main</code> Của agy</h3>
2567
+ <ul>
2568
+ <li><code>*</code>: Tự động chuyển tiếp đúng model mà agy lựa chọn trong giao diện dòng lệnh.</li>
2569
+ <li><code>antigravity/*</code>: Ví dụ <code>antigravity/gemini-3.8-flash-high</code>. intact nhận diện agy là client chính thức của pool Antigravity và cho phép đi xuyên qua <strong>Bifrost</strong> không cần chuyển đổi.</li>
2570
+ <li>Model chỉ định khác (ví dụ: <code>claude/claude-opus-5</code>): Gateway chuyển đổi request Code Assist sang định dạng tương ứng và chuyển câu trả lời về lại định dạng Code Assist.</li>
2571
+ </ul>
2572
+
2573
+ <div class="doc-callout info">
2574
+ <strong>Bảo vệ Token bằng Relay (<code>agy-relay.mjs</code>):</strong> Do agy gửi token xác thực dạng cleartext tới <code>CLOUD_CODE_URL</code>, shim của Switcher khởi chạy agy đằng sau một relay loopback chuyên dụng. Relay này bắt buộc Gateway phải cung cấp bằng chứng mã hóa dựa trên <code>gateway.secret</code> trước khi chuyển token. Nếu gateway chưa chứng minh được danh tính trong 3 giây, agy sẽ fallback về Google endpoint chính thức và token không bao giờ bị phát tán sang tiến trình lạ.
2575
+ </div>
2576
+ </div>
2577
+ </section>
2578
+
2579
+ <!-- 7. TOKEN OPTIMIZERS & HEALER ENGINE -->
2580
+ <section class="doc-section" id="doc-optimizers">
2581
+ <div class="doc-section-h">
2582
+ <h2>
2583
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="m12 14 4-4"/><path d="M3.34 19a10 10 0 1 1 17.32 0"/></svg>
2584
+ 7. Tối Ưu Token & Healer Engine
2585
+ </h2>
2586
+ <span class="badge" style="background:var(--ok-soft); color:var(--ok); font-weight:600;">Headroom · RTK · Ponytail Interoperability</span>
2587
+ </div>
2588
+ <div class="doc-prose">
2589
+ <p>
2590
+ Các công cụ cắt tỉa prompt như <strong>Headroom</strong>, <strong>RTK (Rust Token Killer)</strong> và <strong>Ponytail</strong> cố gắng giảm input tokens bằng cách xóa bớt lịch sử hội thoại hoặc dòng lệnh. Tuy nhiên, hành động này thường <strong>phá vỡ đồ thị tin nhắn</strong>:
2591
+ </p>
2592
+ <ul>
2593
+ <li><strong>Orphaned <code>tool_result</code>:</strong> Optimizer xóa mất lượt <code>assistant</code> chứa <code>tool_use</code> nhưng lại giữ lại lượt <code>user</code> chứa <code>tool_result</code> $\implies$ Anthropic lập tức văng lỗi <code>HTTP 400 (tool_use_id does not correspond to any tool_use)</code>.</li>
2594
+ <li><strong>Consecutive <code>user</code> turns:</strong> Xóa các lượt trung gian khiến 2 lượt <code>user</code> đứng liền nhau $\implies$ Vi phạm quy tắc luân phiên role của Anthropic gây crash <code>HTTP 400</code>.</li>
2595
+ <li><strong>Reasoning Suppression:</strong> Tự ý gọt bỏ tham số <code>thinking</code> hoặc budget để tiết kiệm token $\implies$ Khiến các model tư duy sâu (Opus, Gemini Flash) trở nên nông cạn, trả lời ngắn cũn và lỗi code.</li>
2596
+ <li><strong>Broken Schemas:</strong> Cắt cụt JSON hoặc trả schema rỗng <code>{}</code> làm Gemini/Vertex từ chối xử lý.</li>
2597
+ </ul>
2598
+
2599
+ <h3>Mô Hình Phân Tầng Bắt Buộc</h3>
2600
+ <div class="doc-codebox">
2601
+ <div class="doc-codebox-bar"><span>Kiến trúc tầng bảo vệ</span></div>
2602
+ <pre>[CLI Agent] ──> [Optimizer Proxy: Headroom / RTK] ──> [LLM Switcher :3456] ──> [Internet / Upstream]
2603
+ │
2604
+ ├── 1. Tự chữa lành tool_result mồ côi thành text
2605
+ ├── 2. Tự gộp các lượt cùng role liên tiếp
2606
+ ├── 3. Tự phục hồi tham số suy luận thinking
2607
+ ├── 4. Sửa schema rỗng {} cho Gemini/Vertex
2608
+ └── 5. Giữ nguyên 100% tracing headers (x-rtk-*)</pre>
2609
+ </div>
2610
+
2611
+ <h3>Ma Trận Bảo Vệ Của Healer Engine</h3>
2612
+ <table class="doc-table">
2613
+ <thead>
2614
+ <tr>
2615
+ <th style="width:32%;">Tình Huống Lỗi Do Optimizer Gây Ra</th>
2616
+ <th style="width:30%;">Gọi Trực Tiếp Upstream</th>
2617
+ <th>Đi Qua LLM Switcher (Healer Engine)</th>
2618
+ </tr>
2619
+ </thead>
2620
+ <tbody>
2621
+ <tr>
2622
+ <td>Lượt <code>tool_result</code> bị mồ côi</td>
2623
+ <td>❌ <strong>HTTP 400 Crash</strong></td>
2624
+ <td>✅ <strong>HTTP 200 OK:</strong> Tự chuyển thành text block <code>[Tool Result (id)]</code></td>
2625
+ </tr>
2626
+ <tr>
2627
+ <td>Hai lượt <code>user</code> đứng liền kề</td>
2628
+ <td>❌ <strong>HTTP 400 Crash</strong></td>
2629
+ <td>✅ <strong>HTTP 200 OK:</strong> Tự gộp nội dung thành một lượt hợp lệ</td>
2630
+ </tr>
2631
+ <tr>
2632
+ <td>Tham số <code>thinking</code> bị optimizer cắt</td>
2633
+ <td>⚠️ <strong>Suy luận hời hợt</strong></td>
2634
+ <td>✅ <strong>HTTP 200 OK:</strong> Tự phát hiện reasoning model và bù lại thinking budget</td>
2635
+ </tr>
2636
+ <tr>
2637
+ <td>Schema rỗng <code>{}</code> của Gemini/Vertex</td>
2638
+ <td>❌ <strong>HTTP 400 Schema Error</strong></td>
2639
+ <td>✅ <strong>HTTP 200 OK:</strong> Tự bổ sung type string tương thích</td>
2640
+ </tr>
2641
+ <tr>
2642
+ <td>Lượt <code>tool</code> mồ côi trong Chat API</td>
2643
+ <td>❌ <strong>HTTP 400 Crash</strong></td>
2644
+ <td>✅ <strong>HTTP 200 OK:</strong> Chuyển role tool thành ngữ cảnh user an toàn</td>
2645
+ </tr>
2646
+ <tr>
2647
+ <td>Headers tracing (<code>x-rtk-*</code>, <code>traceparent</code>)</td>
2648
+ <td>⚠️ Cảnh báo dropped header</td>
2649
+ <td>✅ <strong>HTTP 200 OK:</strong> Passthrough nguyên vẹn 100%</td>
2650
+ </tr>
2651
+ </tbody>
2652
+ </table>
2653
+
2654
+ <h3>Cấu Hình Chuỗi Cho Các Optimizer</h3>
2655
+ <p>
2656
+ <strong>Headroom (trong headroom.yaml hoặc biến môi trường):</strong>
2657
+ </p>
2658
+ <div class="doc-codebox">
2659
+ <div class="doc-codebox-bar"><span>headroom config</span></div>
2660
+ <pre>ANTHROPIC_BASE_URL="http://127.0.0.1:3456"
2661
+ OPENAI_BASE_URL="http://127.0.0.1:3456/v1"</pre>
2662
+ </div>
2663
+ <p>
2664
+ <strong>RTK (Rust Token Killer):</strong>
2665
+ </p>
2666
+ <div class="doc-codebox">
2667
+ <div class="doc-codebox-bar"><span>rtk config</span></div>
2668
+ <pre>export UPSTREAM_URL="http://127.0.0.1:3456"</pre>
2669
+ </div>
2670
+ </div>
2671
+ </section>
2672
+
2673
+ <!-- 8. AGENT SKILL & MCP SERVER -->
2674
+ <section class="doc-section" id="doc-agent-mcp">
2675
+ <div class="doc-section-h">
2676
+ <h2>
2677
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M12 2a10 10 0 1 0 10 10H12V2z"/><path d="M12 12 2.1 10.5"/></svg>
2678
+ 8. Tích Hợp Agent Skill & MCP Server
2679
+ </h2>
2680
+ <span class="badge" style="background:var(--accent-soft); color:var(--accent); font-weight:600;">Control-Plane Security & Automation</span>
2681
+ </div>
2682
+ <div class="doc-prose">
2683
+ <p>
2684
+ Để đảm bảo các AI Agent tự hành (Claude Code, Cursor, Opencode, Windsurf) không bao giờ tự ý sửa đổi file cấu hình hệ thống hoặc bypass Gateway, LLM Switcher cung cấp 2 tài nguyên chuẩn hóa:
2685
+ </p>
2686
+
2687
+ <h3>1. Agent Skill (<code>skills/llm-switcher/SKILL.md</code>)</h3>
2688
+ <p>
2689
+ Tập luật bắt buộc dạy cho AI Agent:
2690
+ </p>
2691
+ <ul>
2692
+ <li>Mọi request LLM và proxy nén token phải trỏ về <code>http://127.0.0.1:3456</code>.</li>
2693
+ <li>Tuyệt đối không sửa <code>~/.claude/settings.json</code> hay <code>~/.codex/config.toml</code>.</li>
2694
+ <li>Không bao giờ hướng dẫn sub-agent chạy <code>source env.sh</code> (đã có launcher shims tự tiêm).</li>
2695
+ </ul>
2696
+ <div class="doc-codebox">
2697
+ <div class="doc-codebox-bar">
2698
+ <span>Terminal · Cài đặt Skill</span>
2699
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'cp -r skills/llm-switcher ~/.config/opencode/skills/\ncp -r skills/llm-switcher ~/.claude/skills/')">Copy</button>
2700
+ </div>
2701
+ <pre># Cài đặt cho Opencode:
2702
+ cp -r skills/llm-switcher ~/.config/opencode/skills/
2703
+
2704
+ # Cài đặt cho Claude Code:
2705
+ cp -r skills/llm-switcher ~/.claude/skills/</pre>
2706
+ </div>
2707
+
2708
+ <h3>2. MCP Server (<code>mcp.mjs</code>)</h3>
2709
+ <p>
2710
+ Server đạt chuẩn Model Context Protocol hoạt động qua <code>stdio JSON-RPC 2.0</code> (không cần dependencies). Thêm vào cấu hình MCP (ví dụ <code>opencode.jsonc</code> hoặc <code>claude_desktop_config.json</code>):
2711
+ </p>
2712
+ <div class="doc-codebox">
2713
+ <div class="doc-codebox-bar">
2714
+ <span>opencode.jsonc / claude_desktop_config.json</span>
2715
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, '{\n &quot;mcp&quot;: {\n &quot;llm-switcher&quot;: {\n &quot;type&quot;: &quot;local&quot;,\n &quot;command&quot;: [&quot;node&quot;, &quot;/path/to/llm-switcher/mcp.mjs&quot;],\n &quot;enabled&quot;: true\n }\n }\n}')">Copy</button>
2716
+ </div>
2717
+ <pre>{
2718
+ "mcp": {
2719
+ "llm-switcher": {
2720
+ "type": "local",
2721
+ "command": ["node", "/path/to/llm-switcher/mcp.mjs"],
2722
+ "enabled": true
2723
+ }
2724
+ }
2725
+ }</pre>
2726
+ </div>
2727
+
2728
+ <h3>5 Công Cụ MCP Có Sẵn</h3>
2729
+ <table class="doc-table">
2730
+ <thead>
2731
+ <tr>
2732
+ <th style="width:30%;">Tên MCP Tool</th>
2733
+ <th>Mô tả chức năng chi tiết</th>
2734
+ </tr>
2735
+ </thead>
2736
+ <tbody>
2737
+ <tr>
2738
+ <td><code>switcher_status</code></td>
2739
+ <td>Kiểm tra trạng thái cổng Gateway, port Blindfold và profile đang kích hoạt cho từng tool.</td>
2740
+ </tr>
2741
+ <tr>
2742
+ <td><code>switcher_audit</code></td>
2743
+ <td>Quét toàn diện môi trường máy trạm để phát hiện các request gọi thẳng ra ngoài hoặc tool nén token cấu hình sai.</td>
2744
+ </tr>
2745
+ <tr>
2746
+ <td><code>switcher_switch_profile</code></td>
2747
+ <td>Đổi profile linh hoạt theo từng CLI (<code>claude</code>, <code>codex</code>, <code>agy</code>) ngay trong phiên làm việc.</td>
2748
+ </tr>
2749
+ <tr>
2750
+ <td><code>switcher_recent_logs</code></td>
2751
+ <td>Xem danh sách request gần nhất, độ trễ, số token và reasoning block (lưu ý: trả về tối đa 150 ký tự đầu của prompt).</td>
2752
+ </tr>
2753
+ <tr>
2754
+ <td><code>switcher_models</code></td>
2755
+ <td>Lấy danh mục mô hình mới nhất từ Claude Code và Codex, hỗ trợ đồng bộ động từ upstream.</td>
2756
+ </tr>
2757
+ </tbody>
2758
+ </table>
2759
+ </div>
2760
+ </section>
2761
+
2762
+ <!-- 9. ADVANCED FEATURES: COMPACTION & GOAL CHECK -->
2763
+ <section class="doc-section" id="doc-advanced">
2764
+ <div class="doc-section-h">
2765
+ <h2>
2766
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="3"/><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"/></svg>
2767
+ 9. Tính Năng Nâng Cao: Compaction, Goal Check & Contract Lab
2768
+ </h2>
2769
+ <span class="badge" style="background:var(--card2); color:var(--muted);">Pro Features · Cache & Quality Gate</span>
2770
+ </div>
2771
+ <div class="doc-prose">
2772
+ <h3>A. Idle Compaction (Nén Prefix Khi Nhàn Rỗi)</h3>
2773
+ <p>
2774
+ Khi một phiên làm việc dừng lại quá lâu (&gt;15 phút mặc định), prompt cache của các nhà cung cấp LLM (Anthropic: 5 phút, OpenAI: 30 phút, Gemini: 1 giờ) sẽ hết hạn (cold cache). Request tiếp theo sẽ phải thanh toán toàn bộ chi phí token input ban đầu mà không được hưởng giảm giá cache.
2775
+ </p>
2776
+ <p>
2777
+ <strong>Idle Compaction</strong> phát hiện thời gian nhàn rỗi và tự động nén gọn prefix lịch sử cũ, lưu cache xác thực trong <code>STATE_DIR/idle-compact</code> mà không chạm vào file JSONL của Claude hay SQLite của Codex.
2778
+ </p>
2779
+ <div class="doc-codebox">
2780
+ <div class="doc-codebox-bar"><span>Terminal · Quản trị Compaction</span></div>
2781
+ <pre>switch compact on # Kích hoạt nén cho Claude Code
2782
+ switch compact codex on # Kích hoạt nén cho Codex Responses
2783
+ switch compact idle 20 # Đặt ngưỡng nhàn rỗi là 20 phút (1-1440)
2784
+ switch compact min 128 # Ngưỡng kích thước tối thiểu 128 KB
2785
+ switch compact model &lt;model-id&gt; # Chỉ định model tóm tắt chạy ngầm
2786
+ switch compact model none # Tắt tóm tắt model, dùng cắt tỉa tất định
2787
+ switch compact status # Kiểm tra cấu hình hiện tại</pre>
2788
+ </div>
2789
+ <p>
2790
+ <em>Cắt tỉa tất định (Deterministic Mode):</em> Khi không đặt summary model, hệ thống giữ lại text đầu và gần nhất trong ngân sách (<code>userChars: 3000</code>), loại bỏ payload tool cũ nhưng bảo toàn nguyên vẹn các cặp tool call/result mới nhất (<code>keepRecent: 6</code>) mà không tốn bất kỳ token nào để gọi AI tóm tắt.
2791
+ </p>
2792
+
2793
+ <h3>B. Goal Check: Cơ Chế Veto Độc Lập Bằng Jev System One</h3>
2794
+ <p>
2795
+ Trong Claude Code, khi dùng lệnh <code>/goal</code>, mô hình evaluator đọc transcript và có nguy cơ bị đánh lừa: coi việc <em>sửa báo cáo (rút finding)</em> là <em>đã sửa code thật</em> và vội vàng đánh dấu hoàn thành (False Completion).
2796
+ </p>
2797
+ <p>
2798
+ <strong>Goal Check</strong> chặn phản hồi của evaluator trước khi gửi về client, đồng thời gọi mô hình quyết định <strong>Jev (System One)</strong> chấm điểm trên 3 trục độc lập:
2799
+ </p>
2800
+ <ul>
2801
+ <li><code>complete</code> $\ge 0.90$: Công việc theo condition đã hoàn thành thực sự.</li>
2802
+ <li><code>evidence</code> $\ge 0.85$: Có tool-result trực tiếp chứng minh kết quả thực thi (chạy test, compile...).</li>
2803
+ <li><code>unfinished</code> $\le 0.10$: Không còn công việc dang dở hoặc lỗi chưa xử lý.</li>
2804
+ </ul>
2805
+ <div class="doc-callout success">
2806
+ <strong>Nguyên tắc Fail-Closed:</strong> Gateway chỉ trả về <code>ok: true</code> khi cả evaluator gốc lẫn Jev đồng thuận. Bất kỳ lỗi mạng, timeout hoặc thiếu evidence đều bị Veto (<code>ok: false</code>) để bảo vệ chất lượng công việc. Cấu hình tại tab <strong>Goal Check</strong> trên thanh điều hướng.
2807
+ </div>
2808
+
2809
+ <h3>C. Contract Lab: Tự Hoàn Thiện & Chống Rơi Rớt Dữ Liệu Với Intact</h3>
2810
+ <p>
2811
+ Bộ đôi <strong>LLM Switcher</strong> và <strong>intact</strong> vận hành 2 vòng lặp tự sửa lỗi khép kín:
2812
+ </p>
2813
+ <ul>
2814
+ <li><strong>intact sửa lỗi nhà cung cấp từ chối:</strong> Thu thập lỗi 429 giả mạo, replay và bóc tách câu lệnh cấm (như fingerprint Codex/Claude) thành bộ lọc tự động phân phối cho mọi máy trạm.</li>
2815
+ <li><strong>Switcher sửa lỗi rơi rớt field qua chuyển đổi:</strong> Khi bật Contract Lab, Gateway gửi mẫu exchange được che giấu dữ liệu (masking: mọi text nhạy cảm đều biến thành chuỗi ký tự <code>x</code> cùng độ dài, chỉ giữ lại cấu trúc và enum) lên intact. intact so sánh và phát hiện field bị rơi rụng.</li>
2816
+ </ul>
2817
+ <div class="doc-codebox">
2818
+ <div class="doc-codebox-bar"><span>Terminal · Lệnh Contract Lab</span></div>
2819
+ <pre>switch contract-probe # Gửi 6 test probe qua gateway lên intact
2820
+ switch contract-check # Lấy các trường bị mất và tạo file unit test tự động</pre>
2821
+ </div>
2822
+ </div>
2823
+ </section>
2824
+
2825
+ <!-- 10. CONFIG.JSON REFERENCE -->
2826
+ <section class="doc-section" id="doc-config">
2827
+ <div class="doc-section-h">
2828
+ <h2>
2829
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/></svg>
2830
+ 10. Cấu Trúc Cấu Hình (config.json) & Biến Môi Trường
2831
+ </h2>
2832
+ <span class="badge" style="background:var(--code); color:#042f2e; font-weight:600;">JSON Schema & Environment Reference</span>
2833
+ </div>
2834
+ <div class="doc-prose">
2835
+ <p>
2836
+ File <code>config.json</code> nằm trong thư mục dữ liệu (<code>~/.llm-switcher/config.json</code> cho npm, hoặc thư mục gốc repo cho git clone). Dưới đây là schema mẫu đầy đủ tất cả các trường:
2837
+ </p>
2838
+ <div class="doc-codebox">
2839
+ <div class="doc-codebox-bar">
2840
+ <span>config.json</span>
2841
+ <button type="button" class="btn btn-outline btn-sm" onclick="copyDocCode(this, document.getElementById('code-config-sample').textContent)">Copy</button>
2842
+ </div>
2843
+ <pre id="code-config-sample">{
2844
+ "port": 3456, // Cổng chính của LLM Switcher Edge Gateway
2845
+ "blindfold": { "port": 3457 }, // Cổng của Blindfold TLS Interceptor proxy
2846
+ "activeProfiles": {
2847
+ "claude": "intact-claude", // Profile kích hoạt cho Claude Code (/v1/messages)
2848
+ "codex": "intact-codex", // Profile kích hoạt cho Codex (/v1/responses)
2849
+ "agy": "agy-intact" // Profile kích hoạt cho Antigravity CLI
2850
+ },
2851
+ "profiles": {
2852
+ "intact-claude": {
2853
+ "name": "Claude via intact",
2854
+ "tool": "claude", // claude | codex | agy | null
2855
+ "mode": "convert", // convert | hybrid | direct
2856
+ "outFormat": "openai-chat", // openai-chat | anthropic | vertex
2857
+ "baseURL": "https://intact.example.com/v1",
2858
+ "apiKey": "sk-...",
2859
+ "thinkingMode": "auto", // auto | native | off
2860
+ "thinkingEffort": "auto", // auto | none | low | medium | high | max | ultra
2861
+ "defaultModels": {
2862
+ "opus": "ag/claude-opus-4-6-thinking",
2863
+ "sonnet": "ag/gemini-3.7-flash",
2864
+ "haiku": "ag/gemini-3.6-flash-medium",
2865
+ "fable": "ag/gemini-3.8-flash"
2866
+ }
2867
+ },
2868
+ "intact-codex": {
2869
+ "name": "Codex via intact",
2870
+ "tool": "codex",
2871
+ "mode": "convert",
2872
+ "outFormat": "openai-chat",
2873
+ "baseURL": "https://intact.example.com/v1",
2874
+ "apiKey": "sk-...",
2875
+ "publicModels": ["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"],
2876
+ "codexRoles": { "review": "gpt-5.6-sol" },
2877
+ "defaultModels": {
2878
+ "main": "gemini-3.8-flash",
2879
+ "review": "gemini-3.7-flash-medium",
2880
+ "subagent": "gemini-3.6-flash-low"
2881
+ }
2882
+ },
2883
+ "agy-intact": {
2884
+ "name": "agy on intact",
2885
+ "tool": "agy",
2886
+ "mode": "convert",
2887
+ "outFormat": "openai-chat",
2888
+ "baseURL": "https://intact.example.com/v1",
2889
+ "apiKey": "sk-...",
2890
+ "defaultModels": { "main": "antigravity/*" } // * giữ nguyên model của agy (Bifrost)
2891
+ }
2892
+ },
2893
+ "debug": false,
2894
+ "idleCompact": {
2895
+ "enabled": false,
2896
+ "codex": false,
2897
+ "idleMinutes": 15,
2898
+ "minBytes": 65536,
2899
+ "model": ""
2900
+ },
2901
+ "goalCheck": {
2902
+ "enabled": false,
2903
+ "backend": "jev",
2904
+ "baseURL": "https://intact.example.com/v1",
2905
+ "apiKey": "sk-...",
2906
+ "model": "typesafe/jev-latest"
2907
+ },
2908
+ "contractLab": {
2909
+ "url": "https://intact.example.com",
2910
+ "apiKey": "sk-...",
2911
+ "enabled": false
2912
+ }
2913
+ }</pre>
2914
+ </div>
2915
+
2916
+ <h3>Giải Thích Các Tham Số Cấu Hình Quan Trọng</h3>
2917
+ <ul>
2918
+ <li><code>thinkingMode</code>:
2919
+ <ul>
2920
+ <li><code>auto</code> (mặc định cho intact, 9Router): Phục hồi thinking bị cắt, tự thêm chỉ dẫn &lt;think&gt; cho model thường, gửi cả <code>thinking</code> + <code>reasoning_effort</code>.</li>
2921
+ <li><code>native</code> (OpenAI chuẩn): Chỉ gửi <code>reasoning_effort</code> khi client yêu cầu, không sửa prompt, dùng <code>max_completion_tokens</code>.</li>
2922
+ <li><code>off</code>: Tắt hoàn toàn suy luận, không gửi tham số reasoning nào.</li>
2923
+ </ul>
2924
+ </li>
2925
+ <li><code>thinkingEffort</code>: <code>auto</code> (client tự chọn) hoặc ghi đè cứng: <code>none</code>, <code>minimal</code>, <code>low</code>, <code>medium</code>, <code>high</code>, <code>xhigh</code>, <code>max</code>, <code>ultra</code>. Tự động quy đổi sang ngưỡng cao nhất mà provider đích hỗ trợ.</li>
2926
+ </ul>
2927
+
2928
+ <h3>Bảng Tra Cứu Biến Môi Trường (Environment Variables)</h3>
2929
+ <table class="doc-table">
2930
+ <thead>
2931
+ <tr>
2932
+ <th style="width:34%;">Biến Môi Trường</th>
2933
+ <th>Mô tả tác dụng chi tiết</th>
2934
+ </tr>
2935
+ </thead>
2936
+ <tbody>
2937
+ <tr>
2938
+ <td><code>LLM_SWITCHER_HOME</code></td>
2939
+ <td>Đường dẫn thư mục lưu trữ dữ liệu (config, token, shims, logs).</td>
2940
+ </tr>
2941
+ <tr>
2942
+ <td><code>LLM_SWITCHER_CONFIG</code></td>
2943
+ <td>Chỉ định vị trí file <code>config.json</code> bên ngoài thư mục dữ liệu mặc định.</td>
2944
+ </tr>
2945
+ <tr>
2946
+ <td><code>LLM_SWITCHER_PORT</code></td>
2947
+ <td>Ghi đè cổng lắng nghe của Edge Gateway (ưu tiên: cờ <code>--port</code> &gt; env &gt; <code>config.port</code>).</td>
2948
+ </tr>
2949
+ <tr>
2950
+ <td><code>LLM_SWITCHER_BLINDFOLD_PORT</code></td>
2951
+ <td>Ghi đè cổng lắng nghe của Blindfold TLS Interceptor (mặc định: 3457).</td>
2952
+ </tr>
2953
+ <tr>
2954
+ <td><code>LLM_SWITCHER_STATE_DIR</code></td>
2955
+ <td>Tách riêng thư mục runtime launch flags và logs ra khỏi data directory.</td>
2956
+ </tr>
2957
+ <tr>
2958
+ <td><code>LLM_SWITCHER_BLINDFOLD_CERTS</code></td>
2959
+ <td>Đường dẫn tới thư mục chứa chứng chỉ CA (mặc định: <code>blindfold/certs/</code>).</td>
2960
+ </tr>
2961
+ <tr>
2962
+ <td><code>LLM_SWITCHER_CODE_ASSIST_URL</code></td>
2963
+ <td>Địa chỉ máy chủ Google Code Assist nhận cuộc gọi phụ của agy (mặc định: <code>https://daily-cloudcode-pa.googleapis.com</code>).</td>
2964
+ </tr>
2965
+ <tr>
2966
+ <td><code>LLM_SWITCHER_CODE_ASSIST_TIMEOUT_MS</code></td>
2967
+ <td>Thời gian chờ cho các cuộc gọi phụ của agy (mặc định: 300,000 ms = 5 phút).</td>
2968
+ </tr>
2969
+ <tr>
2970
+ <td><code>LLM_SWITCHER_BIFROST_RETRY_MS</code></td>
2971
+ <td>Thời gian chờ trước khi hỏi lại intact sau khi tra cứu Bifrost thất bại (mặc định: 30,000 ms).</td>
2972
+ </tr>
2973
+ <tr>
2974
+ <td><code>CLAUDE_CONFIG_DIR</code></td>
2975
+ <td>Thư mục cấu hình của Claude Code khi tìm <code>settings.json</code> để kiểm tra cảnh báo.</td>
2976
+ </tr>
2977
+ </tbody>
2978
+ </table>
2979
+ </div>
2980
+ </section>
2981
+
2982
+ <!-- 11. TROUBLESHOOTING & FAQ -->
2983
+ <section class="doc-section" id="doc-faq">
2984
+ <div class="doc-section-h">
2985
+ <h2>
2986
+ <svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><circle cx="12" cy="12" r="10"/><path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3"/><line x1="12" y1="17" x2="12.01" y2="17"/></svg>
2987
+ 11. Xử Lý Sự Cố & Câu Hỏi Thường Gặp (FAQ)
2988
+ </h2>
2989
+ <span class="badge" style="background:var(--warn-soft); color:var(--warn); font-weight:600;">Troubleshooting Playbook</span>
2990
+ </div>
2991
+ <div class="doc-prose">
2992
+ <h3>Q1: Lỗi EADDRINUSE: Cổng 3456 hoặc 3457 đã bị tiến trình khác chiếm dụng?</h3>
2993
+ <p>
2994
+ Chạy lệnh <code>switch doctor</code> để phát hiện tiến trình đang giữ cổng. Bạn có thể đổi cổng gateway sang cổng khác bất kỳ bằng lệnh: <code>switch port 8080</code> (gateway sẽ tự động cập nhật và khởi động lại). Nếu cổng Blindfold bị chiếm, đổi <code>blindfold.port</code> trong <code>config.json</code>.
2995
+ </p>
2996
+
2997
+ <h3>Q2: Claude Code hiển thị banner "Custom API endpoint configured"?</h3>
2998
+ <p>
2999
+ Nguyên nhân do trước đây bạn đã cấu hình <code>ANTHROPIC_BASE_URL</code> trực tiếp vào <code>~/.claude/settings.json</code>. Hãy chạy <code>switch doctor</code>: Switcher sẽ phát hiện và dọn sạch các trường thừa này để trả về trạng thái Zero-Mutation trong suốt.
3000
+ </p>
3001
+
3002
+ <h3>Q3: Lệnh <code>claude --resume</code> trong cửa sổ terminal mới không đi qua Gateway?</h3>
3003
+ <p>
3004
+ Điều này xảy ra khi thư mục shims <code>~/.llm-switcher/bin</code> chưa được đặt lên đầu biến <code>PATH</code> của bạn. Hãy kiểm tra bằng lệnh <code>switch shim status</code> và đảm bảo dòng <code>export PATH="$HOME/.llm-switcher/bin:$PATH"</code> có mặt trong <code>~/.bashrc</code> hoặc <code>~/.zshrc</code> và mở một terminal mới.
3005
+ </p>
3006
+
3007
+ <h3>Q4: Claude Code Remote Control (<code>claude rc</code>) có dùng được với Switcher không?</h3>
3008
+ <p>
3009
+ Một phiên Remote Control chạy qua 2 hop:
3010
+ </p>
3011
+ <ul>
3012
+ <li><strong>Session bridge (<code>api.anthropic.com/v1/code/sessions/*</code>):</strong> Chạy trực tiếp qua Anthropic để đồng bộ phiên và kiểm tra gói Max/Pro.</li>
3013
+ <li><strong>Inference:</strong> Các cuộc gọi model của agent mới đi qua LLM Switcher.</li>
3014
+ </ul>
3015
+ <p>
3016
+ <em>Lưu ý:</em> Bạn cần duy trì tài khoản Claude còn quota cho hop bridge; nếu tài khoản hết quota, phiên sẽ bị ngắt ngay tại bridge trước khi request model kịp đến Gateway.
3017
+ </p>
3018
+
3019
+ <h3>Q5: Trên Windows, làm thế nào để tạo chứng chỉ cho Blindfold Mode?</h3>
3020
+ <p>
3021
+ LLM Switcher tự động tạo chứng chỉ bằng Node.js crypto khi bạn kích hoạt tool. Nếu muốn tạo thủ công bằng OpenSSL, hãy chạy lệnh <code>bash blindfold/make-certs.sh</code> trong <strong>Git Bash</strong> (không chạy trong PowerShell vì lệnh <code>New-SelfSignedCertificate</code> thiếu thuộc tính <code>serverAuth</code>).
3022
+ </p>
3023
+
3024
+ <h3>Q6: Antigravity CLI báo lỗi 502 Bad Gateway khi gọi qua relay?</h3>
3025
+ <p>
3026
+ Điều này xảy ra khi Gateway chưa khởi động hoặc phiên bản Gateway cũ hơn relay dẫn đến không vượt qua thử thách <code>gateway.secret</code>. Hãy chạy <code>switch off</code> rồi <code>switch on</code> để khởi động lại Gateway đồng bộ.
3027
+ </p>
3028
+
3029
+ <h3>Q7: Màn hình <code>/model</code> của Codex không hiển thị đúng model mong muốn?</h3>
3030
+ <p>
3031
+ Đảm bảo profile phục vụ Codex có trường <code>publicModels</code> khai báo danh sách tên model chính thức (ví dụ <code>["gpt-5.6-sol", "gpt-5.6-terra", "gpt-5.6-luna"]</code>) tương ứng với các slot <code>main</code>, <code>review</code>, <code>subagent</code> trong <code>defaultModels</code>.
3032
+ </p>
3033
+
3034
+ <h3>Q8: Làm thế nào để gỡ cài đặt sạch sẽ hoặc khôi phục hoàn toàn về mặc định?</h3>
3035
+ <div class="doc-codebox">
3036
+ <div class="doc-codebox-bar"><span>Terminal · Clean Uninstall</span></div>
3037
+ <pre>switch off # Khôi phục endpoint chính hãng
3038
+ switch shim uninstall # Gỡ bỏ launcher shims
3039
+ switch service uninstall # Gỡ bỏ OS background service
3040
+ switch plugin uninstall # Gỡ bỏ các plugin hook thông báo
3041
+ npm uninstall -g llm-switcher # Gỡ bỏ package npm (nếu cài qua npm)</pre>
3042
+ </div>
3043
+ </div>
3044
+ </section>
3045
+ </div>
3046
+ </div>
3047
+ </div>
1539
3048
  </div>
1540
3049
  </div>
1541
3050
 
@@ -1875,7 +3384,7 @@
1875
3384
  function handleRoute() {
1876
3385
  const hash = window.location.hash || '#/routes';
1877
3386
  const route = hash.replace(/^#\/?/, '').split('/')[0] || 'routes';
1878
- const validRoutes = ['routes', 'compact', 'goal-check', 'models', 'logs', 'doctor'];
3387
+ const validRoutes = ['routes', 'logs', 'models', 'doctor', 'goal-check', 'compact', 'docs'];
1879
3388
  const activeRoute = validRoutes.includes(route) ? route : 'routes';
1880
3389
 
1881
3390
  // Update active nav link
@@ -1900,6 +3409,7 @@
1900
3409
  if (activeRoute === 'goal-check') loadGoalCheck();
1901
3410
  if (activeRoute === 'models') renderCatalogView();
1902
3411
  if (activeRoute === 'logs') loadInspectorLogs();
3412
+ if (activeRoute === 'docs') initDocsScrollSpy();
1903
3413
  }
1904
3414
  window.addEventListener('hashchange', handleRoute);
1905
3415
 
@@ -1966,9 +3476,57 @@
1966
3476
  actionsEl.innerHTML = `
1967
3477
  <button class="btn btn-outline" onclick="loadStatus()">Re-check Status</button>
1968
3478
  `;
3479
+ } else if (route === 'docs') {
3480
+ titleEl.textContent = 'Documentation & User Guide';
3481
+ subEl.textContent = 'Tài liệu hướng dẫn cài đặt, cấu hình, lệnh CLI, MCP, Skill và tối ưu hóa hệ thống chi tiết.';
3482
+ actionsEl.innerHTML = `
3483
+ <button class="btn btn-outline btn-sm" onclick="copyDocCode(this, 'npm install -g llm-switcher &amp;&amp; switch on')">Copy Quick Start</button>
3484
+ <button class="btn btn-primary btn-sm" onclick="window.open('https://github.com/louisphamdev/llm-switcher', '_blank')">GitHub Repository</button>
3485
+ `;
1969
3486
  }
1970
3487
  }
1971
3488
 
3489
+ // Docs Helpers
3490
+ function copyDocCode(btn, codeText) {
3491
+ if (!navigator.clipboard) {
3492
+ showToast('Trình duyệt không hỗ trợ sao chép tự động', true);
3493
+ return;
3494
+ }
3495
+ navigator.clipboard.writeText(codeText).then(() => {
3496
+ showToast('Đã sao chép vào bộ nhớ tạm!');
3497
+ if (btn) {
3498
+ const originalText = btn.textContent;
3499
+ btn.textContent = '✓ Copied';
3500
+ setTimeout(() => { btn.textContent = originalText; }, 2000);
3501
+ }
3502
+ }, () => {
3503
+ showToast('Sao chép thất bại', true);
3504
+ });
3505
+ }
3506
+
3507
+ let docsObserver = null;
3508
+ function initDocsScrollSpy() {
3509
+ const tocLinks = document.querySelectorAll('#docsToc .docs-toc-link');
3510
+ if (!tocLinks.length) return;
3511
+ const sections = document.querySelectorAll('#view-docs .doc-section');
3512
+ if (docsObserver) docsObserver.disconnect();
3513
+ docsObserver = new IntersectionObserver((entries) => {
3514
+ entries.forEach(entry => {
3515
+ if (entry.isIntersecting) {
3516
+ const id = entry.target.id;
3517
+ tocLinks.forEach(link => {
3518
+ if (link.getAttribute('href') === '#' + id) {
3519
+ link.classList.add('active');
3520
+ } else {
3521
+ link.classList.remove('active');
3522
+ }
3523
+ });
3524
+ }
3525
+ });
3526
+ }, { rootMargin: '-10% 0px -75% 0px' });
3527
+ sections.forEach(s => docsObserver.observe(s));
3528
+ }
3529
+
1972
3530
  // Modal Tabs
1973
3531
  function switchTab(tabId) {
1974
3532
  document.querySelectorAll('#profile-form .tab-btn').forEach(btn => btn.classList.remove('active'));