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
This commit is contained in:
jjbrunton committed 2026-08-08 16:46:16 +01:00
commit 50985b52f1
28 files changed
+987 -124

No files matched your search

+5
View File
@@ -5,6 +5,11 @@ export function createControlRouter(ctx: WebContext): Router {
const router = Router();
router.post('/start', (_req, res) => {
if (!ctx.services.accountAuth?.isFullyAuthenticated()) {
ctx.logger.warn('Bot start blocked until full account authentication');
res.status(409).json({ success: false, running: false, authenticated: false });
return;
}
ctx.setBotRunning(true);
ctx.activity.push('sent', 'Bot started by user');
ctx.logger.info('Bot started via web UI');
+24 -15
View File
@@ -18,36 +18,45 @@ export function createLoginRouter(ctx: WebContext): Router {
router.get('/login/status', async (_req, res) => {
const browser = ctx.services.browser;
if (!browser) {
const accountAuth = ctx.services.accountAuth;
if (!browser || !accountAuth) {
res.status(503).json({ ready: false, error: 'browser not initialised' });
return;
}
try {
const loggedIn = await browser.isSessionValid();
res.json({ ready: true, loggedIn, status: browser.getSessionState() });
const loggedIn = await accountAuth.ensureFullyAuthenticated();
const browserState = browser.getSessionState();
res.json({
ready: true,
loggedIn,
status: loggedIn
? 'authenticated'
: browserState === 'unreachable'
? 'unreachable'
: 'login-required',
});
} catch (error) {
ctx.logger.error('Failed to check login status:', error);
res.status(500).json({ ready: true, loggedIn: false, error: 'check failed' });
}
});
// Navigate the headed Chromium running under Xvfb to reddit.com/login so
// the human watching the noVNC stream can complete the flow.
// Restart into a completely native Chrome process. The container supervisor
// does not enable DevTools until the human explicitly completes the handoff.
router.post('/login/start', async (_req, res) => {
const browser = ctx.services.browser;
if (!browser) {
res.status(503).json({ ok: false, error: 'browser not initialised' });
const nativeLogin = ctx.services.nativeLogin;
if (!nativeLogin) {
res.status(503).json({ ok: false, error: 'native login unavailable' });
return;
}
try {
const page = await browser.getPage();
await page.bringToFront().catch(() => undefined);
await page.goto('https://www.reddit.com/login', { waitUntil: 'domcontentloaded' });
ctx.logger.info('Opened Reddit login page for human completion via VNC');
res.json({ ok: true });
ctx.services.accountAuth?.invalidate('Native Reddit login requested');
nativeLogin.request();
ctx.logger.info('Restarting into native Chrome login mode; bot logic remains locked');
res.status(202).json({ ok: true, restarting: true });
} catch (error) {
ctx.logger.error('Failed to open login page:', error);
res.status(500).json({ ok: false, error: 'navigation failed' });
ctx.logger.error('Failed to start native login mode:', error);
res.status(500).json({ ok: false, error: 'handoff failed' });
}
});