[eric] browser: borrow the user's existing browser sign-in at a login wall (opt-in)

This commit is contained in:
ciregenz
2026-07-27 14:01:05 -07:00
parent 404ca418a3
commit 514aef45d6
12 changed files with 793 additions and 9 deletions
@@ -3,7 +3,9 @@ import Box from '@mui/material/Box';
import Typography from '@mui/material/Typography';
import Button from '@mui/material/Button';
import Dialog from '@mui/material/Dialog';
import Switch from '@mui/material/Switch';
import TextField from '@mui/material/TextField';
import type { AppSettings } from '@/shared/state/settingsSlice';
import { useClaudeTokens } from '@/shared/styles/ThemeContext';
import { API_BASE } from '@/shared/config';
import type { SettingsStyles } from '../settingsStyles';
@@ -11,7 +13,11 @@ import type { SettingsStyles } from '../settingsStyles';
const ERASE_WORD = 'ERASE';
// The iOS Reset menu, two actions only: "Reset All Settings" (preferences back to defaults, your stuff + sign-in stay) and "Erase All Content and Settings" (factory wipe + relaunch). Flat rows, not a boxed "danger zone": red lives only on the destructive label, and the real friction is the typed-confirm in the dialog.
const DataPrivacySection: React.FC<{ styles: SettingsStyles }> = ({ styles }) => {
const DataPrivacySection: React.FC<{
form: AppSettings;
setForm: React.Dispatch<React.SetStateAction<AppSettings>>;
styles: SettingsStyles;
}> = ({ form, setForm, styles }) => {
const c = useClaudeTokens();
const { sectionSx, labelSx, descSx } = styles;
@@ -122,6 +128,21 @@ const DataPrivacySection: React.FC<{ styles: SettingsStyles }> = ({ styles }) =>
<Button variant="outlined" size="small" onClick={() => { setErr(null); setResetOpen(true); }} sx={rowBtnSx}>Reset</Button>
</Box>
<Box sx={{ ...rowSx, borderBottom: `1px solid ${c.border.subtle}` }}>
<Box sx={{ mr: 3 }}>
<Typography sx={labelSx}>Use my sign-ins from my other browser</Typography>
<Typography sx={descSx}>When an agent hits a site you're not signed into here, borrow the sign-in you already have in Chrome, Arc, Brave, or Edge instead of stopping to ask you. Reads only the site it's stuck on, and never asks for a password. Off by default.</Typography>
</Box>
<Switch
checked={form.browser_import_signins}
onChange={(e) => setForm({ ...form, browser_import_signins: e.target.checked })}
sx={{
'& .MuiSwitch-switchBase.Mui-checked': { color: c.accent.primary },
'& .MuiSwitch-switchBase.Mui-checked + .MuiSwitch-track': { bgcolor: c.accent.primary },
}}
/>
</Box>
<Box sx={{ ...rowSx, borderBottom: `1px solid ${c.border.subtle}` }}>
<Box>
<Typography sx={labelSx}>Clear browsing data</Typography>
@@ -43,7 +43,7 @@ const GeneralTab: React.FC<{
<GeneralAdvanced form={form} setForm={setForm} styles={styles} />
<DataPrivacySection styles={styles} />
<DataPrivacySection form={form} setForm={setForm} styles={styles} />
</Box>
);
@@ -1867,6 +1867,22 @@ async function handleSessionCookies(params: Record<string, any>): Promise<Record
}
}
// Load the user's own existing sign-in for a site into the browser partition, so an agent stuck at
// a login wall can carry on as them instead of interrupting to ask for a password. Like the cookie
// bridge above this needs no webview: it writes straight to the main-process cookie store.
async function handleImportSession(params: Record<string, any>): Promise<Record<string, any>> {
const bridge = (window as any).openswarm?.setPartitionCookies as
| ((domain: string, cookies: Record<string, any>[]) => Promise<{ ok: boolean; set: number; error?: string }>)
| undefined;
if (!bridge) return { ok: false, set: 0, error: 'Session import unavailable (desktop app only)' };
const cookies = Array.isArray(params.cookies) ? params.cookies : [];
try {
return await bridge(String(params.domain || ''), cookies);
} catch (err: any) {
return { ok: false, set: 0, error: `Session import failed: ${err?.message || String(err)}` };
}
}
// Drive a session-borrow site's own already-open card: resolve the webview by its live domain
// (no browser_id, like the cookie bridge), then run a small navigate/evaluate step sequence.
// The shims use this for writes on sites that sign every HTTP request (TikTok).
@@ -1905,6 +1921,11 @@ async function runBrowserCommand(
dashboardWs.send('browser:result', { request_id, ...result });
return;
}
if (action === 'import_session') {
const result = await handleImportSession(params);
dashboardWs.send('browser:result', { request_id, ...result });
return;
}
const wv = await awaitWebview(browser_id, tab_id || undefined);
if (!wv) {
dashboardWs.send('browser:result', {
@@ -51,6 +51,7 @@ export interface AppSettings {
openrouter_api_key?: string | null;
custom_providers?: CustomProvider[];
browser_homepage: string;
browser_import_signins: boolean;
auto_select_mode_on_new_agent: boolean;
expand_new_chats_in_dashboard: boolean;
auto_reveal_sub_agents: boolean;
@@ -127,6 +128,7 @@ export const DEFAULT_SETTINGS: AppSettings = {
new_agent_shortcut: 'Meta+l',
anthropic_api_key: null,
browser_homepage: 'https://duckduckgo.com',
browser_import_signins: false,
auto_select_mode_on_new_agent: false,
expand_new_chats_in_dashboard: true,
auto_reveal_sub_agents: true,