Merge remote-tracking branch 'origin/main'

# Conflicts:
#	src/browser/client.ts
#	src/services/reddit-session-importer.ts
#	src/web/routes/conversations.ts
This commit is contained in:
jjbrunton committed 2026-08-08 20:17:19 +01:00
commit 48492d5238
30 files changed
+1041 -181

No files matched your search

+26 -19
View File
@@ -3,6 +3,18 @@ import type { Config } from '../config/index.js';
import type { Logger } from '../utils/logger.js';
import path from 'path';
import fs from 'fs';
import { z } from 'zod';
import type { RedditAccountIdentity } from '../services/account-auth-context.js';
const RedditAccountSchema = z.object({
name: z.string().trim().min(1),
id: z.string().trim().min(1),
}).passthrough();
const RedditIdentityResponseSchema = z.union([
RedditAccountSchema,
z.object({ data: RedditAccountSchema }).passthrough().transform(({ data }) => data),
]);
export interface BrowserRuntime {
launchPersistentContext(
@@ -115,17 +127,13 @@ export class BrowserClient {
/** Returns the account name from Reddit's authenticated identity endpoint. */
async getAuthenticatedUsername(): Promise<string | null> {
try {
return (await this.getRedditIdentity(await this.getPage()))?.username ?? null;
} catch {
return null;
}
return (await this.getAuthenticatedIdentity())?.username ?? null;
}
async getAuthenticatedIdentity(): Promise<{ username: string; accountId: string } | null> {
/** Returns the username and stable t2 account ID for identity binding. */
async getAuthenticatedIdentity(): Promise<RedditAccountIdentity | null> {
try {
const identity = await this.getRedditIdentity(await this.getPage());
return identity?.accountId ? { username: identity.username, accountId: identity.accountId } : null;
return await this.getRedditIdentity(await this.getPage());
} catch {
return null;
}
@@ -180,25 +188,24 @@ export class BrowserClient {
return (await this.getRedditIdentity(page)) !== null;
}
private async getRedditIdentity(page: Page): Promise<{ username: string; accountId: string | null } | null> {
private async getRedditIdentity(page: Page): Promise<RedditAccountIdentity | null> {
try {
return await page.evaluate(async () => {
const payload: unknown = await page.evaluate(async () => {
try {
const res = await fetch('https://www.reddit.com/api/me.json', { credentials: 'include' });
if (!res.ok) return null;
const data: unknown = await res.json();
if (!data || typeof data !== 'object') return null;
const account = 'data' in data && data.data && typeof data.data === 'object'
? data.data
: data;
if (!('name' in account) || typeof account.name !== 'string') return null;
const username = account.name.trim();
const accountId = 'id' in account && typeof account.id === 'string' ? account.id.trim() : '';
return username ? { username, accountId: accountId || null } : null;
return await res.json();
} catch {
return null;
}
});
const parsed = RedditIdentityResponseSchema.safeParse(payload);
if (!parsed.success) return null;
const rawAccountId = parsed.data.id;
return {
username: parsed.data.name,
accountId: rawAccountId.startsWith('t2_') ? rawAccountId : `t2_${rawAccountId}`,
};
} catch {
return null;
}