v0.0.7 - Fixed agent login flow: browser starts normally with GTK dialog, no blocking wait. Agent server is available throughout — agent can log in at any time (during dialog or after browser is running). Agent login takes priority over dialog if both occur.
This commit is contained in:
@@ -169,9 +169,11 @@ The browser embeds a WebSocket server (using libsoup) that allows external
|
||||
AI agents to control the browser programmatically. Connect to
|
||||
`ws://localhost:17777/agent` and send JSON tool commands.
|
||||
|
||||
The server starts **before** the login dialog, so an agent can authenticate
|
||||
without human interaction. If no agent logs in within a timeout (default
|
||||
30s, configurable in Settings), the GTK login dialog appears as fallback.
|
||||
The server starts before the login dialog and remains available throughout
|
||||
the browser's lifecycle. The browser starts normally with the GTK login
|
||||
dialog — no blocking wait. An agent can log in at any time: while the dialog
|
||||
is showing, or after the browser is already running. If an agent logs in
|
||||
while the dialog is open, the agent's login takes priority.
|
||||
|
||||
**Login tools** (available before login):
|
||||
|
||||
|
||||
+44
-40
@@ -60,7 +60,6 @@ typedef struct {
|
||||
static app_state_t g_state = {0};
|
||||
static GtkWindow *g_window = NULL;
|
||||
static gboolean g_logged_in = FALSE;
|
||||
static gboolean g_agent_login_done = FALSE;
|
||||
|
||||
/* ---- App state accessors (used by agent_login.c) ─────────────────── */
|
||||
|
||||
@@ -417,11 +416,14 @@ static gboolean on_key_press(GtkWidget *widget, GdkEventKey *event,
|
||||
|
||||
/* ---- Agent login callback ------------------------------------------ *
|
||||
* Called by agent_server when an agent successfully logs in via the
|
||||
* 'login' tool. Sets the flag so the main loop can proceed.
|
||||
* 'login' tool. The agent can log in at any time — while the GTK login
|
||||
* dialog is showing, or after the browser is already running. We just
|
||||
* set the flag; the do_login function checks it after the dialog
|
||||
* returns and skips the dialog's result if the agent already logged in.
|
||||
*/
|
||||
static void agent_login_callback(void) {
|
||||
g_agent_login_done = TRUE;
|
||||
g_print("[login] Agent login detected, proceeding.\n");
|
||||
g_logged_in = TRUE;
|
||||
g_print("[login] Agent login detected.\n");
|
||||
}
|
||||
|
||||
/* ---- Window destroy ------------------------------------------------- */
|
||||
@@ -444,15 +446,36 @@ static void on_window_destroy(GtkWidget *widget, gpointer data) {
|
||||
gtk_main_quit();
|
||||
}
|
||||
|
||||
/* ---- Login flow (GTK dialog fallback) ───────────────────────────── */
|
||||
/* ---- Login flow (GTK dialog) ─────────────────────────────────────── *
|
||||
* Shows the GTK login dialog. The agent server is already running at
|
||||
* this point, so an agent can call 'login' while the dialog is showing
|
||||
* (gtk_dialog_run runs a nested main loop that processes WebSocket
|
||||
* events). After the dialog returns, we check if the agent already
|
||||
* logged in — if so, we discard the dialog's result.
|
||||
*/
|
||||
|
||||
static int do_login(GtkWindow *parent) {
|
||||
/* nostr_init() is already called before this function. */
|
||||
login_result_t result;
|
||||
if (login_dialog_run(parent, &result) != 0) {
|
||||
/* Dialog was cancelled. But if the agent logged in while the
|
||||
* dialog was showing, proceed with the agent's login. */
|
||||
if (g_logged_in) {
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* If the agent already logged in while the dialog was showing,
|
||||
* discard the dialog's result and use the agent's login. */
|
||||
if (g_logged_in) {
|
||||
g_print("[login] Agent login took priority over dialog.\n");
|
||||
if (result.signer) {
|
||||
nostr_signer_free(result.signer);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
g_state.signer = result.signer;
|
||||
g_state.method = result.method;
|
||||
strncpy(g_state.pubkey_hex, result.pubkey_hex, 64);
|
||||
@@ -486,9 +509,11 @@ int main(int argc, char **argv) {
|
||||
g_signal_connect(window, "key-press-event", G_CALLBACK(on_key_press), NULL);
|
||||
g_window = GTK_WINDOW(window);
|
||||
|
||||
/* Start the agent server before login so agents can authenticate
|
||||
* without human interaction. The server runs on the configured port
|
||||
* (default 17777). If disabled in settings, skip it. */
|
||||
/* Start the agent server before login. The server runs on the
|
||||
* configured port (default 17777) and is available throughout the
|
||||
* entire browser lifecycle — an agent can log in at any time, even
|
||||
* while the GTK login dialog is showing or after the browser is
|
||||
* already running. If disabled in settings, skip it. */
|
||||
const browser_settings_t *s = settings_get();
|
||||
if (s->agent_server_enabled) {
|
||||
if (agent_server_start(s->agent_server_port) == 0) {
|
||||
@@ -507,40 +532,19 @@ int main(int argc, char **argv) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Login flow: wait for agent login or fall back to GTK dialog.
|
||||
* If the agent server is running, wait up to agent_login_timeout_ms
|
||||
* for an agent to call the 'login' tool. If no agent connects or
|
||||
* the timeout expires, show the GTK login dialog. */
|
||||
if (s->agent_server_enabled && s->agent_login_timeout_ms > 0) {
|
||||
g_print("[login] Waiting up to %d ms for agent login...\n",
|
||||
s->agent_login_timeout_ms);
|
||||
/* Set up the login callback so if an agent calls 'login' while the
|
||||
* GTK dialog is showing, we can close the dialog and proceed. */
|
||||
agent_server_set_login_callback(agent_login_callback);
|
||||
|
||||
/* Run a main loop with a timeout so the agent server can process
|
||||
* incoming WebSocket connections and login commands. */
|
||||
g_agent_login_done = FALSE;
|
||||
agent_server_set_login_callback(agent_login_callback);
|
||||
|
||||
GMainContext *ctx = g_main_context_get_thread_default();
|
||||
gint64 deadline = g_get_monotonic_time() +
|
||||
(gint64)s->agent_login_timeout_ms * 1000;
|
||||
|
||||
while (!g_agent_login_done) {
|
||||
gint64 remaining = deadline - g_get_monotonic_time();
|
||||
if (remaining <= 0) break;
|
||||
|
||||
/* Process events for up to 200ms at a time. */
|
||||
g_main_context_iteration(ctx, FALSE);
|
||||
if (!g_agent_login_done) {
|
||||
g_usleep(50 * 1000); /* 50ms */
|
||||
}
|
||||
}
|
||||
|
||||
agent_server_set_login_callback(NULL);
|
||||
}
|
||||
|
||||
/* If not logged in via agent, show the GTK login dialog. */
|
||||
/* Show the GTK login dialog immediately — no blocking wait.
|
||||
* The dialog runs a nested GTK main loop (gtk_dialog_run) which
|
||||
* processes WebSocket events, so the agent server is live during
|
||||
* the dialog. If an agent calls 'login' while the dialog is open,
|
||||
* the callback fires and we close the dialog.
|
||||
*
|
||||
* If the agent logs in before the dialog appears (unlikely but
|
||||
* possible), skip the dialog entirely. */
|
||||
if (!g_logged_in) {
|
||||
g_print("[login] No agent login, showing GTK dialog.\n");
|
||||
if (do_login(g_window) != 0) {
|
||||
g_print("[login] Cancelled, exiting.\n");
|
||||
agent_server_stop();
|
||||
|
||||
+2
-2
@@ -11,9 +11,9 @@
|
||||
#ifndef SOVEREIGN_BROWSER_VERSION_H
|
||||
#define SOVEREIGN_BROWSER_VERSION_H
|
||||
|
||||
#define SB_VERSION "v0.0.6"
|
||||
#define SB_VERSION "v0.0.7"
|
||||
#define SB_VERSION_MAJOR 0
|
||||
#define SB_VERSION_MINOR 0
|
||||
#define SB_VERSION_PATCH 6
|
||||
#define SB_VERSION_PATCH 7
|
||||
|
||||
#endif /* SOVEREIGN_BROWSER_VERSION_H */
|
||||
|
||||
Reference in New Issue
Block a user