Commit Graph

121 Commits

Author SHA1 Message Date
程序员阿江(Relakkes) 74f87a27e4 revert(session): remove protocol-based session restrictions
Revert efe5cae19 so existing sessions remain usable and models can be
switched without protocol admission checks. Retain the additive index
schema for already-upgraded caches and rebuild their summaries.
2026-09-10 14:00:50 +08:00
程序员阿江(Relakkes) 56c6a9aa8e feat(computer-use): align native app automation with Codex
Add a persistent isolated JavaScript worker for native app actions and batch
known operations without a model round trip between each input. Preserve
per-cell context, native errors, screenshot coordinates, and image types.

Align macOS gesture, key, inventory, capture, scroll, and clipboard behavior;
include a signed native receiver fixture and compiled sidecar regression tests.
Keep the Windows pixel route and fix cancellation with session-owned mouse
cleanup, lock revalidation, and portable signing-fixture tests.
2026-09-10 03:34:39 +08:00
程序员阿江(Relakkes) efe5cae19e fix(session): lock model switching to the session API protocol 2026-09-09 23:05:33 +08:00
程序员阿江(Relakkes) cfc15ecbf6 Merge remote-tracking branch 'origin/main' into fix/pr-1301-maintainer 2026-09-07 23:39:51 +08:00
程序员阿江(Relakkes) 5f7c719c96 fix(ci): preserve coverage logs across reporter cleanup 2026-09-07 23:30:57 +08:00
程序员阿江(Relakkes) 4eb7ca42ac fix(ci): install native sidecar workspace dependencies 2026-09-07 23:27:37 +08:00
程序员阿江(Relakkes) bd5689dfad fix(ci): run macOS Swift checks on a required macOS lane 2026-09-07 23:23:32 +08:00
程序员阿江(Relakkes) c6146501cc fix(ci): capture coverage reports through regular file descriptors 2026-09-07 23:20:34 +08:00
程序员阿江(Relakkes) 8a4be0dee7 fix(ci): make runtime validation portable to Linux 2026-09-07 21:56:11 +08:00
程序员阿江(Relakkes) 9e4f83bbe3 test: stabilize PR validation and install server adapter dependencies 2026-09-07 21:41:31 +08:00
程序员阿江(Relakkes) b1b5a6d678 docs: switch community to WeCom and make Chinese README default
- Add 新功能支持 issue template, refine bug/question templates with a
  pointer to the user group
- Replace the Feishu user group QR with the WeChat Work group QR in both
  READMEs, include contact info for enterprise/Agent customization
- Make the Chinese README the default README.md, keep the English one as
  README.en.md, update language badges on both
- Update PR change policy, pr-triage docs set and site docs check for the
  renamed README files

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-06 15:57:41 +08:00
程序员阿江(Relakkes) 633320e9f2 fix(release): allow explicitly skipping Windows signing 2026-09-06 11:46:22 +08:00
程序员阿江(Relakkes) 783eed6be6 fix(release): reuse the prepared macOS signing keychain 2026-09-06 04:17:06 +08:00
程序员阿江(Relakkes) 8d8169ea70 fix(models): add GLM 5.3 reasoning capabilities (#1283) 2026-09-01 21:56:49 +08:00
程序员阿江(Relakkes) 0fd6904a95 merge: integrate Computer Use into local main 2026-09-01 20:14:24 +08:00
程序员阿江(Relakkes) 8a37865536 fix(computer-use): harden native macOS automation runtime 2026-09-01 20:06:25 +08:00
程序员阿江(Relakkes) 7858c46bd2 fix(models): preserve GPT relay reasoning effort (#1238) 2026-08-26 19:17:24 +08:00
Relakkes Yang a8cb0c509d test(policy): budget the full dead-import sweep 2026-08-23 19:55:11 +08:00
Relakkes Yang 5fafabfca1 fix(release): isolate draft validation from published assets 2026-08-23 19:36:21 +08:00
Relakkes Yang 417cfdfd69 fix(release): preserve Windows updater config before signing 2026-08-23 19:17:29 +08:00
Relakkes Yang c7a90f2cd1 fix(release): separate SignPath test and release policies 2026-08-23 18:55:10 +08:00
程序员阿江(Relakkes) dd4edc0efe merge: bring main into the computer-use worktree
main is 87 commits ahead and carries a large amount of fixed behaviour this
branch should not be re-deciding. The rule applied throughout: this worktree
owns Computer Use, main owns everything else.

Only 12 files were touched on both sides, and Git merged all of them without
reporting a conflict — but two of those silent merges were wrong, and neither
was visible until the checks ran.

`desktop/src/api/client.ts` ended up with two `apiGetBlob` implementations.
Both sides had independently hit the same problem (an `<img src>` pointed at an
API endpoint is a cross-origin subresource, so it carries no Authorization
header and the server's fetch-metadata policy refuses it) and both had written
the same fix. Git saw two additions in different places and kept both, which
does not even compile. main's version survives: it builds its headers through
the shared `buildHeaders()` rather than assembling them inline, so it inherits
whatever main adds there later.

`src/server/api/computer-use.ts` still imported `runtime/mac_helper.py` and
`runtime/requirements.txt` as compile-time text, both deleted on this branch.
Nothing at runtime referenced them, which is why the deletion looked clean; the
bundler resolves those imports when the server module is loaded, so the failure
surfaced only when the tests actually imported it. That path is now Windows-only
in the same sense the rest of the Python bridge is, and it also ships
`win_cursor_badge.py`, which the badge needs because it runs as its own process.

`computer-use-requirements.test.ts` drops its darwin half for the same reason —
the pins it guards still matter, but only one requirements file is left.

Verified: server 3869 tests / 331 files, desktop 4612 tests / 319 files
(lint + tsc + build), Swift 272 XCTest + 14 Swift Testing, Python 25.

Claude-Session: https://claude.ai/code/session_015j1yxxaoonyAS2iZ7qGnTS
2026-08-23 18:39:47 +08:00
Relakkes Yang af4454f38a feat(release): sign Windows artifacts with SignPath 2026-08-23 18:14:52 +08:00
程序员阿江(Relakkes) 27c31bdc15 fix(models): preserve GPT relay reasoning effort (#1238) 2026-08-23 17:21:53 +08:00
Relakkes Yang fef789f5a8 fix(quality-gate): stabilize Windows validation 2026-08-22 16:06:17 +08:00
程序员阿江(Relakkes) a0afcc7efa feat(desktop): add custom project display names 2026-08-11 02:38:55 +08:00
程序员阿江(Relakkes) ffa2b59105 docs(agents): reserve agent-browser for its committed lanes
The instruction files told every coding agent to reach for agent-browser
whenever a change needed browser-level evidence: copilot-instructions
listed "E2E or agent-browser smoke" as the remedy for cross-boundary
flows, and both contributing guides repeated it. That wording outlived
the tool. With the agent-browser skill uninstalled, agents still parsed
those lines as a recommendation and went looking for the binary instead
of using the browser skill that is actually installed.

Deleting the references would have made the docs wrong. agent-browser is
still a real dependency: check:desktop-ui-smoke spawns it on Linux CI,
and seven maintainer-run e2e scripts under desktop/scripts drive it
directly. It cannot be swapped for ego-browser either — ego lite is a
macOS-only GUI app with no headless mode and a one-time interactive
onboarding, so it cannot run on ubuntu-latest at all.

So the lanes keep the binary and the prose loses the recommendation.
agent-browser is now described as an implementation detail of those two
call sites, and ad-hoc browser work — manual verification, screenshots,
exploratory UI checks — is pointed at the ego-browser skill.

The quality contract asserted the old string, so it would have failed
closed on the reworded line. It now pins the replacement plus the new
routing rule; flipping either sentence turns the test red.
2026-08-09 19:03:50 +08:00
程序员阿江(Relakkes) 4314345b6c fix(quality-gate): drive the ProseMirror composer in the desktop smoke
The smoke waited on `textarea`, but the composer became a ProseMirror
contenteditable (MentionComposer) some time ago, so the selector could
never match. The lane hung for its full 30s timeout and failed on every
run — the screenshot it captured on the way out showed the app rendered
and idle, which is what makes this easy to misread as a UI break.

Target `[data-composer-editor]`, the attribute the editor puts on its
editable node and the same hook composerTestUtils already drives.
2026-08-09 02:47:12 +08:00
程序员阿江(Relakkes) 8e033e2890 fix(computer-use): restore the sidecar signing exclusion, and show app icons
Two things, in one commit because both touch `src/server/api/computer-use.ts`
and the halves cannot be split without rewriting the file twice.

## The regression

`desktop/package.json` had lost `"claude-sidecar-[^/]+$"` from `mac.signIgnore`.
That entry is not a hardening nicety — it is load-bearing for attestation:

  1. `build-sidecars.ts` signs the sidecar with an explicit
     `--identifier com.claude-code-haha.desktop.sidecar`
  2. `ClientAttestation.swift` compares that identifier EXACTLY in
     `validDesktopChain`
  3. without the exclusion, electron-builder re-signs the sidecar and drops the
     flag, so codesign derives the identifier from the file name
     (`claude-sidecar-aarch64-apple-darwin`), which never matches

Measured on the shipped 0.5.3 build: host and helper identifiers were correct,
the sidecar's was not. `validDesktopChain` backs BOTH `authorizeOneShot` and
`authorizeDaemon`, so this was not merely a broken permission probe — every
Computer Use call in that build failed closed with `unauthorized_client`.

The existing guard was a literal `toEqual` on the whole signIgnore array, which
does not survive an edit that changes the array and the expectation together —
exactly how the entry was lost. The replacement asserts the behaviour instead:
real sidecar file names must match some exclusion pattern, and the identifier
constants in `sign-identity.ts` and `ClientAttestation.swift` must agree.

`checkCuHelperPermissions` swallowed the failure into nulls, which the settings
page renders as a permanent "checking…" — indistinguishable from a probe still
in flight, and the only symptom this bug ever produced. It now records an
error-level diagnostic: the helper binary is present, so the user did nothing
wrong and the check still did not complete.

## App icons

Rows in the picker and the authorized list showed a letter tile. They now show
the application's own icon, resolved the way Finder does it: `CFBundleIconFile`
from Info.plist, the `.icns` under `Contents/Resources`, rasterised with `sips`.

`openTargetService` already does this, but its resolver is keyed on a
`TargetDefinition` and cannot answer for an arbitrary installed app, so the
path-only half lives in `macAppIcon.ts` and that service is left alone.

The endpoint takes a bundle id and resolves the path itself. There is
deliberately no parameter that names a file — it rasterises and returns bytes,
so its input surface is a security property, and a test drives paths at it.

Enumeration is shared across concurrent lookups: opening the picker fires one
icon request per visible row while the cache is still cold, and a plain
check-then-fill cache would walk every application root once per row.

macOS only, matching where this engine exists. The Windows list renders no icon
slot at all, and Linux is not a supported Computer Use platform.

## Verification

- 208 installed applications on the dev machine: 204 icons resolved; the 4
  misses are background bundles (Adobe sync extension, a URL handler, an
  updater, a token host) that ship no icon and correctly fall back
- check:server 3345 pass, desktop 4101 pass, check:policy 243 pass, lint clean
  (the 2 failures in each suite are `*.golden.test.ts`, pre-existing on main)
- mutation-checked that the new guards actually fail: removing the signIgnore
  entry, dropping the icon `onError` fallback, and breaking the in-flight share
  each turn a test red
2026-08-05 22:59:26 +08:00
程序员阿江(Relakkes) b8a90626ce feat(computer-use): native macOS engine, on top of main and nothing else
Rebuilt against main so the branch carries the Computer Use work and no other
divergence. Three unrelated efforts had been sitting uncommitted in this
worktree and were swept into an earlier commit; they are preserved on
cu-worktree-full-backup and belong on their own branches — adapter control
credentials, Electron asar sealing, and the sidecar code-loading audit. Every
file outside Computer Use now matches main exactly.

The engine
  A Swift helper drives apps through the accessibility tree, with coordinate
  actuation for the Chromium and Electron apps whose tree is a bare window
  frame. Ten primitives matching the shape Codex uses, so an app's guidance and
  the model's habits transfer.

  Coordinate actions resolve their target window once and refuse when none can
  be named. The unbound event they used to fall back to is discarded by custom
  renderers, so a minimized target produced a whole session of "Action
  completed" with nothing behind it.

  Input acceptance is established for typing and key presses as well as clicks:
  each MCP call is seconds apart, so the keyboard cannot inherit the focus a
  click established. The synthetic focus notification is gated on the target
  not already being active — sent unconditionally it names window 0 at an app
  that already owns a key window, and nine window-bound clicks were discarded
  with the traffic lights fully lit.

State the model can trust
  An off-screen target says so, and says which tools still reach it: element
  actions need no on-screen geometry, so an app with a real tree can still be
  driven from the Dock. A fully covered window is recovered once, then left
  alone — burying it again is the user wanting their screen back. A repeated
  capture is reported with the cause that actually applies rather than both,
  because coverage is something we compute.

Signing
  The helper is signed under a stable identity before electron-builder sees it,
  and excluded from re-signing: macOS ties Accessibility and Screen Recording
  grants to the signing identity, so rotating it drops both on every update.

Discoverability
  The desktop slash menu falls back to a directory scan while a session's CLI
  has not started, which is when the menu is first opened. Built-ins and
  bundled skills live in the binary, so /computer-use was absent until after
  the first message.
2026-08-05 22:06:23 +08:00
程序员阿江(Relakkes) 1aefc402ce build(policy): fix four lexer desyncs and extend the dead-import check to all of src
The check landed scoped to src/server/ws because blanking desynced on 6 of
2149 files and a desync reports a live import as dead. All four causes are
fixed, so the scope is now every source root no compiler checks — src,
scripts and adapters — and 0 of 2541 files desync.

The bugs, each with a regression test that places the import's only
reference after the construct so a desync makes it go dead:

- The token before a slash was read back out of the raw source, so the last
  word of a preceding comment decided whether `/` opened a regex. In
  useIssueFlagBanner.ts `// …correction tone` made the next line's regex lex
  as a division, and its apostrophe opened a string that ate the line. A
  comment is whitespace to the grammar; it now contributes nothing.
- That token accumulated across whitespace, so `return false` became one
  token named `returnfalse` and the following `return /re/` no longer looked
  like a keyword. This is what broke markdownImages.ts and dead-imports.ts
  itself.
- `input! / 10` divides but `!/re/.test(x)` negates, and both put `!` before
  the slash. What precedes the `!` settles it.
- Character classes and quotes inside regular expressions, fixed earlier.

desktop/ stays out of scope: its tsconfig already sets noUnusedLocals, and
scanning it anyway finds nothing — the cross-check that this agrees with a
real compiler. `blankingIsSound` still refuses to analyse a file whose
blanked form no longer parses, so a future desync is reported, not acted on.

Routing follows the scope: policyPrefixes now names adapters/, scripts/ and
src/ instead of the three scripts/ subdirectories and src/server/ws/. The
check reads these files rather than importing them, so the import graph
cannot select the lane on its own. `does not widen docs, policy, or coverage
lanes` split in two — its fixture selects the policy lane through its own
files now, so the dependent-must-not-widen invariant moved to a desktop
fixture that still shows it.

Verified by mutation, each reverted from an explicit backup: planting
`plantedProbeSymbol` into one file per root reported all four and failed
check:policy; reverting each of the three lexer fixes failed exactly its own
regression test; removing 'src/' from policyPrefixes failed the routing test
and made change-policy report policy=false for a src-only diff.

check:policy is 223 pass / 0 fail in 8.6s, up from 2s — the scan is 3.8s
over 2150 files and the planted-import test covers every one of them.
2026-08-04 20:42:45 +08:00
程序员阿江(Relakkes) a5fe04a485 build(policy): fail check:policy on dead imports under src/server/ws
Nothing checked src/ for unreferenced imports. desktop/tsconfig.json sets
noUnusedLocals and eslint covers desktop/ only; the root tsconfig.json sets
no such option and nothing installs typescript or bun-types at the root, so
no tool reads it at all. Splitting handler.ts left nine imports whose
symbols had moved out, and a human found them by reading the diff.

Measured before choosing. Under a temporary tsconfig extending the root
one, tsc reports 3225 errors over src/ and scripts/ before noUnusedLocals
and 3871 after — 646 net, on a baseline that already fails. That option
would land disabled, so this adds a narrow check instead: imports only,
one directory.

The analysis is lexical like module-graph.ts, but it blanks comments and
literal text first, so a symbol kept alive only by a comment still reports
dead — the exact shape the handler.ts split left behind. Template
substitutions, `//` inside a URL string and quotes inside a regex literal
must survive that blanking or a live import reads as dead;
src/utils/terminalShellEnvironment.ts is the last one and mis-lexing it
blanked 130 lines. Where blanking desyncs anyway the result no longer
parses, so the file is reported as degraded rather than mis-analysed: 6 of
2149 files repo-wide, none under src/server/ws.

src/server/ws/ also joins policyPrefixes. The check reads its files rather
than importing them, so the import graph cannot route a ws-only diff to
this lane, and without the prefix the check would never run on the diffs it
was written for.

Verified by mutation, each reverted from an explicit backup:
- planted `import { randomUUID }` into src/server/ws/events.ts →
  check:policy 216 pass / 1 fail, reporting
  "src/server/ws/events.ts:1 randomUUID"
- disabled line-comment blanking → 1 fail; block-comment blanking → 2 fail;
  regex-literal detection → 2 fail (the extra one is the desync guard)
- pointed DEAD_IMPORT_ROOTS at a missing directory → 1 fail, so the check
  cannot silently scan nothing
- dropped src/server/ws/ from policyPrefixes → 1 fail, and change-policy
  reports policy=false for a ws-only diff

check:policy is 219 pass / 0 fail; src/server/__tests__/websocket-handler
.test.ts is 87 pass / 0 fail.
2026-08-04 20:14:41 +08:00
程序员阿江(Relakkes) 0cea7b5a61 feat(quality-gate): drive the agent flow with a provider the user configured
`check:agent-flow` proves the protocol with the mock CLI, which is what makes it
CI-safe and lets any contributor run it with no credentials. It cannot prove the
thing this product actually is: a desktop agent talking to a real model. That can
only run where the credentials are, so this lane is local and manual by
construction — registered in no quality-gate mode, referenced by no workflow, and
live.test.ts fails if either changes.

Six scenarios, sharing the existing harness rather than a second copy of it:
first turn, permission allow, permission deny, interrupt, reconnect, and history
recovery. Prompts induce the behaviour instead of dictating it, and assertions
only look at protocol shape and side effects on disk — never at generated text —
so the lane passes on any provider, including a local one. The three flows left
out (api-error, tool-error, runtime-select) each carry a written reason, because
a silently missing flow reads as a covered one.

Spending someone's quota is the failure mode worth engineering against, so the
runner refuses to guess: no implicit fallback to the active provider, an ambiguous
selector is an error rather than a pick, and without --yes it prints the provider,
model and config path it would use and exits without sending anything. User state
is copied into a throwaway config dir and the real ~/.claude is fingerprinted
before and after — a run that writes to it fails loudly instead of being cleaned
up quietly.

Not yet run end to end: the local LM Studio endpoint answers 502 here, so the six
runners have only been verified for structure. Target resolution, the
confirmation gate, and lane placement are covered by 14 tests that need no
provider at all.
2026-08-04 17:15:29 +08:00
程序员阿江(Relakkes) 20b6e6dc9e ci: make the full quality sweep manual instead of nightly
4f9fec876 added this workflow with `cron: '0 18 * * *'`. That was the wrong call
to make unilaterally: the repository had no scheduled workflow at all before it,
so this was not one more cron among several but the introduction of recurring CI
spend — about ninety minutes per run — on a schedule nobody asked for.

The reasoning for the sweep still holds: a per-PR gate only covers what the diff
reaches, so it is blind to checks no recent PR selected and to failures that only
appear when the whole suite runs together. Keeping the workflow on
`workflow_dispatch` keeps that one click away without deciding for the maintainer
when to spend the time.

pr-quality-workflow.test.ts now asserts the absence of `schedule:` and `cron:`
rather than their presence, so a schedule cannot drift back in unnoticed —
verified by adding the cron back and watching the test go red. The docs' four-tier
table renames the tier accordingly; calling it "Nightly" when nothing runs nightly
is exactly the kind of comment that outlives its code.
2026-08-04 16:56:09 +08:00
程序员阿江(Relakkes) 537b48ba7b test(desktop): fail on unreachable components instead of covering them
Coverage answers "is this tested", never "should this exist", and the difference
cost real work. BackgroundTasksBar, SessionTaskBar and TeamStatusBar lost their
last import in 56a4be3d1 when SessionActivityPanel replaced them. Nothing noticed:
598b968ee then wrote tests for two of them — "cover the components left at zero" —
purely to lift changed-lines coverage past the gate, and c712f5285 restyled all
three during the UI redesign. 577 component lines plus 348 test lines were kept
alive for code no user could reach, along with nine translation keys carried in
five locales.

Delete all of it, and add the check that would have caught it: every .tsx under
src must be reachable by static import from a script tag in index.html or
gallery.html. Reading the entries out of the HTML rather than hardcoding them
means a new entry brings its whole subtree with it. The allowlist is empty and
should stay that way.

Scoped to .tsx deliberately. The .ts side has entry points a static graph cannot
see — workspaceDiffHighlight.worker.ts is a `new Worker(new URL(...))` target and
src/preview-agent/** is built into its own bundle — so covering it needs an
allowlist, which is where this kind of check goes to die.

Two mutations: putting BackgroundTasksBar.tsx back names it exactly, and breaking
ENTRY_HTML trips the entry-point guard first so the reader is not sent hunting
through 160 falsely-unreachable components.

Also drops the now-dangling desktop/src/mocks/ coverage exclusion, deleted in
33df50b9c.
2026-08-04 13:59:05 +08:00
程序员阿江(Relakkes) 4f9fec8760 ci(quality-gate): route checks by import graph and add offline agent QA 2026-08-03 21:46:54 +08:00
Relakkes Yang f332057b88 fix(windows): handle tab edits and missing Git Bash #1168
Preserve file indentation across tabs and spaces, route command tools to an available Windows shell, and make the source CLI launcher cross-platform.
2026-08-02 16:45:19 +08:00
Relakkes Yang 486fa1d1ff test(desktop): verify Unicode Windows install paths 2026-07-30 18:02:22 +08:00
程序员阿江(Relakkes) dda92e6deb feat(providers): import from cc-switch and fetch model lists
Two additions to the provider settings page, both modelled on cc-switch.

One-click import from cc-switch:
- Reads the local cc-switch installation and offers its Claude Code
  providers for bulk import. SQLite (cc-switch v3.8.0+) is the primary
  store, with the legacy v2 config.json as a fallback used only when no
  database exists — once cc-switch migrates it archives that file, so
  reading it alongside a database would surface pre-migration data.
- Supports cc-switch v3.1.0 and newer. Older installs wrote a config
  format cc-switch itself dropped in v3.6.0; those are refused explicitly
  rather than reported as an empty scan.
- Degrades honestly when cc-switch's storage moves: a structure we cannot
  read reports why, distinguishing "cc-switch too old" from "cc-haha does
  not recognise this layout" from "the file could not be read at all".
  Only id/app_type/settings_config are required; other columns are
  optional and unknown ones are ignored.
- Full credentials are resolved server-side during import and never
  appear in the scan payload.

Fetch model lists:
- Probes the provider's Base URL for an OpenAI-compatible /models
  endpoint, walking cc-switch's candidate ladder (version segments and
  nine vendor compat suffixes) and falling through on 404/405.
- Only http(s) endpoints are fetched; a 2xx that carries no model list is
  reported as a failure with the upstream's own message rather than as an
  empty catalog, so a key rejected behind a 200 is not read as "this
  provider has no models".
2026-07-30 00:19:06 +08:00
程序员阿江(Relakkes) 047f4fbfea docs: make the English README default with a zh-CN companion
The English README becomes README.md so the GitHub landing page reads in
English; the Chinese one moves to README.zh-CN.md. Both cross-link at the
top. Drops the source-origin framing from the intro and removes the
Disclaimer section, and points the license badge at MIT.

change-policy and pr-triage hardcoded README.en.md, which would have
dropped README.zh-CN.md out of the docs lane.
2026-07-29 09:23:11 +08:00
程序员阿江(Relakkes) 8bad67e590 fix(release): integrate RPM artifacts into desktop release 2026-07-28 00:51:10 +08:00
程序员阿江(Relakkes) c2cd615824 docs: rebuild the documentation site around two readers
The site had drifted from the product. Every screenshot predated the
v0.5.0 UI redesign, the reading experience shipped no search and no
syntax highlighting, and a third of the pages were internal process
artefacts — migration task lists addressed to agentic workers, a
release runbook, a proposal marked "historical".

Reorganise around the only two people who read this: someone getting
the desktop app running for the first time, and someone reading the
source. Five sections replace nine — start / desktop / im / cli /
internals — and the pages that served neither reader are gone.

Site rewrite:

- Palette lifted from the desktop app's 「纸·墨·印」 themes, so the
  site and the product read as one thing. Light mirrors 纯白, dark
  mirrors 墨夜, and dark mode exists at all now.
- Fonts are self-hosted. The old @import from Google Fonts is
  unreachable from mainland China, which left every heading in a
  fallback serif; it also only requested weight 600 while the CSS
  asked for 900, so Latin and CJK in the same heading disagreed.
- Docs were shipped as one 968KB manifest downloaded on every page
  view. Split into a 32KB index plus one lazily imported chunk per
  page; the entry bundle is now 101KB gzipped.
- Add search, syntax highlighting, per-route meta with canonical and
  hreflang, a sitemap, and an error boundary. Replace the 44vh
  mobile sidebar with a drawer.
- Image dimensions are read at build time and written into the tag,
  so lazy images reserve their space instead of collapsing.

Screenshots are recaptured from a real v0.5.0 build against a clean
demo project, with tokens, QR codes and paired accounts redacted.
The previous set is deleted rather than kept alongside.

Routes follow file paths, so the restructure would have broken every
inbound link; 37 old paths redirect, in both languages. The PR policy
gate and CODEOWNERS also hardcoded docs/guide/contributing.md.

Verified: check:docs 78 pages / 323 links / 0 problems, check:policy
127 pass. Walked every route at 1440 and 390 in both themes for
overflow, contrast, keyboard reachability and focus management.
2026-07-27 17:32:41 +08:00
程序员阿江(Relakkes) 9b88c5a527 fix(test): probe the surface the desktop process token actually guards
The Windows x64 build job went red on `Verify compiled Windows sidecar
startup`, asserting that a loopback request without
`CC_HAHA_LOCAL_ACCESS_TOKEN` returns 403 while it returned 200. Nothing
about x64 is involved — that step carries
`if: matrix.smoke_platform == 'windows' && matrix.arch == 'x64'`, so it is
the only job in the whole matrix that runs the smoke at all. Any regression
in this area can surface nowhere else.

The 200 is correct. `7d2a8a3cd keep loopback trusted without the desktop
process token` deliberately made the token additive again: gating every
local request behind it turned the Grok OAuth success page, `/preview-fs`
links and plain `curl` into 401s, because none of that traffic can ever
carry the token. Loopback is trusted on its own; the token is demanded only
on the `/api/h5-access` control plane, where another program on the same
box must not be able to publish the user's sessions to the network. The
assertion, written before that change, was still guarding the path that had
been intentionally opened.

So the probe moves to the boundary that is actually enforced, and gains a
positive assertion — loopback without a token must be 200 — so the additive
model is pinned down rather than merely no longer contradicted. Reverting to
the pre-`7d2a8a3cd` behaviour now fails the smoke instead of passing it.

Three copies of the stale assertion existed; all three are updated. Only the
compiled-sidecar smoke runs in CI, but `local-index-benchmark.ts` and its
corpus test were already failing the same way for anyone running them
locally. The benchmark also cancels the probe response bodies now: an unread
body holds its connection open, and that would land in the event-loop delay
and RSS samples taken immediately after.

Verified with the CI parameters — `bun run build:sidecars` then
`CC_HAHA_COMPILED_SIDECAR_SMOKE_STARTS=20 bun run test:compiled-sidecar-smoke`,
8/8 — and by running the benchmark directly, which now reports
`loopbackAuth` as 200/403/403/200 with validation intact.
2026-07-27 05:37:31 +08:00
程序员阿江(Relakkes) 598b968eec test(desktop): cover the components left at zero and exclude dev tooling
Independent QA on 3264db10 flagged changed-lines coverage at 86.17%
(5539/6428), under the 90% gate. Two causes, handled differently.

`desktop/src/dev/` joins `mocks/` and `types/` in the coverage exclusions.
It holds the component gallery — 260 of the 889 uncovered lines, and by
far the largest single contributor. Vite never bundles it (the build
input is `index.html` alone), and unit-testing a page whose whole job is
rendering every primitive would assert that the primitives render, which
their own tests already do. Excluding it is a scope correction, not a
threshold adjustment.

The rest are three components this branch touched that had no test file
at all. They now have one each, covering the behavior that changed:

- `BackgroundTasksBar` — drawer open/close including Escape, the running
  vs finished split, dismissed-key filtering, and that clearing reports
  every finished key while keeping the drawer open if work continues.
- `TeamStatusBar` — the progress bar's `aria-valuenow`, lead exclusion
  from both list and count, and that it greens on "nothing running"
  rather than on 100%: one completed plus one errored is done at 50%,
  which is why `tone="auto"` would have been wrong here.
- `MarketSkillDetail` — skeleton semantics, retry, install/uninstall by
  `installState`, and the disabled+spinner state mid-install.

Changed-lines coverage: 91.06% (5610/6161).

The QA report's second finding, `check:impact` blocking on a missing
`allow-cli-core-change` label, is an artifact of the branch trailing
main. `check:impact` diffs against `main`, so main's own newer commits —
9 files under `src/` — are counted as this branch's. Against the merge
base the same evaluator returns `areas: desktop, blocked: false`. No code
change here; the branch needs a rebase before it can pass that lane.
2026-07-26 18:31:34 +08:00
程序员阿江(Relakkes) c2fd674662 docs: rebuild documentation site with React 2026-07-23 20:46:33 +08:00
程序员阿江(Relakkes) 727bcea077 fix(desktop): harden Windows crash recovery #1064 #1065 #1071 2026-07-20 22:06:34 +08:00
程序员阿江(Relakkes) 36560c09d5 feat(pets): add interactive desktop companions 2026-07-20 19:20:18 +08:00
程序员阿江(Relakkes) 100a2b25b8 fix(release): remove environment-dependent alias fixture 2026-07-18 20:25:42 +08:00
程序员阿江(Relakkes) f8cccb096f fix(release): stabilize Windows recovery self-test 2026-07-18 20:20:34 +08:00
程序员阿江(Relakkes) c48171e2f0 fix(desktop): harden Windows upgrades (#1028, #1029, #1036) 2026-07-17 23:38:59 +08:00