feat(sites): site requests follow browser UA, smooth UA switch, CF cookies browser-only (BR-4)

- every outbound site request (adapter list/search/detail/download/health,
  RSS, login, user data) carries the account UA via SourcesRuntime.siteFetch;
  no more Deno default UA
- priority: account manual > pending > global setting > browser identity
  (fallback: same-shape Windows Firefox UA); settings UA default is empty
- migration 0056: existing cookie/password accounts keep their old UA as
  ua_pending until re-login or browser cookie write-back switches cookie+UA
  in one transaction; browser_provider.identity caches the browser identity
- cf_clearance/__cf_bm stripped on browser write-back (names kept only)
- browser sessions get per-site egress (same proxy as HTTP, or direct)
- site account page shows UA source; raw UA in tech info

Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
rootandClaude Opus 5.5 committed 2026-09-30 03:17:13 +08:00
1 parent ebd07c9230
commit adc853aa64
21 files changed
+955 -72

No files matched your search

@@ -114,12 +114,16 @@ function open(
*/
export function createSecretStore(deps: { db: Kysely<DB>; masterKey: string }): SecretService {
const { db, masterKey } = deps;
const replace = async (secretRef: string, plaintext: string) => {
const replace = async (secretRef: string, plaintext: string, exec: Kysely<DB> = db) => {
assertRef(secretRef);
const row = await db.selectFrom("secret").select(["id", "purpose"]).where("id", "=", secretRef).executeTakeFirst();
const row = await exec
.selectFrom("secret")
.select(["id", "purpose"])
.where("id", "=", secretRef)
.executeTakeFirst();
if (!row) throw new SecretError("not_found", secretRef);
const s = seal(masterKey, row.id, row.purpose, plaintext);
await db
await exec
.updateTable("secret")
.set({
ciphertext: s.ciphertext,