tools/nomad_client.py
Python client. Zero dependencies.
Pure stdlib. Talks bincode over /tmp/nomad-daemon.sock. Semantic queries by role and intent — stable across page re-renders, ready for LLM reasoning loops.
- +navigate(url, fmt=4) → Summary, ~150 B
- +find({role, tag, intent, text_contains, limit})
- +click(id) · input(id, value) · evaluate(js)
- +compare_tabs(a, b) · transfer_tab(from, to)
- +get_action_log() → replayable history
- +MCP server bundled · Claude / Cline ready
agent_loop.py
from nomad_client import NomadClient# socket-bound session, auto-cleanupasync with NomadClient() as c:page = c.navigate("https://news.ycombinator.com", fmt=4) # Summary, ~150 Bprint(page.title, len(page.entities))# semantic query — stable across re-rendersbtn = c.find({"role": "button", "text_contains": "more"})[0]c.click(btn.id)# replayable audit trail for LLM reasoninglog = c.get_action_log()
@nomad/client
Typed TS over fetch.
Drop-in client for browser or Node. Every response is typed against NomadResponse<T> so your IDE knows the envelope shape — including the live meta.daemon_rss_kbyou'll want to render in a resource indicator.
- +Single class · ~80 LOC including types
- +setToken() persists Bearer auth across calls
- +navigate · click · input · find · evaluate
- +snapshot · action-log · compare-tabs · transfer-tab
- +stats · health · formats — for dashboard widgets
- +Works in Cloudflare Workers, Edge, Node 18+
agent.ts
import { NomadApiClient } from "@nomad/client";
const nomad = new NomadApiClient("https://api.nomad.dev");
// Optional auth
const { data } = await nomad.login("sk-...");
nomad.setToken(data.token);
// Navigate, find, click
const page = await nomad.navigate("https://news.ycombinator.com", "summary");
const [more] = (await nomad.find({ role: "button", text_contains: "more" })).data;
await nomad.click(more.id);
// Live resource indicators
const stats = await nomad.stats();
console.log(stats.data.daemon.rss_mb, stats.data.backend.requests_per_second);