@bitkyc08/opencodex 2.6.27-preview.20260705 → 2.6.30-preview.20260707

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.
Files changed (121) hide show
  1. package/README.md +1 -0
  2. package/bin/ocx.mjs +4 -4
  3. package/gui/dist/assets/index-ByGC8-Bm.css +1 -0
  4. package/gui/dist/assets/index-D6YMHYXe.js +15 -0
  5. package/gui/dist/index.html +2 -2
  6. package/package.json +4 -4
  7. package/src/adapters/anthropic-image-guard.ts +195 -0
  8. package/src/adapters/anthropic.ts +85 -14
  9. package/src/adapters/cursor/cursor-errors.ts +2 -2
  10. package/src/adapters/cursor/live-transport.ts +1 -1
  11. package/src/adapters/cursor/transport-retry.ts +2 -2
  12. package/src/adapters/google-errors.ts +1 -1
  13. package/src/adapters/google-http.ts +1 -1
  14. package/src/adapters/google-truncation.ts +1 -1
  15. package/src/adapters/google.ts +1 -1
  16. package/src/adapters/kiro-errors.ts +1 -1
  17. package/src/adapters/kiro-retry.ts +1 -1
  18. package/src/adapters/kiro-truncation.ts +1 -1
  19. package/src/adapters/kiro.ts +1 -1
  20. package/src/adapters/openai-chat.ts +12 -2
  21. package/src/adapters/openai-responses.ts +126 -3
  22. package/src/bridge.ts +164 -7
  23. package/src/{doctor.ts → cli/doctor.ts} +109 -4
  24. package/src/{cli-help.ts → cli/help.ts} +1 -1
  25. package/src/cli/index.ts +584 -0
  26. package/src/{init.ts → cli/init.ts} +6 -6
  27. package/src/{cli-models.ts → cli/models.ts} +2 -2
  28. package/src/{cli-provider.ts → cli/provider.ts} +12 -8
  29. package/src/{star-prompt.ts → cli/star-prompt.ts} +1 -1
  30. package/src/{cli-status.ts → cli/status.ts} +7 -7
  31. package/src/cli.ts +9 -575
  32. package/src/{codex-account-label.ts → codex/account-label.ts} +1 -1
  33. package/src/{codex-account-lifecycle.ts → codex/account-lifecycle.ts} +6 -6
  34. package/src/{codex-account-store.ts → codex/account-store.ts} +2 -2
  35. package/src/{codex-account-usability.ts → codex/account-usability.ts} +4 -4
  36. package/src/{codex-auth-api.ts → codex/auth-api.ts} +18 -18
  37. package/src/{codex-auth-collision.ts → codex/auth-collision.ts} +4 -4
  38. package/src/{codex-auth-context.ts → codex/auth-context.ts} +9 -9
  39. package/src/{codex-catalog.ts → codex/catalog.ts} +49 -20
  40. package/src/codex/history-migration-guardian.ts +102 -0
  41. package/src/{codex-history-provider.ts → codex/history-provider.ts} +111 -7
  42. package/src/{codex-home.ts → codex/home.ts} +69 -14
  43. package/src/{codex-inject.ts → codex/inject.ts} +204 -26
  44. package/src/{codex-journal.ts → codex/journal.ts} +2 -2
  45. package/src/{codex-main-account.ts → codex/main-account.ts} +2 -2
  46. package/src/{model-cache.ts → codex/model-cache.ts} +1 -1
  47. package/src/{codex-paths.ts → codex/paths.ts} +2 -2
  48. package/src/{codex-plugins-doctor.ts → codex/plugins-doctor.ts} +2 -2
  49. package/src/{codex-refresh.ts → codex/refresh.ts} +4 -4
  50. package/src/{codex-routing.ts → codex/routing.ts} +8 -8
  51. package/src/{codex-shim.ts → codex/shim.ts} +80 -15
  52. package/src/{codex-sync.ts → codex/sync.ts} +4 -4
  53. package/src/{codex-websocket-registry.ts → codex/websocket-registry.ts} +1 -1
  54. package/src/config.ts +2 -0
  55. package/src/generated/jawcode-model-metadata.ts +2 -0
  56. package/src/{bun-runtime.ts → lib/bun-runtime.ts} +1 -1
  57. package/src/{crash-guard.ts → lib/crash-guard.ts} +1 -1
  58. package/src/lib/gcp-adc.ts +13 -2
  59. package/src/{process-control.ts → lib/process-control.ts} +1 -1
  60. package/src/{service-secrets.ts → lib/service-secrets.ts} +1 -1
  61. package/src/oauth/anthropic.ts +2 -2
  62. package/src/oauth/callback-server.ts +1 -1
  63. package/src/oauth/google-antigravity.ts +7 -4
  64. package/src/oauth/index.ts +67 -16
  65. package/src/oauth/local-token-detect.ts +36 -5
  66. package/src/oauth/login-cli.ts +2 -2
  67. package/src/oauth/store.ts +236 -20
  68. package/src/oauth/token-guardian.ts +24 -20
  69. package/src/oauth/types.ts +16 -0
  70. package/src/providers/api-keys.ts +121 -0
  71. package/src/{provider-context-cap.ts → providers/context-cap.ts} +1 -1
  72. package/src/providers/derive.ts +2 -0
  73. package/src/providers/key-failover.ts +145 -0
  74. package/src/{provider-label.ts → providers/label.ts} +1 -1
  75. package/src/{provider-quota.ts → providers/quota.ts} +12 -7
  76. package/src/providers/registry.ts +66 -4
  77. package/src/responses/compaction.ts +117 -0
  78. package/src/responses/parser.ts +89 -13
  79. package/src/responses/reasoning-envelope.ts +52 -0
  80. package/src/responses/schema.ts +15 -3
  81. package/src/responses/state.ts +117 -2
  82. package/src/router.ts +2 -0
  83. package/src/server/auth-cors.ts +231 -0
  84. package/src/server/index.ts +523 -0
  85. package/src/server/lifecycle.ts +73 -0
  86. package/src/server/management-api.ts +628 -0
  87. package/src/{proxy-liveness.ts → server/proxy-liveness.ts} +1 -1
  88. package/src/server/relay.ts +534 -0
  89. package/src/server/request-decompress.ts +46 -0
  90. package/src/server/request-log.ts +310 -0
  91. package/src/server/responses.ts +775 -0
  92. package/src/{ws-bridge.ts → server/ws-bridge.ts} +44 -13
  93. package/src/service.ts +23 -11
  94. package/src/types.ts +27 -0
  95. package/src/{update.ts → update/index.ts} +5 -5
  96. package/src/{update-job.ts → update/job.ts} +7 -6
  97. package/src/{update-notify.ts → update/notify.ts} +3 -3
  98. package/src/{usage-debug.ts → usage/debug.ts} +3 -3
  99. package/src/{usage-log.ts → usage/log.ts} +3 -3
  100. package/src/{usage-summary.ts → usage/summary.ts} +3 -3
  101. package/src/{usage-totals.ts → usage/totals.ts} +1 -1
  102. package/src/vision/describe.ts +3 -3
  103. package/src/vision/index.ts +21 -1
  104. package/src/web-search/executor.ts +4 -4
  105. package/src/web-search/index.ts +1 -1
  106. package/src/web-search/loop.ts +84 -24
  107. package/gui/dist/assets/index-BcHhxo1I.css +0 -1
  108. package/gui/dist/assets/index-z8MZ_KPP.js +0 -15
  109. package/src/server.ts +0 -2501
  110. /package/src/{codex-account-runtime-state.ts → codex/account-runtime-state.ts} +0 -0
  111. /package/src/{codex-quota.ts → codex/quota.ts} +0 -0
  112. /package/src/{abort.ts → lib/abort.ts} +0 -0
  113. /package/src/{debug.ts → lib/debug.ts} +0 -0
  114. /package/src/{errors.ts → lib/errors.ts} +0 -0
  115. /package/src/{open-url.ts → lib/open-url.ts} +0 -0
  116. /package/src/{privacy.ts → lib/privacy.ts} +0 -0
  117. /package/src/{redact.ts → lib/redact.ts} +0 -0
  118. /package/src/{sidecar-tracker.ts → lib/sidecar-tracker.ts} +0 -0
  119. /package/src/{upstream-retry.ts → lib/upstream-retry.ts} +0 -0
  120. /package/src/{win-paths.ts → lib/win-paths.ts} +0 -0
  121. /package/src/{ports.ts → server/ports.ts} +0 -0
package/README.md CHANGED
@@ -168,6 +168,7 @@ next Codex session. opencodex keeps two separate behaviors:
168
168
  can auto-pick a lower-usage account from the pool, with quota refresh and non-PII request labels.
169
169
  - **Log in once, skip the API key.** OAuth support for xAI, Anthropic, and Kimi means you can authenticate with your existing account. Tokens auto-refresh. Or forward your `codex login`, paste an API key, or use `${ENV_VAR}` references — your call.
170
170
  - **Works everywhere Codex does.** Injects into Codex CLI, TUI, App, and SDK automatically. Routed models show up in Codex's model picker just like native ones.
171
+ - **History-safe injection.** On local installs the proxy points Codex's own built-in `openai` provider at itself via a single `openai_base_url` line — new threads keep their native provider tag, so ongoing chat history is never remapped and an unclean shutdown can't hide it. (Threads re-tagged by older versions are migrated back once on the first start; remote/LAN binds use a dedicated provider entry instead, since they need an API-key header.)
171
172
  - **Delegate to the right model.** Feature up to five routed or native models in Codex's subagent picker from the dashboard or config — route complex tasks to a reasoning model, fast tasks to a cheap one.
172
173
  - **Give any model superpowers.** Non-OpenAI models get real web search and image understanding via a `gpt-5.4-mini` sidecar over your ChatGPT login.
173
174
  - **See what's happening.** The web dashboard shows providers, OAuth status, model selection, and a live request log — no more guessing why a request failed.
package/bin/ocx.mjs CHANGED
@@ -5,8 +5,8 @@
5
5
  * The package source is TypeScript that runs on the Bun runtime. To let
6
6
  * `npm install -g @bitkyc08/opencodex` work without a separately-installed Bun,
7
7
  * we bundle the runtime via the `bun` npm dependency and exec it from this
8
- * Node shim. (Dev still runs `bun run src/cli.ts` directly via the shebang on
9
- * src/cli.ts — only the published npm `bin` routes through here.)
8
+ * Node shim. (Dev still runs `bun run src/cli/index.ts` directly via the shebang on
9
+ * src/cli/index.ts — only the published npm `bin` routes through here.)
10
10
  */
11
11
  import { spawn, spawnSync } from "node:child_process";
12
12
  import { createRequire } from "node:module";
@@ -18,7 +18,7 @@ import { fileURLToPath } from "node:url";
18
18
  const PKG = "@bitkyc08/opencodex";
19
19
  const require = createRequire(import.meta.url);
20
20
  const here = dirname(fileURLToPath(import.meta.url));
21
- const cliPath = join(here, "..", "src", "cli.ts");
21
+ const cliPath = join(here, "..", "src", "cli", "index.ts");
22
22
 
23
23
  function isNodeModulesInstall() {
24
24
  return here.split(/[\\/]/).includes("node_modules");
@@ -67,7 +67,7 @@ function shouldRepairCodexShim() {
67
67
  }
68
68
 
69
69
  function historyRestoreIncomplete() {
70
- // Mirror src/update.ts historyRestoreIncomplete — a codex-history-backup-*.json surviving
70
+ // Mirror src/update/index.ts historyRestoreIncomplete — a codex-history-backup-*.json surviving
71
71
  // a stop means the native-history restore was skipped (locked state DB).
72
72
  try {
73
73
  return readdirSync(configDir()).some(
@@ -0,0 +1 @@
1
+ :root{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light dark;--bg:var(--lightningcss-light,#f6f7f9)var(--lightningcss-dark,#0c0d11);--rail:var(--lightningcss-light,#fff)var(--lightningcss-dark,#101218);--surface:var(--lightningcss-light,#fff)var(--lightningcss-dark,#15171d);--raised:var(--lightningcss-light,#f1f2f5)var(--lightningcss-dark,#1c1f27);--raised-hover:var(--lightningcss-light,#e8eaee)var(--lightningcss-dark,#242833);--border:var(--lightningcss-light,#e2e4e9)var(--lightningcss-dark,#2a2e39);--border-soft:var(--lightningcss-light,#ededf1)var(--lightningcss-dark,#20242d);--hover:var(--lightningcss-light,#11131c09)var(--lightningcss-dark,#ffffff06);--text:var(--lightningcss-light,#16181d)var(--lightningcss-dark,#edeef2);--muted:var(--lightningcss-light,#5b6270)var(--lightningcss-dark,#a3a9b5);--faint:var(--lightningcss-light,#868d9b)var(--lightningcss-dark,#6b7280);--accent:var(--lightningcss-light,#4f46e5)var(--lightningcss-dark,#6366f1);--accent-hover:var(--lightningcss-light,#4338ca)var(--lightningcss-dark,#818cf8);--accent-ink:#fff;--accent-soft:var(--lightningcss-light,#4f46e51a)var(--lightningcss-dark,#6366f129);--accent-ring:var(--lightningcss-light,#4f46e566)var(--lightningcss-dark,#6366f180);--green:var(--lightningcss-light,#047857)var(--lightningcss-dark,#34d399);--green-soft:var(--lightningcss-light,#0596691a)var(--lightningcss-dark,#34d39921);--red:var(--lightningcss-light,#b91c1c)var(--lightningcss-dark,#f87171);--red-soft:var(--lightningcss-light,#b91c1c17)var(--lightningcss-dark,#f8717121);--amber:var(--lightningcss-light,#b45309)var(--lightningcss-dark,#fbbf24);--amber-soft:var(--lightningcss-light,#b453091a)var(--lightningcss-dark,#fbbf2421);--radius:8px;--radius-sm:5px;--radius-xs:4px;--font:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, "Helvetica Neue", sans-serif;--mono:ui-monospace, "SF Mono", "JetBrains Mono", "Cascadia Code", Menlo, Consolas, monospace;--shadow:0 1px 2px var(--lightningcss-light,#1018280f)var(--lightningcss-dark,#00000080), 0 10px 28px var(--lightningcss-light,#10182812)var(--lightningcss-dark,#0000004d);--shadow-sm:0 1px 2px var(--lightningcss-light,#1018280f)var(--lightningcss-dark,#0006)}@media (prefers-color-scheme:dark){:root{--lightningcss-light: ;--lightningcss-dark:initial}}:root[data-theme=light]{--lightningcss-light:initial;--lightningcss-dark: ;color-scheme:light}:root[data-theme=dark]{--lightningcss-light: ;--lightningcss-dark:initial;color-scheme:dark}*{box-sizing:border-box}html,body,#root{height:100%}body{background:var(--bg);color:var(--text);font-family:var(--font);-webkit-font-smoothing:antialiased;text-rendering:optimizelegibility;margin:0;font-size:14px;line-height:1.5}a{color:var(--accent-hover);text-decoration:none}a:hover{text-decoration:underline}code,.mono{font-family:var(--mono);font-size:.92em}h1,h2,h3,h4{letter-spacing:-.01em;margin:0;font-weight:650}::selection{background:var(--accent-soft)}input[type=checkbox],input[type=radio]{accent-color:var(--accent)}::-webkit-scrollbar{width:10px;height:10px}::-webkit-scrollbar-thumb{background:var(--border);border:2px solid var(--bg);border-radius:99px}::-webkit-scrollbar-thumb:hover{background:var(--faint)}:focus-visible{outline:2px solid var(--accent-ring);outline-offset:2px;border-radius:4px}.app{grid-template-columns:232px 1fr;min-height:100dvh;display:grid}.sidebar{border-right:1px solid var(--border);background:var(--rail);flex-direction:column;align-self:start;gap:4px;height:100dvh;padding:18px 14px;display:flex;position:sticky;top:0}.brand{align-items:center;gap:10px;padding:6px 8px 14px;display:flex}.brand-logo{background:var(--text);flex-shrink:0;width:26px;height:26px;-webkit-mask:url(/logo.png) 50%/contain no-repeat;mask:url(/logo.png) 50%/contain no-repeat}.brand .name{letter-spacing:-.02em;font-size:15px;font-weight:700;line-height:26px}.brand .ver{font-family:var(--mono);color:var(--muted);background:var(--raised);border:1px solid var(--border);border-radius:99px;align-self:center;padding:2px 6px;font-size:10px;line-height:1}.nav-item{border-radius:var(--radius-sm);text-align:left;cursor:pointer;width:100%;color:var(--muted);font:inherit;background:0 0;border:none;align-items:center;gap:10px;padding:8px 10px;font-size:13.5px;font-weight:500;transition:background .12s,color .12s;display:flex}.nav-item:hover{background:var(--raised);color:var(--text)}.nav-item.active{background:var(--accent-soft);color:var(--text)}.nav-item svg{width:17px;height:17px;color:var(--faint);flex-shrink:0}.nav-item.active svg{color:var(--accent)}.sidebar-foot{flex-direction:column;gap:2px;margin-top:auto;padding-top:12px;display:flex}.sidebar-link{color:var(--muted);border-radius:var(--radius-sm);align-items:center;gap:9px;padding:8px 10px;font-size:13px;display:flex}.sidebar-link:hover{background:var(--raised);color:var(--text);text-decoration:none}.sidebar-link svg{width:16px;height:16px}.theme-toggle{text-align:left;cursor:pointer;width:100%;color:var(--muted);font:inherit;border-radius:var(--radius-sm);background:0 0;border:none;align-items:center;gap:9px;padding:8px 10px;font-size:13px;transition:background .12s,color .12s;display:flex}.theme-toggle:hover{background:var(--raised);color:var(--text)}.theme-toggle svg{flex-shrink:0;width:16px;height:16px}.theme-toggle .mode{text-transform:capitalize}.stop-toggle{color:var(--red)}.stop-toggle:hover{background:var(--red-soft);color:var(--red)}.stop-toggle:disabled{opacity:.5;cursor:default}.main{min-width:0}.main-inner{max-width:980px;margin:0 auto;padding:32px 36px 64px}.page-head{justify-content:space-between;align-items:center;gap:16px;margin-bottom:6px;display:flex}.page-head h2{font-size:19px}.page-sub{color:var(--muted);max-width:70ch;margin:4px 0 22px;font-size:13.5px}.page-sub b{color:var(--text);font-weight:600}.api-page h2 svg{vertical-align:-.16em;width:1em;height:1em}.api-page .page-sub code{color:var(--text);background:var(--raised);border:1px solid var(--border-soft);border-radius:var(--radius-xs);padding:1px 4px;font-size:.92em}.btn{border-radius:var(--radius-sm);font:inherit;cursor:pointer;white-space:nowrap;border:1px solid #0000;justify-content:center;align-items:center;gap:7px;padding:7px 14px;font-size:13px;font-weight:550;transition:background .12s,border-color .12s,opacity .12s;display:inline-flex}.btn svg{width:15px;height:15px}.btn:disabled{opacity:.55;cursor:default}.btn-primary{background:var(--accent);color:var(--accent-ink)}.btn-primary:hover:not(:disabled){background:var(--accent-hover)}.btn-ghost{background:var(--raised);color:var(--text);border-color:var(--border)}.btn-ghost:hover:not(:disabled){background:var(--raised-hover)}.btn-danger{color:var(--red);background:0 0;border-color:#f871714d}.btn-danger:hover:not(:disabled){background:var(--red-soft)}.btn-sm{border-radius:var(--radius-xs);padding:4px 9px;font-size:12px}.btn-icon{color:var(--muted);border-radius:var(--radius-xs);padding:5px;transition:background .12s,color .12s}.btn-icon:hover{background:var(--raised-hover);color:var(--text)}.card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);min-width:0}.panel{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:18px}.panel-accent{background:linear-gradient(180deg, var(--accent-soft), transparent 120%), var(--surface);border-color:#7c5cff47}.api-panel{flex-direction:column;gap:10px;display:flex;overflow:hidden}.api-panel .panel-title,.api-panel .muted{margin:0}.api-form-row{align-items:center;gap:8px;min-width:0;display:flex}.api-form-row .input{flex:1;min-width:0}.api-code{min-width:0;color:var(--text);background:var(--raised);border:1px solid var(--border-soft);border-radius:var(--radius-sm);font-family:var(--mono);white-space:pre;margin:0;padding:9px 11px;font-size:12.5px;line-height:1.55;display:block;overflow-x:auto}.api-code-inline{white-space:nowrap}.api-actions{justify-content:flex-end;align-items:center;gap:4px;display:flex}.maintenance-head{flex-wrap:wrap;align-items:flex-start}.maintenance-actions{flex-wrap:wrap;justify-content:flex-end;align-items:center;gap:8px;display:flex}.maintenance-notice{align-items:flex-start;margin:14px 0 0}.update-row{justify-content:space-between;align-items:center;gap:12px;margin-bottom:14px;display:flex}.update-row .field-label{margin:0}.update-empty{margin-bottom:14px;padding:18px}.update-box{flex-direction:column;gap:12px;display:flex}.update-command{flex-wrap:wrap;align-items:center;gap:6px;font-size:12px;display:flex}.update-restart{border-top:1px solid var(--border-soft);padding-top:12px}.stat-row{grid-template-columns:repeat(5,1fr);gap:12px;margin-bottom:28px;display:grid}.stat{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);padding:14px 16px;transition:border-color .12s}.stat:hover{border-color:var(--accent-ring)}.stat .label{color:var(--muted);text-transform:uppercase;letter-spacing:.03em;align-items:center;gap:6px;margin-bottom:6px;font-size:12px;font-weight:600;display:flex}.stat .label svg{width:14px;height:14px}.stat .value{letter-spacing:-.02em;font-size:20px;font-weight:700;line-height:1.15}.stat .value.mono{font-family:var(--mono);font-size:17px}.model-group-head{color:var(--muted);text-transform:uppercase;letter-spacing:.04em;align-items:baseline;gap:8px;margin:0 0 8px;font-size:12px;font-weight:600;display:flex}.model-group-head .count{font-family:var(--mono);text-transform:none;letter-spacing:0;color:var(--faint);font-weight:500}.model-grid{grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:8px;display:grid}.model-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius-sm);padding:10px 12px;transition:border-color .12s,background .12s}.model-card:hover{border-color:var(--accent-ring);background:var(--hover)}.model-card .id{font-family:var(--mono);letter-spacing:-.01em;color:var(--text);font-size:13px;font-weight:600}.badge{font-size:11px;font-weight:600;font-family:var(--mono);letter-spacing:.01em;border:1px solid #0000;border-radius:99px;align-items:center;gap:5px;padding:2px 8px;display:inline-flex}.badge-accent{background:var(--accent-soft);color:var(--accent-hover)}.badge-green{background:var(--green-soft);color:var(--green)}.badge-amber{background:var(--amber-soft);color:var(--amber)}.badge-muted{background:var(--raised);color:var(--muted);border:1px solid var(--border)}.badge-clickable{cursor:pointer;appearance:none;transition:filter .12s}.badge-clickable:hover{filter:brightness(1.1)}.badge-disabled{opacity:.5;cursor:default}.card-badges{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;display:inline-flex}.card-badges .badge{flex-shrink:0}.confirm-icon{background:var(--amber-soft);width:44px;height:44px;color:var(--amber);border-radius:50%;justify-content:center;align-items:center;margin:0 auto 12px;display:flex}.credit-list{flex-direction:column;gap:6px;display:flex}.credit-item{border:1px solid var(--border);border-radius:var(--radius-sm);background:var(--raised);padding:8px 10px;transition:border-color .12s}.credit-next{border-color:var(--amber);background:var(--amber-soft)}.credit-item-head{color:var(--text);align-items:center;gap:6px;margin-bottom:3px;font-size:12px;font-weight:600;display:flex}.credit-item-head svg{color:var(--amber);flex-shrink:0}.credit-item-dates{font-size:11px;font-family:var(--mono);color:var(--muted);justify-content:space-between;display:flex}.credit-urgent{color:var(--red);font-weight:600}.dot{border-radius:50%;flex-shrink:0;width:7px;height:7px}.dot-green{background:var(--green);box-shadow:0 0 0 3px var(--green-soft)}.dot-red{background:var(--red);box-shadow:0 0 0 3px var(--red-soft)}.tbl{border-collapse:collapse;width:100%;font-size:13px}.tbl thead th{text-align:left;color:var(--muted);text-transform:uppercase;letter-spacing:.04em;border-bottom:1px solid var(--border);padding:9px 12px;font-size:11.5px;font-weight:600}.tbl tbody td{border-bottom:1px solid var(--border-soft);padding:10px 12px}.tbl tbody tr:last-child td{border-bottom:none}.tbl tbody tr:hover td{background:var(--hover)}.tbl .num{text-align:right;font-family:var(--mono)}.tbl-wrap{border:1px solid var(--border);border-radius:var(--radius);overflow-x:auto}.input,textarea.input{border-radius:var(--radius-sm);background:var(--raised);border:1px solid var(--border);width:100%;color:var(--text);font:inherit;padding:8px 11px;font-size:13px;transition:border-color .12s}.input::placeholder{color:var(--faint)}.input:focus{border-color:var(--accent);outline:none}textarea.input{resize:vertical;font-family:var(--mono);line-height:1.55}.field-label{color:var(--muted);margin-bottom:5px;font-size:12px;font-weight:500;display:block}select.input{appearance:none}.select-sm{border-radius:var(--radius-sm);background:var(--raised);border:1px solid var(--border);color:var(--text);font:inherit;cursor:pointer;padding:5px 8px;font-size:13px;transition:border-color .12s}.select-sm:focus{border-color:var(--accent);outline:none}.select-sm:disabled{opacity:.5;cursor:default}.switch{cursor:pointer;background:var(--lightningcss-light,#c5c9d2)var(--lightningcss-dark,#3a3f4b);border:none;border-radius:99px;flex-shrink:0;width:34px;height:19px;padding:0;transition:background .15s;position:relative}.switch.on{background:var(--accent)}.switch:disabled{opacity:.6;cursor:default}.switch .knob{background:#fff;border-radius:50%;width:15px;height:15px;transition:left .15s;position:absolute;top:2px;left:2px;box-shadow:0 1px 2px #1018284d}.switch.on .knob{left:17px}.muted{color:var(--muted)}.faint{color:var(--faint)}.row{align-items:center;gap:10px;display:flex}.spread{justify-content:space-between;align-items:center;gap:12px;display:flex}.setting-hint{margin-top:3px;font-size:13px}.stack{flex-direction:column;display:flex}.chip{font-family:var(--mono);background:var(--raised);border:1px solid var(--border);border-radius:var(--radius-xs);color:var(--text);padding:1px 7px;font-size:12px}.empty{text-align:center;border:1px dashed var(--border);border-radius:var(--radius);color:var(--muted);padding:56px 20px}.empty svg{width:30px;height:30px;color:var(--faint);margin-bottom:12px}.empty .title{color:var(--text);margin-bottom:6px;font-weight:600}.notice{border-radius:var(--radius-sm);align-items:center;gap:8px;margin-bottom:14px;padding:9px 12px;font-size:13px;display:flex}.notice svg{flex-shrink:0;width:15px;height:15px}.notice-ok{background:var(--green-soft);color:var(--green)}.notice-err{background:var(--red-soft);color:var(--red)}.h-section{color:var(--text);align-items:center;gap:8px;margin:30px 0 12px;font-size:13px;font-weight:600;display:flex}.h-section .count{color:var(--muted);font-weight:500;font-family:var(--mono);font-size:12px}.spin{border:2px solid var(--border);border-top-color:var(--accent);border-radius:50%;width:14px;height:14px;animation:.7s linear infinite spin;display:inline-block}@keyframes spin{to{transform:rotate(360deg)}}@media (prefers-reduced-motion:reduce){*{transition:none!important;animation:none!important}}.modal-overlay{-webkit-backdrop-filter:blur(2px);backdrop-filter:blur(2px);z-index:50;background:var(--lightningcss-light,#11131c73)var(--lightningcss-dark,#0009);justify-content:center;align-items:flex-start;padding:8vh 16px;display:flex;position:fixed;inset:0}.modal-card{background:var(--surface);border:1px solid var(--border);border-radius:var(--radius);width:100%;max-width:520px;box-shadow:var(--shadow);max-height:84vh;padding:20px;overflow-y:auto}.modal-head{justify-content:space-between;align-items:center;margin-bottom:16px;display:flex}.modal-head h3{font-size:16px}.card-head{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;padding:14px 16px 6px;display:flex}.card-head strong{overflow-wrap:anywhere;min-width:0}.card-sub{color:var(--muted);overflow-wrap:anywhere;min-width:0;padding:0 16px 10px;font-size:12px}.card-active{border-color:var(--accent-ring)}.card-right{color:var(--faint);align-items:center;gap:4px;margin-left:auto;font-size:11px;display:flex}.btn-icon-danger.card-right{appearance:none;color:var(--red);cursor:pointer;background:0 0;border:1px solid #0000;justify-content:center}.btn-icon-danger.card-right:hover{background:var(--red-soft);border-color:var(--red-soft);color:var(--red)}.card-row{justify-content:space-between;align-items:center;padding:14px 16px;display:flex}.badge-primary{background:var(--accent-soft);color:var(--accent-hover)}.dot-blue{background:var(--accent);box-shadow:0 0 0 3px var(--accent-soft)}.dot-muted{background:var(--muted)}.dot-amber{background:var(--amber);box-shadow:0 0 0 3px var(--amber-soft)}.section-sep{align-items:center;gap:10px;margin:20px 0 12px;display:flex}.section-label{color:var(--muted);text-transform:uppercase;letter-spacing:.04em;white-space:nowrap;font-size:12px;font-weight:600}.sep-line{background:var(--border);flex:1;height:1px}.quota-compact{gap:4px;padding:2px 16px 12px;display:grid}.quota-row{grid-template-columns:minmax(34px,max-content) 34px minmax(34px,44px) minmax(38px,42px) minmax(58px,1fr) minmax(30px,max-content);align-items:center;gap:8px;min-width:0;display:grid}.quota-label{color:var(--muted);font-size:11px;font-weight:600}.quota-val{font-size:11px;font-family:var(--mono);color:var(--text);text-align:right}.quota-reset-label{text-overflow:ellipsis;white-space:nowrap;color:var(--faint);font-size:11px;overflow:hidden}.quota-reset-day,.quota-reset-time{color:var(--muted);white-space:nowrap;font-size:11px}.quota-reset-time{font-family:var(--mono)}.bar{background:var(--raised);border-radius:3px;min-width:0;height:5px;overflow:hidden}.bar-fill{border-radius:3px;height:100%;transition:width .3s}.bar-green{background:var(--green)}.bar-amber{background:linear-gradient(90deg, var(--green), var(--amber))}.toggle{background:var(--raised);border:1px solid var(--border);cursor:pointer;border-radius:10px;flex-shrink:0;width:36px;height:20px;transition:background .15s;position:relative}.toggle.on{background:var(--accent);border-color:var(--accent)}.toggle-knob{background:#fff;border-radius:50%;width:16px;height:16px;transition:left .15s;position:absolute;top:1px;left:1px}.toggle.on .toggle-knob{left:17px}.notice-warn{border-radius:var(--radius-sm);background:var(--amber-soft);color:var(--amber);align-items:center;gap:6px;margin-bottom:12px;padding:8px 10px;font-size:12px;display:flex}.notice-warn svg{flex-shrink:0;width:14px;height:14px}.modal-desc{color:var(--muted);margin-bottom:14px;font-size:13px}.modal-actions{gap:8px;margin-top:16px;display:flex}.modal-actions .btn{flex:1}.log-reqid{-webkit-line-clamp:2;word-break:break-all;-webkit-box-orient:vertical;max-width:14ch;display:-webkit-box;overflow:hidden}.log-col-model{overflow-wrap:break-word;word-break:normal;max-width:18ch}.log-col-tokens{min-width:10ch}.log-status-cell{flex-direction:column;align-items:flex-start;gap:2px;display:inline-flex}.log-detail-btn{cursor:pointer;color:var(--accent-hover);font:inherit;white-space:nowrap;background:0 0;border:none;padding:0;font-size:11px;text-decoration:underline}.log-detail-grid{grid-template-columns:max-content minmax(0,1fr);gap:6px 14px;font-size:13px;display:grid}.log-detail-break{word-break:break-all}.log-detail-json{background:var(--raised);border:1px solid var(--border-soft);border-radius:var(--radius-sm);font-family:var(--mono);white-space:pre-wrap;word-break:break-all;max-height:40vh;margin:0;padding:10px 12px;font-size:11.5px;line-height:1.5;overflow:auto}.setup-guide{border:1px solid var(--border);border-radius:var(--radius-sm);margin-bottom:4px;padding:8px 12px;font-size:13px}.setup-guide summary{cursor:pointer;color:var(--accent-hover);font-weight:500}.setup-guide summary:hover{text-decoration:underline}.setup-guide a{color:var(--accent-hover)}.list-row{text-align:left;border-radius:var(--radius-sm);border:1px solid var(--border);background:var(--raised);cursor:pointer;width:100%;color:var(--text);font:inherit;justify-content:space-between;align-items:center;gap:10px;padding:11px 13px;transition:background .12s,border-color .12s;display:flex}.list-row:hover{background:var(--raised-hover);border-color:var(--accent-ring)}.list-row .title{font-size:14px;font-weight:600}.list-row .sub{color:var(--muted);margin-top:2px;font-size:12px}.prov-card{overflow:hidden}.prov-card-main{flex-wrap:wrap;justify-content:space-between;align-items:flex-start;gap:12px;padding:15px 16px;display:flex}.prov-card-disabled{opacity:.62}.prov-card-info{flex:360px;align-items:flex-start;gap:11px;min-width:0;display:flex}.prov-card-copy{flex:1;min-width:0}.prov-title{flex-wrap:wrap;align-items:center;gap:8px;min-width:0;margin-bottom:5px;display:flex}.provider-icon{background:var(--raised);border:1px solid var(--border-soft);width:31px;height:31px;color:var(--text);border-radius:7px;flex:none;justify-content:center;align-items:center;display:inline-flex}.provider-icon img{object-fit:contain;width:19px;height:19px;display:block}.prov-meta{flex-wrap:wrap;align-items:center;gap:5px;min-width:0;display:flex}.prov-meta>span{text-overflow:ellipsis;white-space:nowrap;min-width:0;max-width:100%;overflow:hidden}.provider-quota{padding-left:58px}.provider-actions{flex-shrink:0;align-items:center;gap:8px;margin-left:auto;display:flex}.link-btn{color:var(--accent-hover);font:inherit;cursor:pointer;background:0 0;border:none;padding:6px 2px;font-size:13px;text-decoration:underline}.prov-accounts-toggle{border:none;border-top:1px solid var(--border-soft);width:100%;color:var(--muted);font:inherit;cursor:pointer;background:0 0;justify-content:center;align-items:center;gap:6px;min-height:24px;padding:4px 0;font-size:12px;display:flex}.prov-accounts-toggle:hover{color:var(--text);background:var(--raised)}.prov-accounts-toggle .chev{transition:transform .15s;display:inline-flex}.prov-accounts-toggle .chev svg{width:12px;height:12px;transform:rotate(90deg)}.prov-accounts-toggle.open .chev svg{transform:rotate(-90deg)}.prov-accounts-list{border-top:1px solid var(--border-soft);flex-direction:column;gap:2px;padding:6px 16px 10px;display:flex}.prov-account-row{text-align:left;border-radius:var(--radius-xs);width:100%;color:var(--text);font:inherit;cursor:pointer;background:0 0;border:none;align-items:center;gap:8px;min-height:32px;padding:6px 8px;font-size:13px;display:flex}.prov-account-row:hover{background:var(--raised)}.prov-account-row.active{cursor:default}.prov-account-row .prov-account-email{text-overflow:ellipsis;white-space:nowrap;flex:auto;min-width:0;overflow:hidden}.prov-account-row .badge{flex:none}.prov-account-remove{color:var(--muted);cursor:pointer;border-radius:var(--radius-xs);background:0 0;border:none;flex:none;justify-content:center;align-items:center;padding:4px;display:inline-flex}.prov-account-remove:hover{color:var(--red);background:var(--red-soft)}.prov-account-add{color:var(--muted);font-size:12.5px}.prov-account-add:hover{color:var(--accent-hover)}.prov-account-keyform{cursor:default;gap:6px}.prov-account-keyform:hover{background:0 0}.prov-account-keyform .input-sm{flex:auto;min-width:0;height:28px;padding:4px 8px;font-size:12.5px}.oauth-grid{grid-template-columns:minmax(140px,max-content) minmax(0,1fr) max-content;align-items:center;gap:10px 16px;display:grid}.oauth-row{grid-column:1/-1;grid-template-columns:subgrid;align-items:center;min-height:30px;display:grid}.oauth-name{align-items:center;gap:8px;min-width:0;font-size:13px;font-weight:600;display:inline-flex}.oauth-name-text{text-overflow:ellipsis;white-space:nowrap;overflow:hidden}.provider-icon-sm{border-radius:6px;width:24px;height:24px}.provider-icon-sm img{width:15px;height:15px}.oauth-status{align-items:center;gap:7px;min-width:0;font-size:13px;display:inline-flex}.oauth-email{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.oauth-actions{justify-content:flex-end;align-items:center;gap:8px;min-width:0;display:inline-flex}.oauth-login-hint{flex-wrap:wrap;grid-column:1/-1;align-items:center;gap:8px;font-size:12px;display:inline-flex}@media (width<=760px){.app{grid-template-columns:1fr}.sidebar{z-index:20;border-right:none;border-bottom:1px solid var(--border);background:var(--rail);flex-flow:wrap;align-items:center;gap:0;min-width:0;height:auto;padding:0 10px;position:sticky;top:0}.brand{flex:auto;order:1;width:auto;padding:10px 4px}.sidebar-foot{flex-direction:row;flex:none;order:2;gap:4px;margin:0;padding:0}.sidebar-foot .sidebar-link{display:none}.theme-toggle{justify-content:center;min-width:44px;min-height:44px;padding:8px}.theme-toggle .mode{display:none}.sidebar nav{border-top:1px solid var(--border-soft);flex:100%;order:3;grid-template-columns:repeat(4,minmax(0,1fr));gap:2px;min-width:0;margin:0;padding:4px 0 8px;display:grid}.nav-item{text-align:center;text-overflow:ellipsis;white-space:nowrap;flex-direction:column;justify-content:center;gap:3px;width:auto;min-height:44px;padding:6px 4px;font-size:12px;overflow:hidden}.nav-item svg{width:16px;height:16px}.main-inner{padding:22px 18px 48px}.setting-row{flex-wrap:wrap}.setting-row .setting-copy{flex:100%!important}.setting-row .setting-controls{width:100%}.setting-row .setting-controls .select-sm{flex:1 1 0;min-width:0}.setting-row>.select-sm{width:100%}.api-form-row{flex-direction:column;align-items:stretch}.api-form-row .btn{width:100%;min-height:40px}.stat-row{grid-template-columns:repeat(2,minmax(0,1fr))}.tbl{min-width:460px}.usage-cards{grid-template-columns:repeat(2,minmax(0,1fr))!important}.provider-quota{padding-left:16px}.prov-meta{grid-template-columns:max-content minmax(0,1fr);width:100%;display:grid}.prov-meta .chip~span:not(:last-child){display:none}.oauth-grid{grid-template-columns:minmax(0,auto) minmax(0,1fr) max-content;column-gap:8px}}.usage-cards{grid-template-columns:repeat(3,minmax(0,1fr));gap:12px;margin-top:8px;display:grid}.usage-cards .stat-value{margin-top:4px;font-size:20px;font-weight:600}.usage-range{border:1px solid var(--border);background:var(--surface);border-radius:999px;gap:2px;padding:2px;display:inline-flex}.usage-range-btn{color:var(--muted);cursor:pointer;font:inherit;background:0 0;border:none;border-radius:999px;padding:4px 12px}.usage-range-btn.active{background:var(--raised);color:var(--text);font-weight:600}.panel-title{color:var(--text);margin:0 0 12px;font-size:14px;font-weight:600}.panel-head{justify-content:space-between;align-items:center;gap:12px;margin-bottom:12px;display:flex}.panel-head .panel-title{margin:0}.panel-head .input{max-width:220px}.heatmap{--hm-cell:11px;--hm-gap:3px;flex-direction:column;gap:6px;padding-bottom:4px;display:flex;overflow-x:auto}.heatmap-months{color:var(--muted);width:max-content;margin-bottom:-2px;font-size:11px;display:grid}.heatmap-day-spacer{grid-column:1}.heatmap-month{white-space:nowrap}.heatmap-body{gap:var(--hm-gap);width:max-content;display:flex}.heatmap-days{grid-template-rows:repeat(7, var(--hm-cell));row-gap:var(--hm-gap);color:var(--muted);background:var(--surface);z-index:1;flex-shrink:0;align-items:center;width:25px;font-size:10px;display:grid;position:sticky;left:0}.heatmap-grid{gap:var(--hm-gap);display:grid}.heatmap-week{grid-template-rows:repeat(7, var(--hm-cell));gap:var(--hm-gap);display:grid}.heatmap-cell{width:var(--hm-cell);height:var(--hm-cell);background:var(--border);border-radius:2px}.heatmap-cell-0{background:var(--border)}.heatmap-cell-1{background:color-mix(in oklch, var(--green) 25%, var(--surface))}.heatmap-cell-2{background:color-mix(in oklch, var(--green) 50%, var(--surface))}.heatmap-cell-3{background:color-mix(in oklch, var(--green) 75%, var(--surface))}.heatmap-cell-4{background:var(--green)}.heatmap-legend{align-self:flex-end;align-items:center;gap:4px;font-size:12px;display:inline-flex;position:sticky;right:0}.heatmap-legend .heatmap-cell{width:10px;height:10px}.usage-bar{background:var(--border);border-radius:999px;width:100%;min-width:60px;height:6px;overflow:hidden}.usage-bar-fill{background:var(--green);border-radius:999px;height:100%}.daybars{grid-template-columns:repeat(7,1fr);align-items:end;gap:10px;height:180px;padding-top:8px;display:grid}.daybar{flex-direction:column;justify-content:flex-end;align-items:center;gap:6px;height:100%;display:flex;position:relative}.daybar-track{background:var(--border);border-radius:6px;flex:1;align-items:flex-end;width:100%;max-width:48px;display:flex;overflow:hidden}.daybar-stack{border-radius:6px 6px 0 0;flex-direction:column-reverse;width:100%;min-height:2px;transition:height .2s;display:flex;overflow:hidden}.daybar-seg{width:100%;min-height:1px}.daybar-count{color:var(--text);font-size:12px;font-weight:600}.daybar-label{white-space:nowrap;font-size:11px}.daybar:hover .daybar-track{outline:1px solid var(--border)}.daybar-tip{z-index:5;background:var(--surface);border:1px solid var(--border);pointer-events:none;border-radius:8px;min-width:160px;padding:8px 10px;position:absolute;bottom:calc(100% + 6px);left:50%;transform:translate(-50%);box-shadow:0 6px 20px #00000059}.daybar-tip-date{color:var(--text);white-space:nowrap;margin-bottom:6px;font-size:12px;font-weight:600}.daybar-tip-row{align-items:center;gap:8px;font-size:12px;line-height:1.7;display:flex}.daybar-tip-swatch{border-radius:2px;flex-shrink:0;width:10px;height:10px}.daybar-tip-name{color:var(--text);white-space:nowrap;text-overflow:ellipsis;flex:1;max-width:160px;overflow:hidden}.daybar-tip-val{color:var(--muted);font-variant-numeric:tabular-nums}.usage-scroll{max-height:360px;overflow-y:auto}.usage-scroll thead th{z-index:1;background:var(--surface);position:sticky;top:0}