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-cleanup
async with NomadClient() as c:
page = c.navigate("https://news.ycombinator.com", fmt=4) # Summary, ~150 B
print(page.title, len(page.entities))
 
# semantic query — stable across re-renders
btn = c.find({"role": "button", "text_contains": "more"})[0]
c.click(btn.id)
 
# replayable audit trail for LLM reasoning
log = 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);