Skip to content

Latest commit

 

History

11 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

OpenMonitor

A durable research agent.

Always-on web searches that notify you when new results show up. Like Exa Monitors or Parallel Monitor, except it runs as a serverless agent on your own OpenComputer account and uses whichever search API you already pay for.

  • Vendor-neutral. Exa, Parallel, Tavily, Brave, Serper, Firecrawl, Jina, Perplexity, Linkup, You.com, Kagi, Valyu, Monid. One setting switches.
  • One key. Only the secret for the provider named in SEARCH_PROVIDER is required. The other twelve connections are registered but never fetched.
  • Your keys never enter the runtime. They are managed secrets attached at the outbound edge, never visible to the agent or its prompt.
  • Delivery to a webhook (JSON, one POST per watch) and/or Slack.
  • Every refresh is a durable session you can open in the dashboard.

Deploy

Deploy to OpenComputer

or from a clone:

git clone https://github.com/diggerhq/openmonitor.git
cd openmonitor
npm install
npm run opencomputer -- login
printf %s exa | npm run opencomputer -- env set SEARCH_PROVIDER --value-stdin
printf %s "$EXA_API_KEY" | npm run opencomputer -- secrets set EXA_API_KEY --value-stdin
npm run deploy -- --watch

Then run it once from another terminal:

npm run session -- "Refresh every watch with a 24 hour lookback."

Watches

Edit opencomputer/agents/monitor/watches.ts. Each watch is a query and an optional criteria sentence. With criteria, the agent reads every result against it and drops the rest. Without, everything new is delivered.

{
  id: "opencomputer-mentions",
  query: "OpenComputer serverless agents",
  criteria: "Mentions of OpenComputer (opencomputer.dev), the agent platform. Not 'open computer' hardware.",
}

The schedule in schedules/refresh.ts runs daily at 09:00 UTC with a 24 hour lookback. Change the cron and lookback_hours together.

Switch provider

printf %s parallel | npm run opencomputer -- env set SEARCH_PROVIDER --value-stdin
printf %s "$PARALLEL_API_KEY" | npm run opencomputer -- secrets set PARALLEL_API_KEY --value-stdin
Provider SEARCH_PROVIDER Secret Date filter
Exa exa EXA_API_KEY startPublishedDate
Parallel parallel PARALLEL_API_KEY source_policy.after_date
Tavily tavily TAVILY_API_KEY start_date
Brave Search brave BRAVE_API_KEY freshness range
Serper (Google) serper SERPER_API_KEY tbs
Firecrawl firecrawl FIRECRAWL_API_KEY tbs
Jina Reader jina JINA_API_KEY client-side on publishedTime
Perplexity Search perplexity PERPLEXITY_API_KEY search_after_date_filter
Linkup linkup LINKUP_API_KEY fromDate
You.com you YOU_API_KEY freshness range
Kagi kagi KAGI_API_KEY client-side on published
Valyu valyu VALYU_API_KEY start_date
Monid monid MONID_API_KEY client-side; see providers/monid.ts

Not included: SerpApi and Google Custom Search, because they take the key as a query parameter and managed secrets are only injected into headers.

Why only one key is required

  • Every provider module declares its own connection with a useSecret reference, so all thirteen connections are registered at deploy. Secrets are resolved at outbound dispatch, not at deploy, so unset secrets never block a deployment. opencomputer doctor warns about them and nothing else.
  • SEARCH_PROVIDER selects one provider at render time. Only that provider's connection is ever fetched; the other twelve are inert.
  • If the selected provider's secret is unset, the platform rejects the outbound request at the edge with a "secret missing or not allowed for this connection" error. search_watch surfaces it, the agent stops before trying another provider, and the run report names the exact secrets set command. This is enforced by the platform, not by a check in this code.
  • The template manifest marks no secret as required. The deploy form shows all thirteen, enforces none, and requires only SEARCH_PROVIDER.
  • Switching is two commands: set the new secret, change the variable. The old key can stay or be removed with opencomputer secrets remove.

Delivery

Webhook. Set the WEBHOOK_URL runtime variable. Each watch with new results sends one POST:

{
  "type": "monitor.results",
  "watch": { "id": "opencomputer-mentions", "query": "..." },
  "provider": "exa",
  "sentAt": "2026-09-05T09:00:12.000Z",
  "items": [
    { "id": "3f9c1a0b2d4e6f70", "url": "https://...", "title": "...", "why": "..." }
  ]
}

items[].id is stable for a watch and URL across runs. Dedupe on it.

Slack. Scheduled results post to the monitor-alerts destination, and you can @mention or DM the bot to run the watches now, ask "what's new about X", or check the setup. One Slack thread is one durable session. Each scheduled item is published once through the outbox; the id doubles as the idempotency key, so a re-run never double-posts.

Connect it per environment with the helper, which drives the same API the dashboard uses:

scripts/slack.sh start development
# create the Slack app from slack-manifest.development.json at https://api.slack.com/apps
# (Create New App -> From a manifest), install it to the workspace, then:
SLACK_APP_ID=A… SLACK_SIGNING_SECRET=… SLACK_BOT_TOKEN=xoxb-… scripts/slack.sh complete development
# /invite @OpenMonitor in the alerts channel, copy its ID from channel details, then:
scripts/slack.sh bind development C0123456789
scripts/slack.sh status development

Development and Production have separate Slack connections; repeat for production once the daily schedule should post there. The dashboard's Channels page does the same thing with a wizard.

Add a provider

Copy opencomputer/agents/monitor/tools/providers/exa.ts, change the connection and the field mapping, export a Provider, and add it to providers/index.ts. The interface is one function:

search(query: string, { since: Date; limit: number }): Promise<Hit[]>

How it works

agent.ts is a plain TypeScript function. On every step it selects the model and three tools: list_watches, search_watch, deliver. The managed harness runs the loop. The schedule starts a session; the agent walks every watch, searches through the selected provider, applies the criteria, delivers, and writes a one-line report per watch.

Dedupe across runs is by provider date filter plus the stable item id on the receiving side. There is no database in the project.

Where it runs

One click on OpenComputer. The reference runtime. Your account, your keys as managed secrets, every refresh a durable session you can open in the dashboard.

Fork it. A folder of TypeScript, no framework. Change a sentence in watches.ts, redeploy.

Break glass. The runtime is replaceable. This is what it does for this project, so you can rebuild it if you ever want to:

  • a scheduler that starts a session from the cron in schedules/refresh.ts;
  • a loop that renders agent.ts before every model step, calls the model, and executes the three tools;
  • durable session state, so a run survives a restart;
  • secret injection at the outbound edge, so provider keys never enter the process;
  • outbox delivery with retries and idempotency for Slack.

Durable Objects or Postgres for state, Lambda or a microVM for tool execution, a cron for the schedule. Nothing in this repository assumes more than the functions exported by @opencomputer/agent.

About

A durable research agent. Vendor-neutral web monitor on a schedule: bring any search API key (Exa, Parallel, Tavily, Brave, and more), one key required, runs as an OpenComputer serverless agent.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages