Commit Graph
2292 Commits
Author SHA1 Message Date
Jason eb69e4922e feat(presets): add XycAi partner preset across all seven apps
- Gateway: apicdn.xycai.us primary, apicdn.xyc.ai fallback
- Claude auth uses ANTHROPIC_API_KEY per official docs
- Claude Desktop connects direct with native Anthropic format
- In-app partner promotion only (no README entry), four locales
- Register xycai icon (PNG via iconUrls) with metadata
2026-08-13 17:53:33 +08:00
Jason 5b8bf1fe4f feat(presets): split Volcengine into Agent Plan and Coding Plan presets
The former 火山Agentplan preset actually pointed at Coding Plan
endpoints (/api/coding[/v3]). Split it into two presets across all six
app preset files:

- 火山 Agent Plan: /api/plan (Anthropic) and /api/plan/v3 (OpenAI),
  with its own activity/agentplan invite link
- 火山 Coding Plan: keeps /api/coding[/v3] and the original
  activity/codingplan invite link

Both plans' OpenAI endpoints are covered by the official Codex docs
(wire_api=responses), so the Coding Plan Codex preset also uses native
Responses. Widen the Volcengine usage detection to
volces.com/api/(plan|coding) in both the frontend pattern and the
backend detect_provider mirror; the quota query already auto-probes
both plans (GetAFPUsage then GetCodingPlanUsage). Split the partner
promo copy into volcengine_agentplan / volcengine_codingplan i18n keys
in all four locales, and pin both presets in the codex native
Responses preset test.
2026-08-13 17:53:01 +08:00
Jason a7f073e95d docs(partner): update FennoAI offer to $1.99 trial plan with $50 credits
FennoAI changed its CC Switch benefit: the previous ¥9.9/month Coding
Plan worth $150 in credits is replaced by a $1.99 Trial Plan worth
$50 in credits, valid for 7 days. Update the in-app promo line in all
four locales and the partner sections of the four READMEs. The 20%
referral reward and the registration link stay unchanged.
2026-08-13 17:53:01 +08:00
Jason c99550e087 chore(presets): migrate RunAPI domain to runapi.host, keep runapi.co as fallback endpoint
Switch RunAPI website, registration and request URLs to runapi.host
across all seven app presets and the four README partner sections.
The previous runapi.co endpoints are retained as endpointCandidates
fallbacks where the preset type supports the field (claude,
claude-desktop, codex, grok-build).
2026-08-13 17:53:01 +08:00
Jason 5f6072ce46 fix(ui): remove partner star badge from main panel provider cards
The star was legacy display logic gated on meta.isPartner, which the
generic provider form almost never persisted. The standalone Grok Build
form unconditionally saves meta.isPartner, resurrecting the dead code
path. Partner stars belong only in the preset selector, so drop the
card-level badge and the now-unused officialPartner i18n key from all
four locales.
2026-08-13 17:53:01 +08:00
Jason 5602324b28 fix(codex): restore angle-bracket text lost in DeepSeek catalog mirror extraction
The official catalog mirror template was extracted from DeepSeek's docs
page with HTML unescaping applied before tag stripping, so literal
angle-bracket text inside the JSON (e.g. "<this good thing>" and the
spaced-path markdown link example) was swallowed as if it were HTML
tags — 8 spots across base_instructions and model_messages for both
models, 86 bytes missing per harness copy. Restore them; the template
is now structurally identical to the current official models.json.

Also refresh the preset comment: deepseek-v4-pro's Codex integration
is live as of 2026-08 (its catalog entry differs from flash only by
priority), so the "switching early errors upstream" warning is gone.
2026-08-13 17:53:01 +08:00
Jason 7dc0a725ee fix(pricing): correct Grok 4.5 cached rate, seed Grok 4.6 and a DeepSeek alias
Findings from a models.dev audit of the seeded pricing table:

- Correct grok-4.5 cached input 0.50 -> 0.30 (seed + repair with an
  old-value guard). The current xAI list table, models.dev and the billing
  observed for grok-4.5-build all state 0.30; 0.50 is grok-4.6's rate.
- Seed grok-4.6 (2 / 6 / 0.50, released 2026-08-12). Both 4.5 and 4.6 double
  their rates above a 200K prompt, but the table has no tier column, so the
  base tier is stored, consistent with other tiered vendors.
- Seed a deepseek-v4-flash-0731 alias at DeepSeek's official price.
  strip_model_date_suffix only strips ISO, 8-digit YYYYMMDD and 6-digit
  YYMMDD suffixes, and the prefix fallback only matches longer rows, so this
  id resolved to no pricing row and was silently billed at zero.

The GrokBuild backfill test expectations follow the corrected cache-read
price; its cache-inclusive input assertion (450 fresh tokens) is unchanged.
2026-08-13 17:53:00 +08:00
Jason c82624761c fix(proxy): stop thinking/reasoning_content injection for Kimi upstreams
Moonshot/Kimi asked to exit the reasoning-vendor workaround: their
endpoints no longer require thinking replay on tool-call turns, and the
injected placeholders disrupt the model's chain of thought.

- Remove moonshot/kimi from REASONING_VENDOR_HINTS; Kimi now gets clean
  passthrough on the anthropic path (no placeholder thinking blocks, no
  signature stripping) and no reasoning_content on the openai_chat path.
- DeepSeek/MiMo keep the behavior (documented server-side requirement).
- Flip the two Kimi gating tests into negative regressions and update
  stale comments/test model names that still referenced Kimi.
2026-08-13 17:53:00 +08:00
Jason d2b070c965 fix(proxy): never clobber Codex official ChatGPT login on takeover restore
The restore backup is a snapshot taken when takeover starts and is never
refreshed from live afterwards. If the user logged into official ChatGPT
while takeover was active, exit/crash-recovery restored the stale
third-party API-key snapshot verbatim over auth.json, wiping the login on
every restart (and the auto re-takeover on startup looped the clobber).

Restore now arbitrates before writing: whenever live auth.json holds real
login credentials (codex_auth_has_credential_login_material, and not the
proxy placeholder), live wins — the backup's API key is demoted into
config.toml as experimental_bearer_token and the backup auth slot is
dropped, so auth.json is left untouched. Live credentials are always newer
than the snapshot because only Codex itself (login / token self-refresh)
advances them, so even an official-shape backup must not roll live tokens
back. Metadata residue (last_refresh / tokens.account_id) counts neither
as a live login to protect nor as backup material to restore. Both restore
entry points — the with-fallback path (exit / crash recovery / takeover
disable) and the simple path (takeover rebuild and failure rollback) —
share the same protection.

Fixes #6277
2026-08-13 17:53:00 +08:00
Thefool c9fe340be0 fix(sync): harden sync and restore consistency (#6147)
Closes #6129
2026-08-13 16:09:08 +08:00
Thefool dfb2e5235b fix(backup): preserve SQL fidelity and recovery safety (#6146)
* fix(backup): preserve incremental auto-vacuum on import

* fix(backup): preserve SQLite scalar values in SQL dumps

* fix(backup): preserve autoincrement high-water marks

* fix(backup): restore valid empty SQL exports

* fix(backup): make restores atomic under retention

* fix(backup): publish safety snapshots atomically

* fix(backup): serialize backup file operations
2026-08-13 15:50:07 +08:00
Thefool 1f38c83826 fix(zhipu): accept CREDIT_LIMIT quota entries (#6160)
Zhipu renamed the quota entry type field from TOKENS_LIMIT to CREDIT_LIMIT on the mainland endpoint, causing all quota tiers to be skipped and the usage panel to show empty. Accept both values (case-insensitive) so old plans and the international endpoint keep working.

Fixes #6153
2026-08-12 22:09:45 +08:00
SaladDayandJason 7e5007d5bc fix(claude-desktop): clarify model configuration modes (#6208)
* fix(ui): unify checkbox styles

* fix(claude-desktop): align provider form frame

* fix(claude-desktop): clarify model configuration modes

* fix(claude-desktop): preserve direct preset models

---------

Co-authored-by: Jason <farion1231@gmail.com>
2026-08-12 21:49:16 +08:00
SaladDay 580a4d7bbd refactor(hermes): align provider form hierarchy (#6207)
* refactor(hermes): align provider form hierarchy

* fix(hermes): match model detail label typography

* fix(hermes): mute context length label

* fix(hermes): align model detail field width

* fix(hermes): expose model detail state
2026-08-12 21:43:34 +08:00
SaladDayandallenxu09 ccc8629810 feat(proxy): animate routing activation (#6209)
* feat(proxy): animate routing activation

* fix(proxy): gate routing activation animation

* fix(proxy): wait for initial status before takeover

---------

Co-authored-by: allenxu09 <s23372.xu@stu.scie.com.cn>
2026-08-12 21:38:50 +08:00
SaladDay ec84215614 refactor(opencode): clarify provider form hierarchy (#6206) 2026-08-12 21:31:43 +08:00
jiekouaiandjiekouai 58d92e5616 feat(provider): add JieKou AI presets (#6356)
* feat(provider): add JieKou AI presets

* feat(provider): use Claude Fable 5 for JieKou AI

---------

Co-authored-by: jiekouai <231459779+jiekouai@users.noreply.github.com>
2026-08-12 20:49:00 +08:00
SaladDay c005062319 fix(ui): unify checkbox styles (#6203) 2026-08-11 23:23:26 +08:00
SaladDay 5b77da2b60 style(openclaw): clarify User-Agent section hierarchy (#6204)
* refactor(openclaw): align provider model editor

* style(openclaw): clarify user agent section hierarchy
2026-08-11 23:21:04 +08:00
SaladDay 619a592c38 fix(claude-desktop): align provider form frame (#6205) 2026-08-11 23:20:33 +08:00
SaladDayandJason 95b95da6ab refactor(openclaw): align provider model editor (#6201)
Co-authored-by: Jason <farion1231@gmail.com>
2026-08-11 23:20:06 +08:00
Thefool 076c2744ce fix(provider): finish model dropdown consolidation (#6353) 2026-08-11 18:05:09 +08:00
Thefool 7e152d758b feat(provider): add fuzzy search to model mapping dropdown (#6285)
The model mapping dropdown (used by Claude Desktop, Codex, and OpenCode
provider forms) lists every fetched model grouped by vendor with no way
to filter. Providers with hundreds of models make the right model hard
to find, as requested in #6275.

Convert ModelDropdown from a plain DropdownMenu to a Popover + Command
combobox (the existing OmoFormFields pattern), adding a fuzzy search
input powered by cmdk:
- non-matching groups auto-hide; an empty state shows when nothing matches
- vendor name is indexed as a keyword, so models can be matched by vendor
  as well as by model id
- the { models, onSelect } API is unchanged, so all 5 call sites work as-is

Consolidate two near-identical inline copies of the same grouped
dropdown (ModelInputWithFetch and the Copilot branch in ClaudeFormFields)
onto ModelDropdown so every model picker gains the search and the
duplication is removed.

Add searchModelPlaceholder / searchModelEmpty / searchModelAriaLabel i18n
keys for en, zh, zh-TW, ja.
2026-08-11 16:13:40 +08:00
Hu Miao 3711e1a046 feat(provider): add PPIO support (#6239)
* feat(provider): add PPIO support

Refs #6226

* test(provider): avoid pinning PPIO preset order
2026-08-11 15:50:11 +08:00
SaladDay 8673e9d8d8 fix(claude): align advanced options with Codex (#6200) 2026-08-11 11:24:24 +08:00
SaladDay bc7f5f413a fix(ui): reduce provider editor empty space (#6199) 2026-08-11 11:22:36 +08:00
SaladDay 7de63227d4 fix(grokbuild): add glass form container to provider form (#6198)
Apply the shared glass card styles (background, border, radius, padding) used by the generic provider form to the Grok Build provider form.
2026-08-11 11:20:19 +08:00
Jason 967daa1a06 fix(skills): report missing SSOT directory as update in check_updates
check_updates trusted the DB-cached content_hash before ever looking at
disk, so a skill whose SSOT directory is gone (e.g. after restoring a DB
backup on another machine) showed "no update" and the missing files were
permanently masked; the only recovery was uninstall + reinstall.

Extract the local-hash decision into local_hash_for_update_check with
the order inverted: validate the untrusted directory name, check that
the SSOT directory actually exists, and only then trust the cached
hash (computing and backfilling it when absent). A missing directory
now yields None, which never equals the remote hash, so the skill
surfaces as "update available" and the existing update_skill flow
rebuilds it — no new download logic.

Invalid directory values keep the pre-fix behavior (fall back to the
cached hash) instead of being reported as updatable, because
update_skill would hard-fail on the same validation gate.

Covered by four unit tests on the extracted helper.
2026-08-10 23:55:00 +08:00
Jason 3c592d93e6 fix(windows): escape backslashes before Handlebars vars in WiX registry key
The single backslash in Software\{{manufacturer}}\{{product_name}} was
consumed by Handlebars as a mustache escape, so the MSI wrote a literal
key named Software{{manufacturer}}{{product_name}} directly under HKCU.
Double the backslashes to match every other registry key in the template.

Fixes #6283
2026-08-10 23:07:51 +08:00
Jason bef46cd5de chore(grokBuild): correct OpenCode Go exclusion rationale
Upstream has had grok-4.5 since 2026-08, so 'no Grok models upstream' is no longer accurate.
Exclusion is now a deliberate product decision; enrollment requires separate review.
2026-08-10 22:56:51 +08:00
Jason 16cc0d7fea feat(claude): route OpenCode Go preset directly to /messages
/v1/messages only accepts x-api-key: switch env from ANTHROPIC_AUTH_TOKEN to ANTHROPIC_API_KEY and add top-level apiKeyField.
Drop apiFormat so Claude Code connects directly (providerNeedsRouting no longer triggers) instead of through the proxy.
Desktop keeps mode: proxy (it cannot rename models itself) but switches to anthropic passthrough.
2026-08-10 22:56:51 +08:00
Jason 390102a23b fix(codex): fill DeepSeek contextWindow in OpenCode Go catalog
Both deepseek-v4-pro/flash lacked contextWindow; codex_config.rs falls back to 128k for catalog entries without it, forcing premature compaction.
2026-08-10 22:56:51 +08:00
Jason c98cc3a9e9 ci: skip frontend/backend checks when a PR touches neither area
Add a changes-detection job (dorny/paths-filter, SHA-pinned) and gate
the frontend job, the 3-OS backend matrix, and the WSL2 contract gate
on whether the PR actually touches their inputs. About two thirds of
merged PRs only touch one side, and docs-only PRs now skip CI entirely.

Pushes to main still run everything unconditionally so the caches that
PRs restore from stay warm. Path groups mirror .github/labeler.yml,
plus rust-toolchain.toml which pins the compiler used by the backend
jobs.
2026-08-10 17:06:52 +08:00
Jason 36ed280d32 ci: fix i18n labeler glob to match actual locales path
The i18n label never fired because the glob pointed at src/locales/**
while the locale files live under src/i18n/locales/.
2026-08-10 17:06:52 +08:00
Allen XuandJason ceef0a52ad ci: run Windows backend tests against WSL2 filesystems (#6233)
* ci: add WSL2 backend checks

* ci: fix WSL2 backend job by keeping MSVC toolchain on native temp

- compile test binaries before pointing TEMP/TMP at the WSL2 UNC path
  (mt.exe fails with c1010070/LNK1327 when TEMP is a UNC path)
- inject WSL2 TEMP/TMP per test step instead of via GITHUB_ENV
- use --tests so doctests cannot trigger link steps under the UNC temp
- run the filesystem contract gate before the full serial suite
- drop fmt/clippy steps duplicated from the existing Windows job

* ci: move full WSL2 suite to nightly, keep per-PR contract gate

- the full serial suite needs 50+ minutes on the 9P filesystem
  (2026/2343 lib tests finished in 39.5min before the 45min timeout)
- per-PR job now runs only the atomic-write contract (~6min total)
  and builds just the lib test binary via --lib
- new nightly workflow (schedule + workflow_dispatch, 90min timeout)
  runs the contract plus the full serial suite on the WSL2 home

---------

Co-authored-by: Jason <farion1231@gmail.com>
2026-08-10 16:21:42 +08:00
Allen Xu c39c90322a fix(windows): fall back when WSL rejects atomic replace (#6232)
* fix(windows): fall back when WSL rejects atomic replace

* fix(windows): preserve config during WSL fallback

* fix(windows): use rename fallback for WSL UNC
2026-08-10 11:29:12 +08:00
misaka_myu 413c09e079 fix(codex): respect user-owned model_catalog_json when generating catalog (#6087)
The Some arm of set_codex_model_catalog_json_field unconditionally overwrote model_catalog_json with the cc-switch-owned filename, discarding any user-provided custom catalog path or filename. This mirrors the ownership check already present in the None arm: only claim the pointer when it is absent or already cc-switch-owned, leaving user-managed external catalog files untouched.

Add tests covering both full-path and relative-filename user-owned catalogs in the Some arm.
2026-08-06 16:19:04 +08:00
Jason 43eaf07355 docs(release): add v3.19.2 release notes v3.19.2 2026-08-06 12:21:00 +08:00
Jason 425e932b7f chore(release): v3.19.2 2026-08-06 12:21:00 +08:00
Jason baf07a2701 perf(usage): batch Codex session inserts and preload sync cursors
Full Codex usage reimport (triggered by SQL backup import of pre-v16
backups, cursor mismatch after cross-machine restore, or manual rebuild)
pegged one CPU core for minutes on large corpora. Measured on a real
corpus (1920 rollout files / ~1.7GB / 82k billable events, macOS,
release build):

- full reimport on a disk-backed database: 36.3s -> 11.1s (~3.3x)
- the write path accounts for most of the win: per-event autocommit
  under journal_mode=delete paid one full journal create/fsync/delete
  cycle per row; Windows pays ~4.6ms per fsync (per #6122
  measurements), so the absolute win there is an order of magnitude
  larger

Changes:

1. sync_single_codex_file writes token events in batched transactions
   (1000 events per commit), releasing the connection lock between
   batches so UI queries can interleave. The sync cursor advances
   inside the same transaction as the final batch, so a crash can
   never leave the cursor ahead of the data; batches committed before
   a crash are covered on rescan by the existing request_id PK +
   fingerprint dedup.
2. CodexSyncPass preloads all session_log_sync cursors once per pass
   (the archived-sessions suffix inheritance query used substr, could
   not use an index, and ran a full table scan per archived file) and
   caches model pricing lookups per pass instead of one SELECT per
   event.
3. The three dedup queries and the insert statement use
   prepare_cached; the two dynamically assembled dedup SQL strings are
   built once via LazyLock.

Behavioral equivalence verified by replaying the real corpus before
and after the change: all 82k imported rows byte-identical across all
exported columns, with identical import/skip/suspected-duplicate
counts. The replay harness is kept as an ignored test
(replay_real_codex_corpus) for future changes on this path.
2026-08-06 09:50:14 +08:00
Allen Xu 0345fad604 fix(opencode): support unified OMO config (#6011)
* fix(opencode): support unified OMO config

* fix(opencode): couple rollback snapshots to writes

* fix(ci): simplify rollback version type

* fix OMO unified config review issues

* validate OMO round-trip semantics
2026-08-05 09:04:15 +08:00
Jason 290b65c03b chore(sponsors): point Sponsor button to GitHub Sponsors profile 2026-08-04 23:32:39 +08:00
Jason 0cb6e01410 fix(ui): keep header actions visible when all apps are enabled
With every app tab, the profile switcher and the takeover toggles shown,
the header overflowed and clipped the add-provider button. Restructure
the header right side so primary actions live in a fixed shrink-0
cluster, and make AppSwitcher width-aware: apps that no longer fit
collapse into a "more" popover, with the active app always kept visible.
2026-08-04 23:32:39 +08:00
Jason 5b697abc03 chore(sponsors): move Qiniu right after AIGoCode in preset order
Reorders the Qiniu preset entry to sit directly after AIGoCode across all
eight in-app preset files. Pure block move, no field changes; README sponsor
tables are unaffected since Qiniu is not listed there.
2026-08-04 23:30:45 +08:00
40b6376b2a fix(skills): build readme_url from the resolved source directory (#6119)
* fix(skills): build readme_url from the resolved source directory

For skills.sh installs, `skill.directory` is just the skillId (the
innermost directory name) and the search-result readme_url is the bare
repo URL, so the persisted doc link lost the real nested path
(e.g. skills/developertools/solutions/<name>) and 404'd (#6111).

- install: derive the doc path from the resolved source directory
  (repo-relative, SKILL.md-anchored) ahead of the stale readme_url
  extraction and the directory-name fallback.
- update_skill: same derivation, so records written by older versions
  self-heal on the next update instead of re-assembling the broken
  path from the stale readme_url. repo_branch already follows
  used_branch.
- New choose_doc_path / doc_path_for_source helpers with regression
  tests; dropping the resolved-path priority fails the tests.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(skills): harden nested source resolution

* fix(skills): refresh repaired source metadata

* refactor(skills): keep nested readme fix install-only

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: YUZHEthefool <2804776511@qq.com>
2026-08-04 23:26:12 +08:00
668bbda916 perf(backup): batch dump INSERTs and restore preserved tables in one transaction (#6122)
* perf(backup): batch dump INSERTs and restore preserved tables in one transaction

Backup import was pathologically slow for databases with large log
tables. Measured on a database with 26k log rows (Windows, debug build):

- local file import (import_sql_string): 28.0s -> 0.39s (~71x)
- WebDAV/S3 sync import (import_sql_string_for_sync): 118.9s -> 0.33s
  (~360x)
- export: 367ms/15.4MB -> 193ms/3.9MB (column list no longer repeated
  per row)

Root causes, isolated with a phase-level benchmark (kept in the test
suite as ignored perf harnesses):

1. dump_sql emitted one INSERT statement per row. The restore side
   parses/prepares/finalizes every statement individually, so import
   cost scaled with statement count, not data volume. A control run on
   an in-memory database was equally slow, proving this is CPU-bound
   parsing, not disk I/O; journal/synchronous pragmas made no
   difference. dump_sql now emits multi-row VALUES batches of 200 rows
   (~200x fewer statements; SQLite has supported multi-row VALUES since
   3.7.11, and both old and new formats import through the same generic
   execute_batch, so dumps remain cross-version compatible).

2. restore_tables re-inserted preserved local tables (proxy logs,
   stream check logs, usage rollups) with one implicit autocommit
   transaction per row against the disk-backed staging database - one
   fsync per row, ~4.6ms each on Windows. All restores now run in a
   single transaction with the INSERT prepared once per table, which
   also makes a mid-restore failure roll back atomically instead of
   leaving a half-restored table.

Regression tests:
- dump_sql_batches_rows_into_multi_row_inserts pins the batch shape
  (450 rows -> exactly 3 INSERT statements; fails if reverted to
  per-row)
- multi_row_dump_round_trips_special_values round-trips quotes,
  newlines, commas, CJK, emoji, BLOB storage class, and NULL through
  export+import
- existing authorizer / genuine-export / sync-preserve tests still pass

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(backup): preserve SQL dump compatibility

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Co-authored-by: YUZHEthefool <2804776511@qq.com>
2026-08-04 23:04:13 +08:00
Kim Ma 59a2bd1040 fix(usage): handle interleaved Codex token counters (#5854)
* fix(usage): handle interleaved Codex token counters

* fix(usage): dedupe replayed Codex token snapshots

* fix(usage): avoid stale cross-source snapshot dedupe
2026-08-04 16:42:33 +08:00
zayokaandClaude Fable 5 6b8f36431b fix(security): cap scripts, file reads, and proxy bodies; surface deeplink usage fields (#5919)
* fix(security): harden usage script execution and Grok session log collection

Three hardening fixes in v3.19.0 code:

1. **usage_script: cap JS runtime resources and execution time**
   `execute_usage_script` used a vanilla `Runtime::new()` with no limits,
   so a malicious or buggy script delivered via deeplink or synced DB could
   hang the backend thread forever with `while(true)`. Add
   `create_script_runtime()` enforcing a 5-second interrupt handler, 16 MiB
   memory limit, and 256 KiB stack limit. Regression test confirms an
   infinite-loop script is rejected within seconds instead of blocking.

2. **Grok session log: skip oversized files**
   `sync_single_grok_file` loaded the entire `updates.jsonl` into memory
   with no size cap. Add a 50 MiB limit and skip files that exceed it.

3. **Grok session log: bound directory traversal**
   `collect_files_named` recursed without depth limits and followed
   symlinks, so a symlink cycle under `~/.grok/sessions` caused a stack
   overflow. Add a 16-level depth limit and skip symlinks entirely.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(ui): show hidden usage access token and user id in deeplink provider import dialog

The provider import confirmation dialog parsed usageAccessToken and
usageUserId from ccswitch:// URLs and persisted them in
ProviderMeta.usage_script, but never rendered them. Users could not see
these credentials before approving the import.

- Add i18n keys for usageAccessToken/usageUserId in all locales.
- Render both fields in the usage script section; mask the access token
  like usageApiKey.
- Add a regression test verifying the dialog displays the masked token
  and the user id.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(security): bound Codex model catalog and proxy response bodies

Two issues were found while auditing security boundaries:

1. **Codex model_catalog_json arbitrary file read**
    only checked the filename and
   accepted any absolute path. A malicious  could
   point CC Switch at an external file (e.g. ) or a huge
   file to exhaust memory.

   - Require the resolved path to stay inside
     using a new  helper (lexical, no filesystem calls).
   - Treat Unix-style absolute paths as absolute on Windows.
   - Cap catalog reads at 32 MiB.

2. **Unbounded proxy response bodies / compression bombs**
   Non-streaming responses and error responses were read with
    and fully decompressed without any byte
   limit, allowing a malicious upstream to OOM the backend.

   - Add  with a 128 MiB ceiling.
   - Reject oversized bodies and oversized decompressed payloads with
     .
   - Map the new error variant into the Codex proxy error response.

Regression tests cover: absolute-path containment, traversal rejection,
oversized catalog file, oversized buffered/streamed proxy responses.

Co-Authored-By: Claude <noreply@anthropic.com>

* fix(ci): set up pnpm via Corepack instead of pnpm/action-setup v6

pnpm/action-setup v6 installs pnpm through a two-step self-installer:
it bootstraps the latest pnpm (11.7.0) via npm, then downloads the
pinned version (10.12.3) from the npm registry to switch. When that
second tarball fetch failed (error 23), the self-installer still
reported "done" and crashed with ENOENT on @pnpm/linux-x64's
package.json, failing the CI setup step.

Replace the action with Node 20's bundled Corepack and pin the version
once via package.json's packageManager field, so setup performs a
single exact-version download with no floating bootstrap. This also
lets release.yml drop its Windows ARM64 special case, since Corepack
has no arch restrictions, and pins the same version for local dev.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

* fix(security): close review gaps in proxy body caps, deeplink dialog, catalog path

Address maintainer review on the security PR:

- Proxy: bound the four remaining unbounded forwarder read paths
  (non-2xx error body, failover pre-read, both 2xx error-envelope
  validators) with bytes_with_limit + bounded decompression.
- Proxy: bytes_with_limit now accumulates Hyper/Reqwest/Streamed bodies
  chunk-by-chunk and aborts mid-stream instead of collecting the full
  body before comparing; Buffered keeps the post-hoc check.
- Proxy: decompression is budgeted at the decoder (Read::take on
  gzip/deflate/zstd, bounded brotli reader) via decompress_body_with_limit,
  so bombs are cut off at the byte budget instead of after full expansion,
  including stacked-encoding intermediates.
- Deeplink: show the usage section whenever any usage field is present,
  matching build_provider_meta persistence — a deeplink carrying only
  usageAccessToken/usageUserId no longer imports credentials invisibly.
- Codex catalog: after the lexical containment check, canonicalize the
  existing file and re-verify containment so symlinks inside ~/.codex
  cannot escape the config dir; the path_is_within doc comment no longer
  oversells the lexical check as symlink-safe.
- Grok sync: comment now matches the skip-all-symlinks behavior and each
  skip is logged, so sessions behind a symlink are diagnosable.
- Docs: CONTRIBUTING notes the packageManager pnpm pin and Corepack flow.

Regression tests follow the same methodology as round 1: reverting a
guard fails the corresponding test (server write-count assertions prove
the mid-stream abort; a truncated gzip stream distinguishes TooLarge
from read-to-end errors; symlink escape is rejected only after the
canonicalize re-check).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
2026-08-04 15:44:08 +08:00
Thefool 9f19d8fdf6 feat(management): add searchable lists and bulk app toggles (#5967)
* feat(management): add searchable lists and bulk app toggles

* test(management): cover search and bulk toggle workflows

* fix(management): await refresh after app toggles

* fix(management): await collection refreshes

* fix(management): prevent stale concurrent writes
2026-08-04 14:55:10 +08:00
Allen Xu 92ca95ffcd feat(opencode): load OMO models from runtime opencode models (#5522)
* feat(opencode): load OMO models from runtime opencode models

Surface OAuth/Zen free models in OMO/OMO Slim pickers by running the
installed OpenCode CLI, with a 20s timeout and a toast when discovery fails.

* style: fix rustfmt and prettier for OMO runtime models

* fix(opencode): pass OPENCODE_CONFIG_DIR for runtime model discovery

Honor the configured OpenCode config directory when running
`opencode models`, including WSL UNC path translation. Also drop the
unused run_detected_tool_command wrapper that failed clippy.

* fix(opencode): satisfy Windows Clippy

* fix(opencode): bound runtime model discovery

* fix(opencode): address runtime model review feedback

* fix(ci): parenthesize unsafe kill expression
2026-08-04 09:43:20 +08:00