Commit Graph

1282 Commits

Author SHA1 Message Date
Jason 2df2212ceb fix(usage): reject transient transport failures so retry and keep-last-good work
Usage/quota queries frequently showed spurious "query failed" states that
manual refresh could not clear (#3820). Root cause: all transport-level
failures were folded into Ok(success:false), so react-query's retry never
fired and the failure body poisoned the cache as regular data.

Backend:
- balance/coding_plan/subscription services now return Err for send
  failures and body-read failures (read body via bytes() before
  serde_json::from_slice; reqwest's json() wraps read errors as Decode,
  making error-kind checks on it dead code). Auth/4xx/parse errors stay
  Ok(success:false) and surface immediately.
- Script path maps transient AppError keys (request_failed /
  read_response_failed) to Err; Volcengine adds a Transient call variant.
- Command layer skips snapshot persistence, usage-cache-updated emit and
  tray refresh on Err so the cache bridge cannot overwrite retained data.
- Expired-credential retry propagates transient errors instead of
  rewriting them as "OAuth token has expired".

Frontend:
- resolveDisplayUsage generalized to subscription quotas; 5th param is now
  an options object with a `rejected` flag: stale success data retained by
  react-query across rejections is re-anchored to dataUpdatedAt and expires
  through the same 10-minute keep-last-good window instead of being shown
  indefinitely (a total outage used to mask longer than a single 5xx).
- useSubscriptionQuota / useCodexOauthQuota gain keep-last-good with
  per-scope snapshot reset; rejected queries with no displayable value
  synthesize a failure placeholder carrying the real error message so
  footers keep rendering a retry entry point.
- HTTP 429 is classified transient (retry-later) alongside 5xx.
- UsageScriptModal surfaces string rejections via extractErrorMessage.

Tests: 6 backend behavior tests drive real HTTP against a local listener
to pin the Err/Ok channel semantics; frontend suite extends
keepLastGoodUsage coverage (405 passing).
2026-07-07 20:05:29 +08:00
Jason 7fada72dd4 chore(code0): update partner invite link to agent register URL
Replace the ?source=ccswitch referral with the new agent register invite
link (https://code0.ai/agent/register/B2XHxGjGmRvqgznY) on code0's
apiKeyUrl across all seven presets and the sponsor rows in all four
READMEs. API endpoints stay bare per the referral red-line.
2026-07-07 10:49:32 +08:00
Jason 9f7642e29c refactor(profiles): drop manual snapshot update now that switching autosaves
Since switching projects automatically saves the current configuration
back to the project being left, the per-project "update from current"
button is redundant and even harmful: it allowed overwriting another
project's snapshot with the current state, breaking the invariant that
a project holds the state you last left it in.

- Remove the resnapshot button and confirm dialog from the manage
  dialog; drop the now-unused scope prop and PROFILE_SCOPE_LABELS
- Add a footer Close button to the manage dialog (overlay click is
  disabled app-wide, so it previously could only be closed via Esc)
- Update manageDescription in all four locales to describe the
  autosave behavior; remove the two dead i18n keys
- Fix a shadowed `warnings` vec in ProfileService::apply that silently
  dropped autosave-failure warnings before they reached the frontend
- Mention auto-capture on next switch in the "no snapshot for this
  scope yet" warning and doc comments

The backend update command keeps its resnapshot parameter; it is now
only exercised by the pre-switch autosave path.
2026-07-07 10:49:32 +08:00
Jason 22159430c6 fix(profiles): use camelCase keys for current profile ids in frontend 2026-07-07 10:49:32 +08:00
Jason 754af2cc31 feat(profiles): split Claude Desktop into independent profile scope 2026-07-07 10:49:32 +08:00
Jason f05ed3dbac fix(ui): invalidate proxy takeover status after profile switch 2026-07-07 10:49:32 +08:00
Jason dbb5999d1e refactor(profiles): shared project entity with per-scope switching
Projects are now global shared entities; Claude and Codex groups switch
independently via scoped current pointers and scoped payload slots.

- Remove scope column from profiles; keep current_profile_id_<scope>
- Use Option<Vec<String>> for mcp/skills to distinguish 'never captured'
  from 'captured empty', preventing cross-side accidental disable
- Update/apply operations scoped to the active group via merge_scope_from
- Tray menu nests same shared list under Claude Code/Codex groups
- Add i18n for per-scope tooltips and 'not saved for this side' hint

Refs: profile P1 shared-entity redesign
2026-07-07 10:49:32 +08:00
Jason 65a5464fcc feat(profiles): include Claude Desktop provider in project profiles
Claude Desktop's only dimension managed by cc-switch is its provider
(MCP/Skills are hardcoded unsupported and prompts have no live file),
so snapshots capture just the current desktop provider while the empty
MCP/Skills sets and None prompt make apply a natural no-op for the
other dimensions - no per-dimension special casing needed.

- Add claude-desktop slot to PROFILE_APPS and PerApp (serde key uses
  the hyphenated app id); old payloads without the key deserialize to
  None and leave Claude Desktop untouched on apply
- Gate the tray Projects submenu by iterating PROFILE_APPS instead of
  hardcoding Claude/Codex visibility
- Show the profile switcher on the Claude Desktop tab, mirror the
  PerApp type, invalidate the claude-desktop providers cache on apply,
  and extend the switcher tooltip in all four locales
- Extend the roundtrip integration test with the desktop provider
  dimension; the desktop switch is cfg-gated to macOS/Windows because
  desktop live writes error on Linux where CI runs cargo test
2026-07-07 10:49:32 +08:00
Jason 6179c18805 fix(profiles): scope switcher to supported app tabs and relocate it
- Render the profile switcher only when the active tab is Claude or
  Codex (frontend mirror of backend PROFILE_APPS), so viewing an
  unsupported app no longer suggests its config was switched
- Move the switcher from beside the logo to the right of the route
  toggles, where the flexible spacer absorbs its appearance and other
  header controls no longer shift when switching tabs
2026-07-07 10:49:32 +08:00
Jason 8f018a2d45 feat: add project profiles for snapshot-based config switching
Add a profile feature that captures the current provider, MCP,
skills and prompt state for Claude Code and Codex as a named
snapshot, and re-applies it in one click from the header switcher
or the tray Projects submenu.

- New profiles table (schema v12) with current marker in settings
- ProfileService orchestrates the four existing switch primitives
  (provider first, then MCP diff, skills diff, prompt enable)
- Best-effort apply: dangling references become warnings, no rollback
- Header combobox switcher + snapshot-style manage dialog
- Tray Projects submenu shared with the UI apply/event pipeline
- i18n for zh/en/ja/zh-TW under the new profiles domain
- Integration tests covering roundtrip, dangling refs and clear
2026-07-07 10:49:32 +08:00
秋澪Akimio b3e5e32c89 feat: add Claude subagent model config (#4830)
* feat: add Claude subagent takeover config

* feat: add Claude subagent model field

* i18n: add Claude subagent model labels

* fix(proxy): preserve configured subagent model mapping

* fix(providers): exclude subagent model from Claude common config

* style: format rust code
2026-07-07 10:28:36 +08:00
SaladDay 7a7d41c873 fix(subscription): display Codex free-plan 30-day quota window (#3651) (#4886)
* fix(subscription): display Codex free-plan 30-day quota window (#3651)

Codex free accounts are metered on a rolling 30-day secondary window
(limit_window_seconds = 2,592,000) instead of the weekly window used by
paid plans. The backend already maps this correctly to the tier name
"30_day", but the frontend TIER_I18N_KEYS whitelist had no entry for it,
so SubscriptionQuotaView filtered the tier out. When that was the only
surviving tier (as happens for free accounts), the whole quota footer
rendered nothing — free accounts showed no remaining quota or reset time
while paid accounts worked.

- Add "30_day" -> subscription.thirtyDay to TIER_I18N_KEYS
- Add the thirtyDay label to all four locales (zh/en/ja/zh-TW)
- Add a unit test locking in window_seconds_to_tier_name mappings,
  including the 30-day case

Fixes #3651

* fix(tray): render Codex free-plan 30-day window in tray menu

The tray keeps its own tier-name whitelist (TIER_LABEL_GROUPS) independent
of the frontend TIER_I18N_KEYS. Its month group only listed "monthly", so a
free Codex account whose only tier is "30_day" produced empty labeled parts
→ format_subscription_summary returned None → the tray showed no quota, even
though the footer now does. That breaks the invariant the existing
gemini_summary_lite_only_still_renders test guards: any tier visible in the
footer must not leave the tray blank.

- Add TIER_THIRTY_DAY ("30_day") constant so the string is single-sourced
  across backend mapping, tray grouping, and the frontend whitelist
- Map 2_592_000s explicitly to it in window_seconds_to_tier_name
- Add TIER_THIRTY_DAY to the tray month ("m") group
- Add codex_summary_thirty_day_only_still_renders regression test

Follow-up to the review on #3651 / PR for the 30-day footer fix.
2026-07-05 22:54:20 +08:00
Butui Hu ffc22ea714 feat(universal-provider): Auto-sync after adding and drop unused addSuccess i18n key (#2811)
* feat(universal-provider): Auto-sync after adding and drop unused addSuccess i18n key

Signed-off-by: Hu Butui <hot123tea123@gmail.com>

* fix(i18n): sync zh-TW universal provider strings

---------

Signed-off-by: Hu Butui <hot123tea123@gmail.com>
Co-authored-by: Jason <farion1231@gmail.com>
2026-07-05 22:41:05 +08:00
Cheng Zi Feng 7a8b956253 Update Longcat presets to LongCat-2.0 (#4838)
* Update Longcat presets to LongCat-2.0

* fix(proxy): classify LongCat-2.0 as text-only for media sanitizer

The Longcat presets now use LongCat-2.0, but the known_text_only_model
allowlist still only matched the retired longcat-flash-chat tail. Without
this, images pasted into a text-only LongCat-2.0 session are forwarded
upstream instead of being replaced with the unsupported-image marker,
causing a hard rejection. Add longcat-2.0 (keeping the retired name for
saved configs) and a regression test.

---------

Co-authored-by: chengzifeng <chengzifeng@meituan.com>
Co-authored-by: Jason <farion1231@gmail.com>
2026-07-04 16:38:35 +08:00
Jason 9079935d72 feat(presets): add NekoCode partner preset for 6 apps
Partner aggregator (New API gateway) across Claude Code, Claude Desktop,
Codex, OpenCode, OpenClaw, and Hermes -- no Gemini, since its /v1beta
surface falls through to the web app. Claude Code and Claude Desktop use
the bare host https://nekocode.ai; Codex, OpenCode, OpenClaw, and Hermes
use /v1 with gpt-5.5.

Marks isPartner with the ?aff=CCSWITCH referral on apiKeyUrl only (the
website URL and every API endpoint stay bare, so the attribution param
never rides on a real request). Adds the partnerPromotion blurb in all
four locales and a sponsor row appended to the end of all four READMEs,
surfacing the CC Switch offer: 10% off top-ups with promo code cc-switch.
Registers the logo-derived nekocode-icon.png via URL import; the README
banner is normalized to the standard 1920x798 aspect ratio.
2026-07-01 22:27:10 +08:00
Jason a8657d229a feat(presets): add Code0.ai partner preset for 7 apps
Partner aggregator (New API gateway) across Claude Code, Claude Desktop,
Codex, Gemini, OpenCode, OpenClaw, and Hermes. Claude Code, Claude
Desktop, and Gemini use the bare host (gemini-3.1-pro-preview for
Gemini); Codex, OpenCode, OpenClaw, and Hermes use /v1 with gpt-5.5.

Marks isPartner with ?source=ccswitch attribution on apiKeyUrl only
(website URL stays clean). Adds the partnerPromotion blurb in all four
locales and a sponsor row in all four READMEs. Positioned after
ClaudeAPI in the Claude Code and Claude Desktop lists (the two apps
that carry a ClaudeAPI preset). Registers the favicon-derived code0.png
icon via URL import; the README banner is normalized to the standard
1920x798 aspect ratio.
2026-07-01 22:27:10 +08:00
Jason cd9e025b5a feat(presets): switch Claude Sonnet tier default to Sonnet 5
Bump every claude-sonnet-4-6/claude-sonnet-4.6 default pin to claude-sonnet-5 across all provider presets (claude, claudeDesktop, hermes, openclaw, opencode, universal NEWAPI_DEFAULT_MODELS) and the Claude Desktop DEFAULT_PROXY_ROUTES sonnet route id. Update route-derivation test expectations accordingly. Non-Anthropic pins (gpt/gemini/glm/sonnet-4-5) left untouched.
2026-07-01 22:27:10 +08:00
Jason 332a3c16a8 feat(presets): add Amux provider preset for 6 apps
Non-partner aggregator across Claude Code, Claude Desktop, Codex,
OpenCode, OpenClaw, and Hermes. Claude Code uses bare host
api.amux.ai; Codex uses /v1 native Responses; the other three apps
default to gpt-5.5. No Gemini preset. Registers the amux inline icon
(currentColor) in the icon index and metadata.
2026-07-01 22:27:10 +08:00
SaladDay 586fb46b10 fix(usage): keep date-range picker calendar on-screen in narrow popovers (#4860)
The custom date-range picker switched to its two-column layout (date
fields | calendar) based on the viewport width via Tailwind `sm:`
(640px). But the popover is clamped to `100vw - 2rem` and anchored to
its trigger with `align="end"`, so its actual available width is not the
viewport width. On narrow windows the wide two-column layout could
activate while the popover only had room for one column, pushing the
calendar column off the right edge of the window where it was clipped
and unusable (month header and 4 of 7 weekday columns cut off).

Gate the two-column layout on the popover's own inline size via a CSS
container query instead of the viewport, and let the popover width
shrink to fit the viewport. When the popover is genuinely wide it shows
two columns; when it is clamped narrow it stacks vertically so the
calendar stays fully visible. The base stacked layout is the safe
fallback for engines without container-query support.

Closes #4669
2026-07-01 16:33:07 +08:00
Nothing 251b13e5d2 feat(sessions): 新增会话分类视图与分组管理 (#4776)
* feat(sessions): 新增会话分类查看方式

* test(sessions): 补充会话分类视图覆盖

* feat(sessions): 适配分类视图批量管理

* test(sessions): 补充分类批量选择覆盖

* feat(sessions): 新增分类视图收起状态控制

* test(sessions): 补充分类收起状态覆盖
2026-07-01 12:23:46 +08:00
Arc f21d6f7a42 fix(i18n): rename "Write Common Config" checkbox to "Apply Common Config" (#4829)
* fix(i18n): rename "Write Common Config" checkbox to "Apply Common Config"

The original label "Write Common Config" (写入通用配置) was ambiguous
about data flow direction — it read as "write current config INTO the
common config" when the actual behavior is the reverse: merge the
saved common config snippet INTO this provider's config.

Rename to "Apply Common Config" across zh/zh-TW/en/ja, including all
hint/guide/notice references and component defaultValue fallbacks, to
make the action unambiguous. Also aligns the Gemini hint wording with
the claude/codex "when checked" phrasing.

* docs(ja): sync Japanese docs with renamed 'Apply Common Config' label

Codex review on #4829 pointed out the Japanese user manual and README
still referenced the old checkbox label after the rename.

- docs/user-manual/ja/2-providers/2.1-add.md: 共通設定を書き込み → 共通設定を適用
- README_JA.md: 共有設定を書き込む → 共有設定を適用
2026-07-01 11:58:31 +08:00
ayxwi ab3e628bad Fix scroll bounds for long select dropdowns (#4798)
Co-authored-by: Xvvln <180168103+Xvvln@users.noreply.github.com>
2026-07-01 11:57:17 +08:00
Jason 01bf003783 feat(presets): add TeamoRouter provider preset for 6 apps
Add TeamoRouter (enterprise Agentic LLM gateway) as a sponsor partner
across Claude Code, Claude Desktop, Codex, OpenCode, OpenClaw and Hermes.

- Claude Code uses bare host https://api.teamorouter.com; Codex uses
  https://api.teamorouter.com/v1 (native Responses); OpenCode/OpenClaw/
  Hermes use gpt-5.5 over the OpenAI-compatible /v1 endpoint. No Gemini.
- apiKeyUrl uses the English referral link with UTM params (in-app stays
  English; README links are per-language).
- Add localized in-app promo blurb (zh/en/ja/zh-TW).
- Add README sponsor rows below PatewayAI in all four READMEs.
- Register the teamorouter provider icon and pad the README banner to the
  standard 2.40 aspect ratio.
2026-06-30 23:01:49 +08:00
Jason e1e3dadf89 chore(icons): remove unused dds.svg orphan
dds.svg (1.4MB genuine vector, 2222 paths) was not imported in index.ts
nor referenced anywhere in src/, so it never shipped in the app or
appeared in the icon system — it only bloated the repo. Remove it.
2026-06-30 17:02:19 +08:00
Jason ba87e8f69d perf(icons): downscale hermes, claudecn and atlascloud icons to 256px
All three shipped well above their ~32px render size (hermes/claudecn at
512x512, atlascloud at 3525x3300). Downscale to a 256px max dimension:
hermes 125KB->38KB, claudecn 109KB->46KB, atlascloud 105KB->9KB. Same
filenames, no code change.
2026-06-30 17:02:19 +08:00
Jason 37f2e248dc perf(icons): shrink ccsub and shengsuanyun embedded-image SVGs
Both were "fake vector" SVGs wrapping a single oversized base64 PNG
(ccsub embedded 1446x1328 / 1.16MB, shengsuanyun 720x720 / 212KB),
rendered at ~32px. Downscale the embedded bitmap to a 256px max dimension
and re-embed, keeping the SVG wrapper, filename, and import unchanged
(no code change). Result: ccsub 1.16MB -> 60KB, shengsuanyun 212KB -> 51KB.
2026-06-30 17:00:53 +08:00
Jason 6f8e12a1ac perf(icons): downscale relaxcode and sudocode icons to 256px
Both shipped far larger than their on-screen render size: relaxcode was
1462x1076 / 1.16MB and sudocode 2048x2048 / 432KB. Downscale to a 256px
max dimension (aspect ratio preserved; ProviderIcon renders via <img>
object-contain), cutting them to ~41KB and ~36KB. Same filenames.
2026-06-30 16:50:38 +08:00
Jason 8fb6ebb6bd perf(icons): downscale ZetaAPI provider icon to 256px
The ZetaAPI icon shipped as a 1254x1254 / 940KB PNG, roughly 30x larger
than its on-screen render size (~32px in ProviderIcon). Downscale to
256x256, cutting it to ~40KB (-96%) so it no longer bloats the bundle.
Same filename, so no import/code change.
2026-06-30 16:49:35 +08:00
Jason e54a342301 feat(presets): add ZetaAPI provider preset for 6 apps
Add ZetaAPI (api.zetaapi.ai) as a partner aggregator preset across Claude
Code, Claude Desktop, Codex, OpenCode, OpenClaw, and Hermes (Gemini not
supported). ZetaAPI relays native Claude/GPT, so Claude Code/Codex pass
through native models; the OpenAI-compatible apps default to gpt-5.5.

- Claude / Claude Desktop: Anthropic-compatible bare host (api.zetaapi.ai)
- Codex: native Responses at api.zetaapi.ai/v1, gpt-5.5
- OpenCode / OpenClaw / Hermes: gpt-5.5 over api.zetaapi.ai/v1
- Partner referral link (zetaapi.ai/go/ccs) as apiKeyUrl + short
  partner-promotion blurb in zh/en/ja/zh-TW
- Add ZetaAPI sponsor entry to all four READMEs (zetaapi-banner.png),
  first-recharge 10% off with promo code CC-SWITCH
- Register zetaapi-icon.png icon (URL import + iconUrls + metadata)
2026-06-30 16:39:32 +08:00
Jason d4feaa4e53 chore(kimi): update referral links to new platform.kimi.com domain
Moonshot rebranded its console to Kimi, so refresh all Kimi sponsor
referral links across provider presets and READMEs:

- API (domestic): platform.moonshot.cn/console -> platform.kimi.com
- API key page: platform.moonshot.cn/console/api-keys -> platform.kimi.com/console/api-keys
- Coding Plan: www.kimi.com/code/docs/ -> www.kimi.com/code/

Also add the missing ?aff=cc-switch param to the codex and openclaw
entries that previously had no referral attribution. API endpoints
(api.moonshot.cn, api.kimi.com/coding) are intentionally left unchanged.
The overseas link (platform.kimi.ai) is not wired up yet since there is
no overseas API preset.
2026-06-30 09:15:04 +08:00
Jason 36e0378e78 feat(providers): show API key link in Claude Desktop, OpenClaw and Hermes forms
The "Get API Key" link (and partner promotion) below the API key input was
only wired for claude/codex/gemini/opencode. Claude Desktop uses a separate
form that never rendered it, and OpenClaw/Hermes were blocked by two gaps:

- useApiKeyLink whitelisted only 4 app ids, so the link was suppressed for
  claude-desktop/openclaw/hermes even when a preset carried apiKeyUrl.
- useProviderCategory only parsed claude/codex/gemini/opencode preset ids, so
  OpenClaw/Hermes category stayed undefined and the link condition
  (cn_official/aggregator/third_party) never held.

Changes:
- ClaudeDesktopProviderForm: call useApiKeyLink and replace the bare API key
  Input with the shared ApiKeySection.
- useApiKeyLink: add claude-desktop/openclaw/hermes to the whitelist and add
  ClaudeDesktopProviderPreset to the PresetEntry union.
- useProviderCategory: parse and resolve openclaw/hermes preset categories.
- Hermes/OpenClaw form fields: don't let an "official" category disable the
  API key input, since these apps have no OAuth-only official providers
  (e.g. Hermes' Nous Research is "official" but still needs a user key).
2026-06-29 23:30:58 +08:00
Jason 238cd5d7c9 feat(presets): add FennoAI provider preset for 6 apps
Add FennoAI (api.fenno.ai) as a partner aggregator preset across Claude
Code, Claude Desktop, Codex, OpenCode, OpenClaw, and Hermes (Gemini not
supported). FennoAI relays native Claude/GPT, so Claude Code/Codex pass
through native models; the OpenAI-compatible apps default to gpt-5.5.

- Claude / Claude Desktop: Anthropic-compatible bare host (api.fenno.ai)
- Codex: native Responses at api.fenno.ai, gpt-5.5
- OpenCode / OpenClaw / Hermes: gpt-5.5 over api.fenno.ai/v1
- Partner referral link as apiKeyUrl + short partner-promotion blurb in
  zh/en/ja/zh-TW
- Add FennoAI sponsor entry to all four READMEs (fenno-banner.png)
- Register fenno-icon.webp icon (URL import + iconUrls + metadata)
2026-06-29 23:30:58 +08:00
Jason 3476707321 feat(presets): add Qiniu (七牛云) provider preset for all apps
Add the Qiniu Cloud AI gateway (api.qnaigc.com primary, api.modelink.ai
overseas mirror) as a partner aggregator preset across Claude Code, Claude
Desktop, Codex, Gemini, OpenCode, OpenClaw, and Hermes.

Qiniu relays native Claude/GPT/Gemini, so Claude Code/Codex pass through
native models (no domestic-model pinning); the OpenAI-compatible apps
default to gpt-5.5.

- Claude / Claude Desktop: Anthropic-compatible bare host, native passthrough
- Codex: native Responses at /bypass/openai/v1, gpt-5.5
- Gemini: gemini-3.1-pro-preview via /bypass/vertex
- OpenCode / OpenClaw / Hermes: gpt-5.5 over OpenAI-compatible /v1
- Localized display name via nameKey + partner promotion blurb in zh/en/ja/zh-TW
- Register qiniu.png icon (URL import + iconUrls + metadata)
2026-06-29 23:30:58 +08:00
Jason 64a94ba3f7 chore(presets): update SiliconFlow referral link
Replace the SiliconFlow invite code drGuwc9k with YflgU2Ve across all
README locales and provider presets (claude, claude-desktop, codex,
hermes, openclaw).
2026-06-29 23:30:58 +08:00
Jason d916b4e12d fix(openclaw): sync Doubao context window to 262144
The OpenClaw DouBaoSeed preset hard-coded contextWindow 128000 while the
Codex preset/catalog uses 262144 for the same model, giving OpenClaw users
a too-small window that could compress or truncate long context early.
Align to 262144 and add a cross-preset consistency test asserting the
OpenClaw and Codex Doubao context windows stay equal, so neither side can
drift again.
2026-06-29 23:30:58 +08:00
Jason 7f83feb222 fix(presets): use dated Doubao model id and price 6-digit dated models
Volcengine Ark rejects the bare model name doubao-seed-2-1-pro with a 404
("model does not exist or you do not have access to it") even after the
model is activated; the API requires the full dated id
doubao-seed-2-1-pro-260628. Update the Doubao preset model id across all
apps (config default, generated catalog, and OpenClaw namespaced refs).

Pricing lookup only stripped 8-digit (YYYYMMDD) and ISO date suffixes, not
the 6-digit YYMMDD format Volcengine uses (-260628, -250615), so real
Doubao usage never matched the bare-name pricing seed and showed $0 cost.
Extend strip_model_date_suffix to also strip 6-digit YYMMDD (with
month/day validation to avoid eating non-date version suffixes), keeping
the bare-name pricing seed as the canonical identity. This also fixes
pricing for every other Volcengine Doubao model. Add unit and end-to-end
regression tests.
2026-06-29 23:30:58 +08:00
Jason 562480870d fix(presets): point Volcengine/Doubao/BytePlus website links to official sites
The websiteUrl for the 火山Agentplan, BytePlus and DouBaoSeed presets was
accidentally set to the same invite/console link as apiKeyUrl, so the
"visit official site" button took users to the referral/API-key page
instead of the product homepage. Restore clean official homepages across
all six app preset files; the apiKeyUrl referral links are kept intact.

- 火山Agentplan -> https://www.volcengine.com/product/ark
- DouBaoSeed    -> https://www.volcengine.com/product/doubao
- BytePlus      -> https://www.byteplus.com/en/product/modelark (drop utm)
2026-06-29 23:30:58 +08:00
Jason 85b38c1bed refactor(codex): decouple model mapping from local-routing toggle
Align the Codex provider form with Claude Code. Model mapping (the
catalog) is independent from route takeover: native Responses providers
(MiMo, Doubao, MiniMax) need it for direct connect with no proxy, while
Chat providers use the proxy regardless of any per-provider flag.

- Remove the "Needs Local Routing" toggle. It had no backend field and
  only gated catalog/reasoning persistence, which is equivalent to
  "is the mapping filled".
- Always show model mapping for non-official providers; persist when the
  list is non-empty (backend already keys off modelCatalog.models).
- Gate reasoning visibility/persistence on Chat format, not the toggle.
- Mark the Chat upstream-format option "routing required" and refresh the
  advanced-section hint (zh/en/ja/zh-TW); drop dead localRouting* keys.
- Move the model-mapping block above the custom User-Agent block.

fix(codex): preserve native-profile hidden catalog fields on edit

useCodexConfigState dropped supportsParallelToolCalls / inputModalities /
baseInstructions when loading a saved provider, so editing and saving a
native Responses provider lost parallel tools, image input and the
official base instructions from the generated catalog. Preserve them on
load (camelCase + snake_case) and compare them in the row sync. Add a
regression test.
2026-06-29 23:30:58 +08:00
Jason 26f0d221c0 fix(codex): reject web_search for Qwen3-Coder and correct LongCat context window
- Add `qwen3-coder` to the web_search reject model-prefix blacklist so the
  native qwen3-coder-plus direct-connect preset suppresses the built-in tool
  (百炼 rejects it for the coder series), while general Qwen models sharing the
  DashScope host keep web_search enabled. Matched on the model axis, not host.
- Correct LongCat-2.0-Preview context window from 128K to its real 1M
  (1048576), aligning with the MiMo/Qwen 2^20 convention.
- Tighten native Responses preset tests to assert exact model→contextWindow
  catalogs instead of only checking catalog presence.
2026-06-29 23:30:58 +08:00
Jason 15e712e779 feat(codex): support native Responses direct-connect with generated model catalog
Codex providers can now run in two modes per provider:

- Proxy-Chat (route takeover): apiFormat=openai_chat, existing Responses<->Chat conversion. Unchanged.

- Native-Responses (direct): apiFormat=openai_responses, no proxy. cc-switch generates ~/.codex/cc-switch-model-catalog.json so Codex shows the custom models and tools work without the freeform apply_patch (type=custom) tool that native gateways like MiMo reject; editing falls back to shell_command.

Catalog generation is keyed on apiFormat (CodexCatalogToolProfile), decoupled from the takeover toggle, so native providers persist a catalog without enabling local route mapping.

Codex's catalog parser requires base_instructions on every entry; the native template carries a neutral default and per-vendor official text overrides it (MiMo, MiniMax). Synthesized catalogs for Qwen/Doubao/LongCat use the neutral default.

Existing native providers must be re-saved once to regenerate a valid catalog (no DB migration).
2026-06-29 23:30:58 +08:00
yyhhyyyyyy d1f6c74be8 fix(usage): treat usage_script credentials as explicit overrides (#4654)
* fix(usage): treat usage_script credentials as explicit overrides

* fix: treat usage script credentials as explicit overrides
2026-06-28 17:37:58 +08:00
Jason 16922917fb chore(about): remove Fable 5 verified commemorative banner 2026-06-27 10:34:14 +08:00
Jason 90bc37449c fix(presets): sync OpenCode Go referral link and promo to Claude Desktop
The Claude Desktop OpenCode Go preset was missed by the referral/promo update that landed for Claude Code / Codex / OpenCode, so its websiteUrl pointed at the bare opencode.ai domain with no apiKeyUrl or partnerPromotionKey. Align it with the other three: websiteUrl -> /go, apiKeyUrl -> the ref=2YTRG2NGTX referral link, and partnerPromotionKey "opencode_go" so the in-app promo banner shows. All four OpenCode Go presets now carry the referral link and promo copy.
2026-06-27 00:21:19 +08:00
Jason de386b297f feat(presets): point SubRouter apiKeyUrl to aff referral link
Swap the SubRouter apiKeyUrl across all 7 client presets from the
neutral console/token page to the affiliate registration link
(?aff=l3ri), matching the project's referral-link convention.
Sync the preset test constant accordingly.
2026-06-26 23:49:08 +08:00
Jason e6d40d0a93 feat(presets): add OpenCode Go referral link and promo copy without partner badge
Decouple the in-app promotion banner from isPartner so a preset can show a
promo phrase plus referral link without earning the paid-partner star.

- ApiKeySection: gate the banner on partnerPromotionKey alone; the star is
  still driven solely by isPartner. This also un-suppresses the existing
  MiniMax cn/en promos, which already carried copy without isPartner.
- OpenCode Go (claude/codex/opencode): point apiKeyUrl at the referral
  link and add partnerPromotionKey "opencode_go" (no isPartner set).
- i18n: add the opencode_go promo string across zh/en/ja/zh-TW.
2026-06-26 23:39:18 +08:00
Jason dd6a951c34 feat(presets): add OpenCode Go subscription preset for Codex and OpenCode
Add OpenCode Go (opencode.ai/zen/go) provider presets across clients:

- Codex: openai_chat conversion with model catalog (GLM/Kimi/DeepSeek/MiMo), no static codexChatReasoning so per-model capability is inferred

- OpenCode: @ai-sdk/openai-compatible against /zen/go/v1

- Claude: add apiKeyUrl (opencode.ai/auth) and align websiteUrl to /go

Auth is a plain pasteable API key (no OAuth). Go splits wire format by model: OpenAI /chat/completions for GLM/Kimi/DeepSeek/MiMo, Anthropic /messages for MiniMax/Qwen; these presets target the chat-format models.
2026-06-26 23:39:18 +08:00
abing|web3.club 430ddf92bd Add SubRouter provider presets (#4522)
Co-authored-by: abingyyds <abingyyds@gmail.com>
2026-06-26 23:30:52 +08:00
Jason 273cc48c57 feat(codex): use native Responses API for CN providers that support it
Several Chinese providers now expose a native OpenAI Responses API
endpoint, so Codex can reach them directly without the Responses->Chat
route-takeover conversion. Switch these presets to apiFormat
"openai_responses" and drop the now-unused codexChatReasoning and
modelCatalog (removing modelCatalog also keeps the "local route mapping"
toggle unchecked by default):

- Qwen / DashScope Bailian (/compatible-mode/v1/responses)
- Xiaomi MiMo + Token Plan (api.xiaomimimo.com/v1)
- Volcengine Doubao (/api/v3/responses)
- Meituan LongCat (/openai/v1)
- MiniMax CN + intl (/v1/responses, confirmed in the official API reference)

SiliconFlow-hosted MiniMax stays openai_chat (third-party endpoint, not
MiniMax's own base_url).

Also refresh stale model ids on the remaining chat-only providers:
GLM 5.1->5.2, StepFun 3.5-flash-2603->3.7-flash, Ling 2.5-1T->2.6-1T.
2026-06-26 09:15:51 +08:00
Jason f1328d89fc feat(skills): flag unmanaged skills with a green dot on the Import button
- Add an optional `enabled` flag to useScanUnmanagedSkills; the Skills panel
  scans once on mount (enabled:true), with 30s staleTime + keepPreviousData to
  dedupe disk IO across navigations.
- App subscribes to the shared query (enabled:false) and renders a green dot +
  tooltip on the top-bar Import button when unmanaged skills are available.
2026-06-26 09:15:51 +08:00
Jason a4eb5f3778 feat(codex): decouple upstream format selector from model-mapping toggle
The Codex provider form tied Chat-format conversion and route takeover (model
mapping) to one toggle, so a provider serving a native Responses API could not
use model mapping without forcing Chat Completions conversion.

- Promote the upstream format (Chat Completions / Responses) to an independent,
  always-visible selector that triggers no sub-menus on its own.
- The local-routing toggle now solely gates the advanced sub-sections: model
  mapping catalog, plus reasoning capability when the format is Chat.
- Persist modelCatalog / codexChatReasoning based on the takeover toggle and
  derive its initial state from saved catalog presence (no new persisted field).
- Refresh codexConfig i18n (zh/en/ja/zh-TW): add upstreamFormat* keys and reword
  the routing/advanced hints to reflect the decoupling.
- Fix codexChatProviderPresets test expectation for the Doubao Seed 2.1 Pro rename.
2026-06-26 09:15:51 +08:00