بنقرة واحدة
gettokens-domain-engineering
GetTokens 领域工程:账号池、quota、UI 体系、调试与 CLIProxyAPI fork。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
القائمة
GetTokens 领域工程:账号池、quota、UI 体系、调试与 CLIProxyAPI fork。
التثبيت باستخدام Codex أو Claude انسخ هذا Prompt والصقه في Codex أو Claude أو مساعد آخر ليراجع صفحة Skill ويثبّتها لك.
استنادا إلى تصنيف SOC المهني
GetTokens 流程治理:Wails 开发回路、spaces、文档记忆写回与 AGENTS 同步。
GetTokens Codex 账号列表:Codex Channel Routing、账号请求顺序、两模式路由、路由探测、模型映射、OAuth 透传语义、openai-compatible 映射保存与浏览器预览。
GetTokens CLIProxyAPI 上游版本 intake / reference-port 工作流:看新 tag、规划合并、拆子 space、在 gettokens/sidecar 内重实现、验证、重建 sidecar、dev App 验收与双仓提交。
Use when dxyer iOS verification, smoke testing, or debugging needs real API traffic evidence through Proxyman CLI: clear sessions, discover proxy host, export HAR/proxymansession logs, inspect request headers, verify DXY-TOD-SESSION user switching, or distinguish API data mismatch from UI/function failure.
GetTokens 性能治理:用于排查或预防内存占用、WebView/Wails 卡顿、React 渲染放大、大账号池请求放大、sidecar Go 热路径、日志/SQLite/本地文件膨胀、性能回归、perf budget 或 profiling 验收;当用户说性能、内存、卡顿、占用、峰值、泄漏、慢、回归、治理、budget、pprof、profiling,或改动触及账号池、quota、live sessions、session-management、usage attribution、sidecar management API、Wails bridge、日志热路径时使用。
GetTokens 桌面/Wails 前端设计质量统一 skill,合并 taste-skill、Impeccable 与 frontend-system-design 的可复用方法。Use when designing, shaping, auditing, polishing, hardening, or system-designing GetTokens frontend/Wails surfaces: UI/UX flows, Gemini/frontend handoff briefs, visual anti-pattern audits, state/DTO/routing/performance checklists, desktop previews, screenshots, and frontend acceptance criteria. 适用于前端体验优化、视觉重构、复杂页面系统设计、设计稿落地、审计页面、发布前 harden;不用于纯后端/sidecar 修复。
| name | gettokens-domain-engineering |
| description | GetTokens 领域工程:账号池、quota、UI 体系、调试与 CLIProxyAPI fork。 |
This skill unifies the technical rules for building, styling, and debugging GetTokens.
AccountRecord combining auth files (sidecar), API keys (local store), and Codex quota (projected telemetry).Keep provider and credentialSource separate.
Uniqueness is by asset (e.g., auth-file:<name>).
codex api key records must prefer a persisted stable local id over a derived config fingerprint. Editing apiKey / baseUrl / prefix must not change the record id used by frontend selection, modal state, disable state, or rotation ordering.
User-created or copied codex api key records may share the same normalized config identity (apiKey + baseUrl + prefix). Treat each persisted local-id as a separate editable account asset; store files should be keyed by local-id so duplicates do not overwrite each other.
Legacy sidecar mirror data without local-id may still be deduplicated against a stored local record with the same normalized config identity to avoid reintroducing the historical double-card bug.
Account-card copy/import must copy a structured account payload, not just a display name. The payload should round-trip all supported asset classes: auth-file, codex-api-key, and openai-compatible.
Account-card clipboard writes must tolerate browser preview and Wails permission differences. Prefer a shared clipboard helper with DOM copy, Web Clipboard, Wails runtime, and app-local fallback paths instead of calling navigator.clipboard.writeText directly from card components.
Copied account imports create a new editable asset even when the underlying config is identical. When the imported title/provider/file name already exists, append user-facing numeric suffixes from #2 onward; do not use -copy style suffixes. For auth-file imports, preserve .json after the numbered title, such as codex-auth #2.json.
Account import file intake may accept common archives such as .zip, .tar, .tar.gz, .tgz, .json.gz, and .gzip. Use a mature compression library such as fflate for compressed layers; keep any TAR handling limited to archive entry enumeration. Archive handling is only a candidate-expansion step: scan internal .json entries, ignore directories / non-JSON files / __MACOSX, preserve archive-qualified names such as accounts.zip:nested/auth.json, and keep final session/auth normalization in the shared backend upload/import path.
Do not fetch accounts until sidecar is ready.
Reload from Wails after create/delete instead of hand-merging state.
Account inventory reads must not implicitly fan out into all-account quota, usage, and rate-limit reads. Load the safe inventory first, then schedule runtime supplements only for visible, selected, active-route, live-session, or explicitly refreshed accounts.
Automatic account runtime supplement reads must pass through one coordinator keyed by stable account id plus resource. Concurrent overlapping batches may refresh only missing account/resource pairs and must wait for existing in-flight work instead of duplicating it. When no visible/selected target is reported, do not fall back to refreshing the entire small account pool.
Version-like runtime metadata such as rate-limit strategies should use an App-lifetime singleflight loader and a separate failure domain from frequently refreshed per-account statuses. A strategy read failure must not erase an otherwise valid status response.
Account Runtime Authority V2 uses accounts-v2.sqlite as the only GetTokens runtime credential source. Legacy accounts-v1.sqlite, auth-dir/*.json, config API keys, FileTokenStore.List(), codex-api-key, and openai-compatibility are migration inputs only and must never become fallback runtime accounts.
OAuth refresh commit order is strict: upstream refresh result -> credential generation/token revision CAS in accounts-v2.sqlite -> in-memory auth/scheduler/hook update. A CAS conflict must leave memory, route guard, quota/rate-limit and hooks unchanged.
One active account compiles to exactly one runtime auth. Legacy openai-compatible assets containing multiple API key entries must be split into separate account assets during migration; runtime synthesis must not fan one account out into multiple auth IDs.
Every runtime auth enumeration entry point, including generic recursive token filestores, must prune migration-backups directories before reading JSON. Filtering only account-store synthesis is insufficient: a generic filestore can otherwise register backup OAuth files as orphan auths, refresh their consumed tokens, and project the resulting identity-level auth-error onto a current SQLite account with the same provider account_id.
The main account-store SQLite file is sidecar-owned. App/Wails must not directly sql.Open the primary accounts-v2.sqlite for account first-paint, details, diagnostics, or repair decisions. App-side fast reads must go through a sidecar management snapshot endpoint, or through a versioned sidecar-exported read-only projection that is explicitly limited to display data and never participates in runtime state decisions.
SQLite is the account truth source, but hot routing still depends on runtime AuthManager and ModelRegistry indexes. Account-store apply/refresh must rebuild those indexes even in embedded/no-watcher service mode; a card being applied in SQLite is not sufficient evidence that requests can route through it.
Account-store SQLite I/O failures such as SQLITE_IOERR_SHORT_READ / disk I/O error (522) are sidecar runtime failures, not frontend state issues. Fix them inside CLIProxyAPI account-store boundaries: upgrade/verify the SQLite driver, reset/reopen the cached store for recoverable read errors, retry read endpoints at most once, and return structured management errors such as code=account_store_io_error with recoverable=true. Account-card stale banners may summarize this as a user-readable cached-quota warning, but diagnostics surfaces must retain the raw error summary/tooltip for troubleshooting.
Account-store hot refresh must distinguish "read failed" from "valid empty account store". A configured-but-unreadable SQLite store must not be interpreted as an authoritative zero-account snapshot, because delete/update-triggered no-watcher refresh would otherwise prune every existing account-store runtime auth and make Codex relay return auth_unavailable until restart. Preserve existing runtime auths on read failure, surface the structured account-store error, and only prune when ListAccounts succeeds with an actual empty active set.
Account deletion remains soft-delete on the request path: set account_cards.deleted_at_unix_ms first, cancel matching runtime jobs, and reapply the account store once. Physical deletion belongs to a sidecar-owned idle cleanup worker that hard-deletes old soft-deleted account_cards in bounded batches, relies on SQLite ON DELETE CASCADE for credential/runtime rows, uses a retention window so recent mistakes remain recoverable, and must never run inside frontend loops.
Account deletion's immediate runtime effect must be targeted by account_key: after SQLite soft-delete succeeds, remove/disable only matching account-store runtime auths, unregister only their models, prune only their live sessions / websocket pins, and bump the pool epoch. Full account-store reconcile remains as a later consistency check, not the only way for a single delete to take effect and not a reason to mutate unrelated accounts.
Removing an account-store runtime auth must also clear every route-guard source owned by that auth before retaining its disabled tombstone. Healthy credential replacement / OAuth reauthorization must clear transient guards through the same full-auth lookup. Route guard blocks may be owned by auth id or account key and indexed by file name plus provider account identity; clearing only auth.ID is insufficient for quota/runtime blocks that carry only AccountKey. Use one route-guard API that expands the complete auth lookup keys instead of reconstructing identity rules in service code.
codex api key lives in local storage under ~/.config/gettokens-data/codex-api-keys/, not in auth-dir.
AccountsPage is route-only; heavy assembly lives under frontend/src/features/accounts/.
Feature-internal layering for accounts is:
components/hooks/model/tests/Do not re-introduce helpers.ts-style catch-all files. Split by responsibility such as config snippets, selectors, presentation, quota formatting, and actions.
Account list filters must not be collapsed into a single enum once source and availability semantics diverge. For account list and Codex account order filters, use AND-style requirement fields instead of a single availability enum or *Only compatibility fields:
sourcerequiresRequestablerequiresBlockedrequiresDisabledhasBalancehasLongestQuotarequiresErrorKeep disabled and error/unavailable filters separate. A manually disabled account is route-excluded by user intent, while an error/unavailable account needs diagnostic attention.
Account-list status filters and status grouping must consume the same operational auth/quota failure evidence as account cards. 可请求 / requestable means active/configured/routeable and not blocked by terminal quota/runtime evidence such as blocked=true with sources[].source=auth-error or real stale + degradedReason; those accounts belong in 异常 / error instead. 已禁用 remains an explicit user/system disabled bucket, separate from operational error.
Account-card top-level operational state must consume runtime stale/degradedReason and sidecar route-guard source evidence, not only fresh usage/quota success. For OAuth/auth-file quota refresh failures with a real degraded reason, or route runtime blocked=true with sources[].source=auth-error, show the card as status_error_display / danger instead of falling back to "waiting check" or "available" whether the reason is terminal upstream failure (401/402/403, unauthorized, token_expired, invalidated, deactivated_workspace, etc.) or a generic refresh error (management api-call failed, refresh_failed, etc.). This auth/quota failure evidence must be evaluated before runtimeStatus=registered_routeable; being registered only proves the auth existed in runtime, not that the current OAuth credential is still usable. The card visual tint must follow the operational tone as well: danger maps to the critical/danger tint and must not fall back to routeable/positive coloring. Keep only explicit placeholder/unknown reasons such as "Quota runtime status has not been observed yet." in the waiting-check path. Codex account-list requestability remains a separate eligibility chain.
Sidecar management account read models must also project active route guard evidence. GET /v0/management/accounts and GET /v0/management/accounts/:account_key should overlay active AccountRouteGuardStore blocks as display/runtime evidence such as runtime_routeability_status=degraded, runtime_failure_class=<source>, and runtime_routeability_reason=<reason>, while leaving SQLite account_runtime_apply_state unchanged. Do not persist a transient auth-error, quota-empty, rate-limit, or upstream block by rewriting account-store routeability from a read path.
Persist account-list filter preferences separately from ephemeral UI state. Persist filters; do not persist search drafts, modal open state, or bulk-selection state unless a later requirement explicitly needs that.
Account plan is runtime data, not a fixed frontend enum. Account list grouping, badges, and plan filters must aggregate plan keys from AccountRecord.planType and quota planType; new values such as team, enterprise, billing, or provider-specific plan keys must form their own groups/options instead of falling into unknown merely because the frontend has not hard-coded them. Old all-selected plan filter state should not hide newly discovered plan keys.
Browser preview fixtures for account-list filters must include representative samples for every runtime-derived facet being shipped. If a rollout adds dynamic request-status filters such as HTTP 401/402/... or splits resource filters into quota/balance/usage facets, update frontend/src/features/accounts/previewData.ts so preview mode exposes at least one matching account per new facet; otherwise headless browser acceptance can prove only layout, not that the new filter group actually renders or narrows results.
When Wails dev App cards and external browser cards appear inconsistent, classify the evidence before changing card logic: first confirm whether the page has Wails bindings and which sidecar/profile/port it is reading, then compare per-window UI view-state and localStorage cache, then inspect the same account id's React runtime state versus cached quota state. Do not unify WebView/Chrome localStorage or mutate account-card semantics until same-account backend truth is proven inconsistent. A DEV-only diagnostics surface is the preferred first slice for this class of issue.
Vendor preset cURL templates that depend on platform cookies should use a neutral placeholder such as {{platformCookie}}. If platformCookie is stored for an account, keep it scoped to management quota/billing curl execution and never use it as runtime auth, route guard identity, or upstream model/chat credential.
Management-only credentials such as modelFetchApiKey/modelFetchBaseUrl must stay out of runtime account synthesis, route guard, usage attribution, and APIKeyEntriesJSON; they are only for management actions such as provider model-list fetching.
Relay/Codex model catalog refreshes must use modelFetchApiKey/modelFetchBaseUrl for OpenAI-compatible /models fetching when present, with per-field fallback to runtime apiKey/baseUrl only when the management credential field is empty. Token-plan runtime credentials such as tp-* are not valid evidence that /models cannot be populated.
Account-list bulk selection actions should render as one sticky workbench toolbar, not as a nested card or dashed sub-section. High-frequency actions can stay inline; secondary actions should collapse into a menu only when measured available width is insufficient, using a small pure layout predicate plus browser scroll checks that prove no top blank band leaks account cards through the sticky area.
When simplifying an account detail surface, reduce repeated information before shrinking individual controls. Prefer these orderings:
QuotaBars, compact stat strips, and embedded-label inputs instead of inventing detail-only variantsFor account detail surfaces, keep top-of-page runtime summaries limited to live operational signals. Quota, balance, raw auth content, and export/route controls should live in their own sections rather than repeating in the runtime strip.
Account detail mutations that change visible account fields must update the local account-list cache by stable account.id immediately after the backend save succeeds, then use ListAccounts only as a final refresh. Do not update only selectedAccount or rely on reload as the sole display source; if ListAccounts fails or is swallowed, reopening detail from a stale card must still show the saved values.
Detail modals backed by frame hash state must update their local detail state synchronously when opening or closing before relying on hashchange. If close only clears selectedAccount while the old detail value is still in local state, the hydration effect can reopen the modal and make the close button require two clicks.
frontend/src/pages/* should be route wrappers, not long-lived business implementation files.frontend/src/features/<domain>/.features/<domain>/<Domain>Feature.tsxcomponents / hooks / model / testssnapshot / detail*Feature.tsx into a page controller instead of a second catch-all file.session-management, the stable split target is:
*Feature.tsx -> page controller*View.tsx -> business presentation blocks*Copy.ts -> copy factory*Utils.ts -> pure constants/helpersuse*Snapshot / use*Detail / use*Mutation -> focused async hooksAccountsPage -> features/accounts/AccountsFeature.tsxStatusPage -> features/status/StatusFeature.tsxSettingsPage -> features/settings/SettingsFeature.tsxsession_id + provider + model_family 识别)在同一账号上失败达到固定次数(默认 2)后才释放 lease 切到下一个账号。SetRouteDisabled(false) 必须清空该账号的 stale transient route block(Unavailable / NextRetryAfter / ModelStates / Quota / LastError)并 upsert scheduler,使账号无需重启即可参与候选。migration-backups/** 目录下的旧 auth 文件永不参与 runtime routing 候选,仅在人工恢复时使用。SessionAffinitySelector 扩展:FailureBudget(默认2)、RecordRouteFailure、RecordRouteSuccess、BumpPoolEpochSessionCache 扩展:failureCount(同 session+auth 失败计数)、poolEpoch(账号池版本号)Manager.conductor 的 Execute/ExecuteCount/ExecuteStream 路径均需在成功/失败时调用 recordSessionRouteSuccess/FailureapplyAccountStoreStatusChange:禁用时 InvalidateAuth + BumpPoolEpoch,启用时 SetRouteDisabled(false) 清 stale blockrewriteRouteCandidates:缓存命中但 auth 不在候选池时自动 invalidate 并 fallback,不再卡在 stale bindingsynthesizeAccountStoreAuthFile:过滤 isMigrationBackupAuthFileNameconfig.yaml):session_failure_budget: 2, session_failure_window_seconds: 300api-keys, not upstream provider assets such as codex-api-key.api-key assets as the Status page relay key source.api-keys through Wails + management API.localhost, hostname, and LAN IP forms when available.127.0.0.1.provider / model / reasoning effort are local Codex defaults for future sessions[model_providers.*] read from ~/.codex/config.toml~/.codex/models_cache.json when the aggregated result is emptyconfig.toml uses minimal text patch updates for owned keysauth.json uses field-level merge应用到 Codex / 应用到 Claude Code buttons in a footer. The module body must show real generated config data, such as the CPA type: "codex" JSON preview, not explanatory placeholder cards.frontend/src/features/accounts/model/, not inside AccountCard.AccountRecord.apiKey plus the matched source format base URL), not the GetTokens relay key or relay endpoint. Missing relay keys must not disable this path.formatBaseUrls.anthropic, and ANTHROPIC_AUTH_TOKEN according to their official Claude Code guides. Do not replace that URL with the local GetTokens relay endpoint. Zhipu and MiniMax should also write the official API_TIMEOUT_MS=3000000.ApplyRelayServiceConfigToLocalV2, mark it so relay key metadata is not updated; account-pool secrets must not be recorded as relay service key last-used metadata.model_provider; avoid forcing a stable provider id such as gettokens because existing Codex sessions may depend on the current provider.auth_mode=apikey plus OPENAI_API_KEY. Clear OAuth tokens, flat token fields, refresh metadata, agent identity, and user metadata.auth_mode=chatgpt plus native nested tokens. If the source auth-file is sidecar-normalized into flat access_token / id_token / refresh_token fields, convert it back to Codex native nested tokens before writing.auth_mode wins; only when it is missing should OPENAI_API_KEY cause API key fallback.env_key or experimental_bearer_token values that would make Codex choose API key / relay token auth instead of ChatGPT OAuth.env keys such as ANTHROPIC_API_KEY, ANTHROPIC_BASE_URL, and model family fields.localStorage is only a secondary remount/preview cache. ListAccounts() must still overwrite the snapshot after sidecar readiness. First-paint snapshots must exclude credentials and raw auth material such as apiKey/apiKeys, headers, cookies, cURL scripts, modelFetchApiKey, and rawAuthFile./accounts may degrade to a card-only account-store snapshot when credential attachment fails, so the UI can leave skeleton and show local account assets. Runtime account synthesis must remain strict and must not route requests with card-only or missing credential records.remainingPercent as remaining quota. Provider-specific payloads that report usage percent (for example Xiaomi MiMo token plan) must be normalized to remaining quota before reaching the card UI; do not add provider-specific "used percent" branches in QuotaBars.GetQuotaStatuses(accountKeys) / /v0/management/gettokens/quota-status?account_keys=... when the target keys are known, with GetAllQuotaStatuses retained only as compatibility fallback; they must not loop over account cards and call single-account GetCodexQuotaaccount_keys set can be hundreds or thousands of rows, the frontend/Wails caller must chunk GetQuotaStatuses(accountKeys) into bounded requests before crossing the management API. Do not send the full account pool in one GET query, because WebKit/Wails marshalling, sidecar parsing, and Gin request logging all amplify the payload.account_key and account_keys query values instead of writing raw account identifiers. Keep the parameter name and a bounded count such as [redacted:200]; do not log full acct_* lists from quota-status or account bulk endpoints.AccountsFeature must consume runtimeRefreshing / refreshAccountsRuntime from useAccountsPageStateContext; do not define a page-local refreshAccountsRuntime that shadows the hook and calls refreshAccountQuotasBatch(accounts) from the global header actionitems, include stale empty states for missing requested keys, and keep the single account_key response shape compatible for old callersStartCodexQuotasBatchRefreshJob / POST /v0/management/gettokens/quota-refresh-batch/jobs, then poll GetCodexQuotaBatchRefreshJob / GET /v0/management/gettokens/quota-refresh-batch/jobs/:job_id; the old synchronous RefreshCodexQuotasBatch path is compatibility fallback onlyaccount_keys, return immediate job_id, expose pending/running/succeeded/failed plus per-account items/errors, and make restart semantics explicit as in-memory runtime stateUploadAuthFiles, must call a sidecar batch-create endpoint and trigger account-store apply once. Do not loop over hundreds of auth files and call single-account management create unless the new batch route is missing on an old sidecar and the Wails boundary is deliberately using a compatibility fallback.NormalizeAuthFileForSidecar; do not use account_id alone because K12/organization auth files can share it across many real accounts. Persist only hashed identity material and return created/skipped/error summaries from batch-create.wouldCreate/skipped/failed, but they must not reimplement DB duplicate decisions from file name, email, or account_id. Preview endpoints are read-only and must not trigger account-store apply; old sidecars should return a supported=false style fallback so actual upload can still proceed.refreshSupplementalData: false. Do not make import completion wait for quota, usage, or rate-limit supplemental runtime sync; those should run through explicit "refresh runtime" actions or background snapshot sync.readUploadFiles. AntD Upload.beforeUpload fires per file, so aggregate files through a pending queue/microtask instead of parsing and appending queue state once per selected file.filter/reduce/every scans over hundreds of queued items on every render.DeleteAccountsBatch / POST /v0/management/accounts/batch-delete with unified acct_* account ids, not a frontend loop over executeDeleteAccount404 on the new endpoint should downgrade at the management boundary instead of surfacing a hard UI failureSetAccountsDisabledBatch and prefer a sidecar batch status route. A single-account PATCH loop is allowed only as a Wails/root compatibility fallback when an older sidecar returns 404 for the batch route; the frontend must still issue one batch call.data-account-card DOM node per accountAccountGroupSectionView / accountListLayout must preserve virtual window behavior across search, grouping, selection, detail hash restoration, and internal scroll#account-group-body-* / [data-plan-group-grid] limited to visible cards, put top/bottom virtual spacers in an outer wrapper that owns data-account-group-virtualized and render-window metadata, and mark spacers pointer-events: none plus user-select: none so browser comments, hit testing, and copy selection do not target the hidden scroll range as if cards failed to load.group.accounts plus selection statedocs-linhay/scripts/accounts-scale-browser-check.mjs or an equivalent headless scale check that records total accounts, rendered card count, scrolled virtual window, spacer-to-measured-row ratio, internal scroll position, and screenshots under the matching spaceCodex Binary: For Codex CLI binary version/source management, use the dedicated gettokens-codex-binary-management skill. Keep it as an independent binary-management business; do not merge it into account pool, local apply, usage, session, or routing flows.
Codex Account List / Channel Routing: For Codex account request order, route probing, OAuth/auth-file model aliasing, openai-compatible model mappings, channel route mode, and #frame=codex&workspace=account-list, use the dedicated gettokens-codex-account-list skill. In the Account Routing Engine rollout, Codex account list is the Codex Channel Routing workspace: channel order, route mode, channel group state, dry-run/explain, and probe are Codex-owned channel config, not global account inventory priority. Current channel route mode is two-mode only: sequential / balanced; project, projectBindings, projectModeFallbackRouteMode, fallbackMode, and upstream compat route modes such as weighted / canary are removed from Channel Routing save/execute/UI paths.
Project Account Candidate Pool Rule: A project-fixed-account requirement is an account candidate pool rule, not a project route mode, project scope model, or projectBindings revival. Model it as projectKey + channel + allowAccountIDs + enabled, put stable ProjectKey / ProjectName / ProjectKeySource / ProjectKeyConfidence on sidecar RouteContext, compile enabled rules into CompiledRouteSnapshot, execute them in PolicyStagePoolScope with strict allow semantics, and emit DecisionTrace / explain steps from the route engine. Project matching is exact match only: projectName is display/audit only, projectKey must be source-prefixed, current Codex first-pass key is workspace:<sha256(normalized_abs_path)>, no key or ambiguous multi-workspace means not evaluated and no fail-closed, and duplicate enabled matches fail closed as rule conflict. Existing P2 RequestPolicy, P3 StickyPolicy, and sequential / balanced selection must operate only inside the narrowed pool. If the allow set contains no routeable accounts after hard filters, fail closed instead of falling back to the channel pool. Rule changes must bump route snapshot / pool epoch so sticky is re-evaluated on the next request. Writable projectBindings paths must remain deleted; historical input may only be dropped or migrated.
Channel Routing Source of Truth: For Codex / Claude runtime routing, ~/.config/gettokens-data/channel-routing/config.json is the main decision source. Treat sidecar routing.strategy in ~/.config/gettokens/config.yaml as legacy relay/config compatibility only; it must not drive Codex / Claude candidate ordering once channel routing is configured. The CLIProxyAPI fork should install GetTokens channel routing as a pool-scope route policy before legacy selectors, and balanced mode should read active-session counts from the live-session tracker instead of a display snapshot.
Channel Routing Runtime State Persistence: Account-list probe / refresh state and real request routing must share the same sidecar-owned runtime state, not split between Wails explain state and in-memory route guard state. Persist transient abnormal account sources such as auth-error, quota-empty, rate-limit, cooldown, model-unavailable, and upstream-error in the profile-local channel-routing/config.json.runtimeStates, make account-route-guard consume those states on the real request hard-filter path, and write route guard updates back to the same file when they can be mapped to a stable account identity such as acct_*.
manual-disabled is not a persisted runtime-state fact. User disable/enable truth comes from the account-store SQLite / management account status and synthesized runtime auth. Wails explain/probe and sidecar route guard must ignore legacy persisted runtimeStates.*.sources.manual-disabled; any channel-routing save should prune it while preserving other sources on the same account.auth-error or a legacy manual-disabled entry for one account must not delete another active source such as rate-limit on the same account.channels, events, nextEventID, and runtimeStates survive route guard persistence.acct_* account keys over transient auth-id:<id> when available, so restart recovery and account-list state can address the same asset.runtimeStates back into AccountRouteGuardStore before management quota/status reads. The real request hard-filter path and /v0/management/gettokens/quota-status?account_keys=... must report the same active blocked/sources facts after restart; do not make account cards depend only on in-memory route guard state while routing separately reads persisted blocks.Manager.refreshAuth() records an unauthorized / invalid_refresh_token / refresh_token_reused / refresh_token_invalidated / relogin-required failure on an auth, it must notify auth update hooks, and GetTokens' route guard hook must convert that auth state into account-scoped auth-error. Otherwise restart or startup refresh can prove a whole account group is unusable while account cards still show only the few accounts that previously had persisted quota/runtime errors.auth-error route guard lookup must include stable provider account identity, not only the individual GetTokens acct_* key. If multiple account-card assets share the same OpenAI / ChatGPT auth_json.account_id, a terminal credential failure observed on one asset must block sibling assets by an identity lookup key such as openai-account-id:<id> in both real route filtering and management quota-status. Do not fan out hundreds of duplicate persisted runtimeStates just to make cards red; index the guard by provider identity and dedupe repeated sources in quota-status presentation.Codex Extensions: For Codex Skills / MCP Servers, [[skills.config]], tk://github.com / tk://gitlab.com skill sources, and #frame=codex&workspace=skills|mcp-servers, use the dedicated gettokens-codex-extensions-management skill. Keep source-accurate parsing, modal/list UI semantics, and cleanup split rules in that skill instead of expanding this general domain skill.
Claude Code Workspace Parity: When Claude Code adds a capability that corresponds to existing Codex workspace entries, keep the workspace granularity aligned with Codex unless Claude semantics clearly require a different information architecture.
#frame=codex&workspace=skills and #frame=codex&workspace=mcp-servers, Claude should expose separate workspaces such as #frame=claude&workspace=skills and #frame=claude&workspace=mcp-servers, not a single merged page with internal tabs.$CLAUDE_CONFIG_DIR/skills or ~/.claude/skills, plus project .claude/skills) and unified Agent Skills roots (~/.agents/skills, plus project .agents/skills). GetTokens installs project skills under .agents/skills, so scanning only .claude/skills produces a false empty state.ClaudeCodeSkillsWorkspace and ClaudeCodeMcpServersWorkspace, with the page wrapper only dispatching by ClaudeWorkspace.AssetWorkbenchShell, preview data, DTO mappers, and list patterns where they fit, but do not let shared UI collapse distinct navigation surfaces into one tabbed page.extensions may migrate to the safest default (skills), but should not remain a first-class menu item once split pages exist.Usage Desk Local Projection:
CLAUDE_CONFIG_DIR || ~/.claude under projects/**/*.jsonl only. It is read-only: never write, delete, compress, rename, or rewrite Claude native session files.subagents/agent-* sidechain files and parse only assistant envelopes with message.usage; do not return prompt text, tool input, message body, credentials, emails, or raw content.message.id. Keep the final row with non-empty message.stop_reason; if multiple comparable rows exist, keep the one with the larger output_tokens. Skip unfinished rows with empty stop_reason or output_tokens=0.input_tokens + cache_creation_input_tokens, cache_read_input_tokens || cached_input_tokens, and output_tokens.source === 'observed' vs source === 'projected', not by workspace. A workspace-specific override such as workspace === 'claude' inside the observed branch can make a projected button visually selected while still rendering observed data.usage-local:* events must carry and filter provider, so Codex rollout projection and Claude session projection do not overwrite each other's page state.Session Management Local Files:
~/.codex/sessions, ~/.codex/archived_sessions, and ~/.claude/projects as potentially multi-GB local stores. Never make page entry depend on synchronous full-file parsing when a bounded snapshot or stale cache can satisfy the first paint.localStorage. Browser preview may keep a localStorage snapshot cache, but desktop mode should disable that cache and best-effort remove legacy gettokens.sessionManagement.snapshot* keys to avoid WebKit WAL and heap pressure.content through snapshot DTOs.content unless a scoped requirement explicitly needs it.Get*SessionDetail should return only metadata/counts, while a separate message-page API reads JSONL line windows (offset / limit) and stops after the current page plus one lookahead row for hasMore. Frontend detail views should append pages on demand and render only the loaded window.map every session row from the active project into WebKit; render only the visible window plus overscan and preserve scroll height with spacer padding.offset / hasMore cursor but keep only the newest retained message window in React state, so a multi-GB local session store cannot grow WebKit heap without bound.lineNumber, but raw JSON should be fetched through a separate line-read API only after explicit user interaction, never bundled into list/detail/page payloads.App.Codex Live Sessions: For #frame=codex&workspace=live-sessions, treat the feature as runtime observability, not local session-file management. Use this when surfacing in-flight request/session state from CLIProxyAPI.
internal/wailsapp, root main.App, generated frontend/wailsjs, and finally the React feature.account / http|ws, with the session id as a right-aligned secondary action. Keep status, model, timing, request ids, execution ids, and redacted diagnostics in detail panes.已复制 / Copied rather than relying on a title change or silent clipboard write.total, TTFT, first token). Treat secondary gap / stream metrics as secondary-width affordances, not as mandatory first-line content.border + shadow card around the timeline, the filter bar, or the detail root unless a later design system rule explicitly requires that shell.overflow-y-auto, and set overscroll containment so chart/timeline inspection does not roll the page header away.projectName is a display label owned by the CLIProxyAPI live tracker. The sidecar may enrich it from trusted local Codex session metadata (CODEX_HOME || ~/.codex session JSONL) before returning the live snapshot; GetTokens should only pass the optional DTO field through Wails/root bindings/frontend model and fall back to an explicit unknown-project label when absent. Do not add Wails/frontend compatibility lookup for old sidecars unless a later requirement explicitly reintroduces compatibility.QuotaBars and BillingBalance. Add a small adapter from live request quota / billing DTOs into account display shapes instead of copying quota or balance JSX into live sessions.usage.Record carries Latency or TTFT, map those into LiveTimingMetrics (totalDurationMs, firstEventMs, firstTokenMs) and derive streamDurationMs from them; do not rely only on live tracker wall-clock deltas or token detail. A visible request row without positive timing values is expected to produce an empty timing chart, so backend timing propagation is the primary fix point.DELETE /v0/management/gettokens/live-sessions clear only realtime memory state by default. Disk history cleanup requires a separately scoped API with explicit filters/retention semantics; do not reuse memory prune to delete history.limit / offset / window / session_id) and must not rebuild the old unbounded in-memory details list.live, cache, or preview so stale sidecar state is not mistaken for no sessions.generatedAt, preview/cache timestamps, active session duration, and active request streaming duration when the underlying sessions/requests did not change. Reuse unchanged session and request object references so the feed, selection, and detail pane do not flicker during polling.nowMs model options, and keep detail/history state updates for real structural changes.features/codex-live-sessions/model/, merge the active request by requestID, sort points by startedAt, and accept an explicit nowMs option so live requests can be projected in tests.completedAt, project totalDurationMs from nowMs - startedAt with a bounded safety cap. Do not mutate the original request or invent first-event / first-token timings.nowMs - startedAt projection in the timing trend. Historical request rows that still carry streaming / reconnecting from cache or stale sidecar state must keep their recorded timing.totalDurationMs or completedAt - startedAt; otherwise every total-duration point will grow together.sequence is a sidecar-owned lifecycle counter inside one Codex conversation/session. Memory retention may cap the retained request map, but pruning must only delete old requests; it must not renumber retained requests back to 1..50. When a long session keeps only 50 requests, the first retained request may legitimately be #6 and the newest #55.startedAt, then render the visible slice as dense equal-step bars with #sequence as the x-axis label; do not stretch sparse requests across real elapsed gaps.#50 -> #51 -> #52) while the visible data volume remains bounded.total / TTFT / first token / stream / queue / auth select / connect / gaps / rates rather than echoing the latest single request; keep latest-request values in the timeline rows and chart footer.timingSummary when present. Treat timingSummary.window=retained_requests as the authoritative average window, show sampleCount plus sequenceFrom / sequenceTo, and mark the UI as Sidecar summary. Only use frontend request-window averaging as a fallback for old sidecars or preview data without summary, and label that path as a local estimate.timingSummary must not let stale historical streaming/reconnecting requests grow on every snapshot. Only the current active request may project totalDurationMs from the sidecar summary generation time; first-event and first-token fields must remain absent until observed.#frame=codex&workspace=live-sessions exercises curve shape, latest sample footer, and live marker behavior without a Wails runtime. For layout/density bugs, synthesize the edge cardinality that caused the issue, such as 50 retained requests with the latest live request at #50; do not accept a 3-5 request preview as proof for long-session chart fixes.sequence, not from the current visible-window index, so the same request keeps the same timing values across refreshes and rolling-window shifts.typecheck, build, focused model.test.mjs, and at least one browser/DevTools DOM or screenshot check that the chart renders nonblank and has no label overflow, x-axis label overlap, or live-ring clipping.source=live sidecar polls as authoritative. If the next live snapshot omits a session, remove it from the feed instead of retaining stale browser state, because the omission may reflect sidecar-side account pruning or runtime availability filtering. Retention of prior rows is only valid for explicit cache/failure states such as source=cache, not for normal live polling.RuntimeAccountProjection and the live-session tracker must agree: account mutations should prune or suppress the matching tracker rows by authID / accountKey, while the frontend only mirrors the authoritative live snapshot and must not recreate omitted rows locally.Account Route Guard & WebSocket Hot Switch:
previous_response_id, prompt_cache_retention, safety_identifier, stream_options, metadata, or unknown future Responses fields.AccountRouteGuardStore source aggregation for in-memory guard state. Keep sources independent, such as manual-disabled and rate-limit, so automatic recovery never clears a user manual disable inside the current process; do not persist manual-disabled to shared runtimeStates.acct_* account_key; legacy keys such as auth-file:*, codex-api-key:*, and openai-compatible:* are migration/history inputs, not new rate-limit binding keys.rate-limit guard state back over newer decisions.pending / committed reservations. Success commits the reservation until usage attribution releases it by request_id + account_key; failure/cancel releases it, and reconcile expires orphan reservations.tried, otherwise alias/model-pool fallbacks can repeatedly select the same unusable auth.Disabled=true / StatusDisabled. manual-disabled may exist as an in-memory route-guard source for immediate current-process exclusion, but it must not become a persisted runtimeStates source.RoutePolicy deny decisions on the hot path. Do not add Gin middleware that returns 429 in the middle of a request when selector fallback can route to another account.manual-disabled, rate-limit, auth-error, upstream-rate-limit, and upstream-error.remaining <= 0 and a future reset time should write quota-empty into AccountRouteGuardStore; stale/degraded/unknown quota must not create a new hard block.usage_limit_reached is quota exhaustion / cooldown evidence, not an install error or a relogin-required auth failure. Account cards should label it as usage-limit reached, summarize the upstream payload into user-readable quota/reset copy, and suppress reauth CTAs for that state while keeping raw diagnostics available in detail/copy surfaces when needed.blocked=true as "额度已空"; render the empty-quota badge only when sources[].source == quota-empty or the sidecar blockReason explicitly says quota empty/exhausted. manual-disabled, rate-limit, auth errors, and other guard sources need their own labels and must not override fresh positive quota windows.POST /v0/management/gettokens/quota-refresh/:account_key; Wails/root must not infer blocked from local quota bars or bridge API key quota by manually writing quota-status.quota-status upsert are sidecar-owned. Wails/root should only call quota-refresh, quota-test, or billing-test and map the returned QuotaRuntimeState; auth-file usage refresh may still use management /api-call for token injection before writing quota runtime.internal/accounts fallback parser and CLIProxyAPI sidecar management/quota_refresh parser. Use provider fixture payloads, not live secrets, for tests. If an upstream uses fractional ratios such as 0.53 for percent, normalize 0..1 to 0..100; if a balance endpoint returns nested data.balance shapes, map total/granted/topped-up/currency into the unified billing DTO without adding frontend vendor branches.quota-refresh with include_billing=true. Verify the returned windows and billing.balance_infos; do not store user-provided cookies or replay production credentials in committed fixtures or logs.accounts-v1.sqlite once per sidecar handler/store lifecycle, reuse the initialized store, and use WAL plus bounded connection pools before considering larger read/write separation.accounts-v1.sqlite inside per-file, per-auth, or concurrent refresh loops. Share account-store reads through the current SynthesisContext, serialize Watcher.RefreshAuthState, and keep token-store writes on a lifecycle-scoped cached store with a mutex.invalid_refresh_token, refresh_token_reused, app_session_terminated, invalid_grant, token_invalidated, "Could not validate your refresh token", "session has ended", "please try signing in again", "please log in again") are reauth-required states, not transient refresh failures. Do not retry them three times or leave them on the auto-refresh schedule. A later successful refresh or credential update/relogin must reset LastError, Unavailable, NextRefreshAfter, stale model states, and quota/runtime cooldown inherited from the old credential. Do not classify workspace, quota, rate-limit, network, 5xx, or generic refresh_failed states as relogin-required without explicit credential invalidation evidence.quota-status, mark the returned DTO as stale with the refresh error in degradedReason; never make stale cache look like a fresh success on account cards.401 token_invalidated or 402 detail.code=deactivated_workspace, must write stale/degraded quota runtime with the upstream message/code in degradedReason. Parse detail.message/detail.code/detail.type in addition to message/code/error.*; do not hide these cases as a silent cache fallback. Account cards and detail quota sections must surface stale/degraded quota runtime reasons.401 token_invalidated) or OpenAI OAuth token refresh (400 invalid_refresh_token). Sidecar quota/runtime evidence must map terminal credential invalidation to account-scoped auth-error route guard so the OAuth account leaves runtime candidates, and the next fresh successful usage/quota observation or relogin must clear that auth-error. Account cards should show the visible 重新登录 action for active auth-file accounts when quota runtime degradedReason, blockReason, or sources[].reason proves token invalidation; 402 deactivated_workspace and usage_limit_reached remain error/quota displays, not automatic relogin CTAs.quota-empty: use the latest exhausted window reset as ExpiresAt, let active blocks expire naturally, and only successful fresh quota recovery should clear quota-empty before reset. Stale/cache writes must not clear an existing fresh block early.quota-empty by sidecar guard identity lookup, not only by the original block key. This lets an accountKey quota refresh clear an earlier auth-scoped quota-empty for the same account while leaving manual-disabled and rate-limit sources intact.pinnedAuthID and an upstream connection. Add WebSocket-specific session control at request boundaries.source and reason, and write the guard release into the websocket timeline/request trace so rate-limit and manual-disabled releases are distinguishable after the fact.authID or wsURL. When either changes, close the old upstream connection with an explicit reason such as auth_rotated and re-handshake.previous_response_id after failover, and same-session upstream re-handshake when auth changes.Account Budget Guard / Route Engine Mock-First Testing:
RouteRequestContext / request facts.facts.now, runtime Upsert(now), expiry, and revoke checks are ordered deterministically; do not let real wall time decide whether a calibration is future-created, expired, or already revoked.quota-empty priority over local Budget.space before using live services as the primary verifier.docs-linhay/dev/20260603-upstream-downstream-mock-testing.md; feature-specific evidence for the current evaluation lives in docs-linhay/spaces/20260618-route-guard-dsl-evaluation/.Browser Support: New Codex workspace tabs must be usable in a normal browser preview when the interaction is layout/config-flow checkable. Do not let missing window.go.main.App make the page blank; provide explicit preview data and visible preview-only save behavior.
Frame URL Rule: Modal/detail layers opened from Codex workspaces should preserve the frame hash, for example #frame=codex&workspace=<key>&detail=<id>, when the surrounding feature already follows frame/detail routing. Closing a modal should remove only the detail marker.
Wails Binding Boundary: Any Wails-facing Codex method added under internal/wailsapp must also be exposed through cmd/gettokens/app.go, mirrored in cmd/gettokens DTOs/mappers when needed, and regenerated into frontend/wailsjs. Frontend should import from generated bindings only after the main.App method exists.
Raw + Structured Config Editors:
config.toml editor, saving either path must reload or resync the other path before showing success.config.toml pages, do not leave complex TOML tables permanently read-only just because they lack a fine-grained form. Use a raw TOML textarea for complex paths such as features.multi_agent_v2, notice.model_migrations, mcp_servers, skills, projects, profiles, and provider auth/http_headers/query_params.[section] / [[section]] header before saving. The header must match the target path or one of its child paths, for example skills may write [skills...] / [[skills...]], while model_providers.gettokens.auth may write only that provider auth subtree.