Commit Graph
84 Commits
Author SHA1 Message Date
jjbrunton baf673904c Merge branch 'codex/react-foundation' into main (changes already integrated) 2026-08-08 16:46:39 +01:00
jjbrunton 50985b52f1 Merge branch 'codex/native-login-handoff' into main
# Conflicts:
#	public/index.html
#	src/bootstrap.ts
#	src/services/chat/service.ts
#	tests/chat-service-username-persist.test.ts
2026-08-08 16:46:16 +01:00
jjbrunton dbb1842ef4 Use native Chrome for Reddit login handoff 2026-08-08 16:16:11 +01:00
jjbrunton ec0cd1318b feat(dashboard): complete React cutover 2026-08-08 16:11:57 +01:00
jjbrunton 2c533fa52a Verify Reddit identity before Matrix capture 2026-08-08 16:03:54 +01:00
jjbrunton 2fd92bf82c Gate bot logic on full account authentication 2026-08-08 16:01:06 +01:00
jjbrunton d5f0175dc2 feat(web): wire domain events into realtime stream 2026-08-08 16:00:17 +01:00
jjbrunton 8c8ff11fd7 feat(web): publish bot control transitions 2026-08-08 15:51:58 +01:00
jjbrunton ad770a792a feat: secure React dashboard cutover 2026-08-08 15:51:06 +01:00
jjbrunton a51e0b6b31 feat(web): share realtime hub across domain events 2026-08-08 15:46:25 +01:00
jjbrunton 66a1146562 feat(dashboard): activate routed media filters 2026-08-08 15:43:53 +01:00
jjbrunton d108903174 Harden dashboard realtime state and controls 2026-08-08 15:41:58 +01:00
jjbrunton 4b3f00729f feat(dashboard): close legacy parity gaps 2026-08-08 15:41:34 +01:00
jjbrunton 4db6de8bf5 Stop Matrix refresh loop when login is unavailable 2026-08-08 15:41:02 +01:00
jjbrunton 92287c0a9a Stop Matrix refresh loop when login is unavailable 2026-08-08 15:40:55 +01:00
jjbrunton 12a853fa42 docs: describe React dashboard migration state 2026-08-08 15:32:38 +01:00
jjbrunton 720e5e3e34 test: include shared contract suite in root gate 2026-08-08 15:31:44 +01:00
jjbrunton 674cd89cd7 Use full Chrome for interactive Reddit login 2026-08-08 15:30:24 +01:00
jjbrunton 70d62b2da3 feat(dashboard): route settings and access 2026-08-08 15:26:40 +01:00
jjbrunton 79de6aa2a1 feat: add settings and access dashboard slice 2026-08-08 15:25:34 +01:00
jjbrunton dcf2a1200c Clear stale Chromium profile locks on startup 2026-08-08 15:22:20 +01:00
jjbrunton 364b4db200 docs: document dashboard auth and typed events 2026-08-08 15:20:31 +01:00
jjbrunton 271f2d1fb5 Handle non-string image content types 2026-08-08 15:18:28 +01:00
jjbrunton 51438aa5db Keep x11vnc alive for remote login 2026-08-08 15:16:51 +01:00
jjbrunton 89f7376b1b fix(dashboard): deduplicate realtime log replay 2026-08-08 15:15:54 +01:00
jjbrunton e003b0d8b2 feat(dashboard): route performance and operations 2026-08-08 15:11:48 +01:00
jjbrunton 5917b63755 feat(dashboard): add performance and operations slices 2026-08-08 15:10:52 +01:00
jjbrunton 20fa83f245 feat(dashboard): route conversations and media 2026-08-08 15:06:19 +01:00
jjbrunton 768470204b feat: add private media dashboard slice 2026-08-08 15:05:30 +01:00
jjbrunton 8d4eae7ab5 feat(dashboard): add conversations workspace 2026-08-08 15:05:08 +01:00
jjbrunton 40e5f4c818 chore(deps): apply audited dependency fixes 2026-08-08 15:02:23 +01:00
jjbrunton 9919b12aab feat(web): bridge logs to typed event stream 2026-08-08 15:00:02 +01:00
jjbrunton d6d29badcd feat(dashboard): align contracts and production build 2026-08-08 14:58:27 +01:00
jjbrunton 69d560d5c5 feat(web): add Express SSE event adapter 2026-08-08 14:56:37 +01:00
jjbrunton 9f4f198800 fix: align realtime event contracts 2026-08-08 14:54:09 +01:00
jjbrunton 51e3e28a9b feat(dashboard): add React foundation and overview 2026-08-08 14:53:53 +01:00
jjbrunton 28fcb3ea99 feat(dashboard): add React foundation and overview 2026-08-08 14:52:39 +01:00
jjbrunton 59c3a61a88 feat: add shared dashboard API contracts 2026-08-08 14:52:06 +01:00
jjbrunton 939cf408a0 feat(web): add typed realtime event core 2026-08-08 14:50:15 +01:00
jjbrunton 78e84fe36b Fix noVNC WebSocket routing 2026-08-08 14:48:58 +01:00
jjbrunton 316dc3dd3e Show Reddit recovery status in dashboard 2026-08-08 14:42:19 +01:00
jjbrunton d2b3a1b31d Retry Matrix capture after chat login redirect 2026-08-08 14:34:10 +01:00
jjbrunton bffda95251 Improve remote Matrix token refresh 2026-08-08 14:30:40 +01:00
jjbruntonandClaude Opus 4.7 b9df8459e3 Atomic chat-cache writes + stop tests clobbering prod cache
Three intertwined fixes for the torn-write that corrupted the prod
data/chat-cache.json mid-session:

  - ChatCache.save / persistCooldown now write via temp file + rename.
    rename(2) is atomic on the same filesystem, so a crashed or
    concurrent writer can never leave NULs in the middle of the file.
    A small serialising write queue stops two concurrent saves from
    racing to the same temp name.

  - ChatCache throws if constructed without a filePath under NODE_ENV=test.
    The old default (./data/chat-cache.json) silently routed test
    writes into the production cache — exactly how the corruption
    happened. Future test that forgets to pass a tmp path fails loudly
    instead of stomping data.

  - Cooldown / delta-media / username-persist tests now pass an
    explicit tmp ChatCache so the new guard is satisfied.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 23:48:33 +01:00
jjbruntonandClaude Opus 4.7 71548ec2ad Resolve invitees via /state and accept 'invite' membership
For chats where we sent an opening message but the recipient never
accepted, /sync drops the room with no heroes and the recipient is
in state as m.room.member with membership:'invite' — not 'join'.
chat.reddit.com's web UI labels these rooms with the invitee anyway,
because it reads the full room state.

  - roomToActiveChat now accepts 'invite' membership in addition to
    'join' when scanning member-join state events.
  - enrichUsernames pass 2 hits /_matrix/client/v3/rooms/{id}/state
    first (cheap, returns all member events including invites) and
    only falls back to /messages history if /state yields nothing.
  - Added MatrixClient.getRoomFullState to back that lookup.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 23:41:31 +01:00
jjbruntonandClaude Opus 4.7 cf7fc41761 Fix username resolver: use www.reddit.com, not api.reddit.com
api.reddit.com/api/user_data_by_account_ids returns 404 without an
OAuth token — which is why every chat in the dashboard sat on
t2_xxx or unknown. The same path on www.reddit.com (with a .json
suffix) is served unauthenticated and returns the expected user
map. Switch the resolver to that host so enrichUsernames actually
succeeds.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 23:30:01 +01:00
jjbruntonandClaude Opus 4.7 01ea8fd36b Mine t2 IDs from timeline events and /messages history
Reddit's homeserver does not expose /_matrix/client/v3/rooms/{id}/joined_members
(it returns 'page not found'), so the previous fallback was always a 404.
Two replacements:

  - roomToActiveChat: include timeline event senders as candidate MXIDs
    alongside heroes / member-join state. /sync omits heroes for idle
    rooms but always carries full sender MXIDs in any timeline events
    it does return, so we recover a t2 ID whenever the delta sees a
    message.

  - enrichUsernames: split into three passes — apply known mappings
    first (so re-created delta chats get upgraded without a restart),
    then page /messages 'b' once per still-unknown room to mine a t2
    from the recipient's history, then resolve any remaining raw t2s
    via the bulk user_data endpoint. Failed history lookups are cached
    in unresolvableRooms so we don't retry every sync.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 23:15:27 +01:00
jjbruntonandClaude Opus 4.7 1df7b680ee Stop re-attempting /joined_members on rooms we can't read
Reddit's homeserver returns 404 from /_matrix/client/v3/rooms/{id}/joined_members
for rooms we've left, been kicked from, or never fully joined — which is
roughly every chat that wasn't accepted by the recipient. The previous
enrichUsernames pass retried these on every delta sync, flooding the
dashboard log with debug-level 404s. Track failures in an in-memory
unresolvable set so each room is tried at most once per process.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 23:06:33 +01:00
jjbruntonandClaude Opus 4.7 ad5c63fc50 Fall back to /joined_members when delta sync omits the other party
Reddit's homeserver sometimes returns a room in /sync with no
m.heroes and no joined m.room.member state events, leaving us with
no t2 ID to resolve and the chat stuck on 'unknown'. enrichUsernames
now hits /_matrix/client/v3/rooms/{roomId}/joined_members for any
chat whose username is 'unknown' or a bare MXID, stamps the t2 ID,
and lets the existing resolver fill in the real Reddit username.

Also treats a cached 'unknown' username as stale in roomToActiveChat
so subsequent deltas re-attempt the lookup instead of pinning it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 23:01:06 +01:00
jjbruntonandClaude Opus 4.7 2edbc7dbf7 Resolve t2 usernames after every delta sync
enrichUsernames() was only invoked once at startup, so any rooms that
arrived via a later delta /sync stayed pinned to their raw t2_xxx
identifier — which the dashboard then rendered as a bare Matrix-style
ID. Call enrichUsernames() at the tail of syncOnce so new rooms get
resolved to real Reddit usernames before the UI reads them.

Also pass the unformatted username to openConversation() and format
the modal title in the frontend, so lookups by username keep working
after the display sanitisation.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 22:52:04 +01:00