Skip to content

Latest commit

 

History

History
285 lines (217 loc) · 12.3 KB

File metadata and controls

285 lines (217 loc) · 12.3 KB

Watch Mode — Real-Time Event Monitoring

The outlook-cli watch command monitors your mailbox and calendar for changes using Microsoft Graph. Three notification modes are available:

Mode Latency Setup Best For
--mode poll (default) 30–60s None General monitoring
--mode fast-poll 5–60s adaptive None Quick response without external deps
--mode webhook ~1–5s push Tunnel tool (cloudflared) Gateway/agent integration

Quick Start

# Default: poll mode (delta queries every 60 seconds)
outlook-cli watch --account my-account

# Fast-poll: adaptive 5–60 second polling (speeds up on activity)
outlook-cli watch --mode fast-poll --interval 10

# Webhook: near-instant push notifications via Microsoft Graph subscriptions
outlook-cli watch --mode webhook

# JSONL mode for piping to an AI agent
outlook-cli watch --mode webhook --jsonl | my-agent-process

# Watch mail only, specific folder
outlook-cli watch --no-calendar --folder "Important"

# Include heartbeat signals every 5 minutes
outlook-cli watch --heartbeat --heartbeat-interval 300

Watch Modes

Choosing the Right Mode

Question Recommendation
Just monitoring for notifications? poll (default) — 30-60s is fine for human attention
AI agent needs quick response? fast-poll — no external dependencies, 5-10s response
Building a real-time gateway? webhook — ~1-5 second push, but needs a tunnel
Running on a server with a public IP? webhook — skip the tunnel, use --webhook-port
Multiple accounts simultaneously? poll or fast-poll with staggered intervals to avoid rate limits
Limited API quota / shared tenant? poll at 60s — fewest API calls

Poll Mode (Default)

Uses Microsoft Graph delta queries — an efficient pull-based approach that minimizes API calls. Delta queries only return items that changed since your last call, so even if you have 10,000 messages, each poll transfers only the new/changed ones.

  1. Initial sync: Fetches all existing items and establishes a baseline (no events emitted). This can take several seconds for large mailboxes but only happens once.
  2. Incremental sync: Each subsequent call returns only what changed since the last check. Microsoft Graph tracks the changes server-side using a delta token.
  3. Delta tokens: Sync state is persisted to ~/.outlook-cli/delta-{alias}.json, surviving restarts. When you restart the watcher, it picks up where it left off — no duplicate events.

Minimum interval: 30 seconds. Default: 60 seconds. Microsoft Graph rate limits apply — for personal accounts, you get ~10,000 API calls per 10 minutes across all applications.

Fast-Poll Mode

Same delta query mechanism but with adaptive polling that automatically adjusts its frequency based on activity. This is the best mode for AI agent integration when you don't want to set up a tunnel:

  • Starts at the configured interval (minimum 5 seconds)
  • When changes are detected, drops to 5-second polling — catching follow-up messages almost immediately
  • When no changes are found, backs off by 1.5× each cycle — e.g., 5s → 7.5s → 11s → 17s → 25s → 38s → max
  • Caps at the configured --interval value (default 60s)
  • Resets to 5s immediately when any change is detected

This means: during an active email conversation, polling happens every 5 seconds. After the conversation stops, it gradually relaxes back to the configured maximum. This balances responsiveness with API efficiency.

# Adaptive polling: 5–30 second range
outlook-cli watch --mode fast-poll --interval 30

# Aggressive: 5–10 second range (for gateway use)
outlook-cli watch --mode fast-poll --interval 10

Webhook Mode

Uses Microsoft Graph change notifications (push-based). Microsoft Graph POSTs to a webhook URL when changes occur, delivering notifications within ~1–5 seconds.

Since the CLI runs on your local machine (not a public server), a tunnel exposes the local webhook server to the internet:

Microsoft Graph ──POST──→ Tunnel (cloudflared) ──→ localhost:PORT ──→ outlook-cli

Supported tunnels:

Tunnel Install Notes
cloudflared (default) winget install Cloudflare.cloudflared Free, no signup for quick tunnels
localtunnel npx localtunnel (auto-installed) NPM package, zero config
ngrok winget install Ngrok.Ngrok Popular, optional free signup
# Webhook with default tunnel (cloudflared)
outlook-cli watch --mode webhook

# Webhook with specific tunnel
outlook-cli watch --mode webhook --tunnel ngrok

# Webhook with custom port
outlook-cli watch --mode webhook --webhook-port 3000

Reliability features:

  • Auto-renews Graph subscriptions before expiry (~3 days for personal accounts)
  • Runs delta query reconciliation every 5 minutes as safety net for missed notifications
  • Graceful shutdown: deletes subscriptions, stops tunnel, closes server

Event Types

Event Type Description
mail.changed New or modified email message
mail.removed Deleted email message
calendar.changed New or modified calendar event
calendar.removed Deleted calendar event
sync.status Informational (initial sync, errors)
heartbeat Periodic liveness signal

Output Modes

Text Mode (Default)

Human-readable notifications with timestamps and icons:

[14:32:05] ℹ️  Watch started
[14:32:06] ℹ️  Mail baseline: 47 messages in Inbox
[14:32:07] ℹ️  Calendar baseline: 12 events in next 30 days
[14:32:07] ℹ️  Initial sync complete. Watching for changes...
[14:33:07] 📧 Mail from alice@example.com: "Meeting notes" (unread)
         Hi everyone, here are the notes from today's...
[14:34:07] 📅 Calendar: "Team Standup" at 1/15/2024, 10:00:00 AM
         📍 Conference Room B

JSONL Mode (for Agents)

One JSON object per line — designed for piping to AI agents:

outlook-cli watch --jsonl
{"type":"sync.status","timestamp":"2024-01-15T14:32:05.123Z","data":{"message":"Watch started","account":"my-account","watching":{"mail":"Inbox","calendar":true},"interval":"60s"}}
{"type":"mail.changed","timestamp":"2024-01-15T14:33:07.456Z","data":{"id":"AAMk...","subject":"Meeting notes","from":"alice@example.com","receivedDateTime":"2024-01-15T14:33:00Z","isRead":false,"bodyPreview":"Hi everyone...","importance":"normal","hasAttachments":false}}
{"type":"calendar.changed","timestamp":"2024-01-15T14:34:07.789Z","data":{"id":"AAMk...","subject":"Team Standup","start":{"dateTime":"2024-01-15T10:00:00","timeZone":"Pacific Standard Time"},"end":{"dateTime":"2024-01-15T10:30:00","timeZone":"Pacific Standard Time"},"location":"Conference Room B","organizer":"bob@example.com"}}

OpenClaw / NanoClaw Integration

An AI agent can consume the JSONL stream to react to real-time changes:

# Start watch in background, pipe to agent
outlook-cli watch --jsonl --account work --heartbeat | process-events.sh

The agent reads lines from stdin, parses each as JSON, and acts accordingly:

// Example agent event processing
process.stdin.on('data', (chunk) => {
  for (const line of chunk.toString().split('\n').filter(Boolean)) {
    const event = JSON.parse(line);
    if (event.type === 'mail.changed' && !event.data.isRead) {
      console.log(`New email from ${event.data.from}: ${event.data.subject}`);
      // Trigger agent action...
    }
  }
});

Options

Option Default Description
--mode <mode> poll Watch mode: poll, fast-poll, or webhook
--interval <seconds> 60 Poll/fast-poll interval (min: 30 for poll, 5 for fast-poll)
--tunnel <type> cloudflared Tunnel for webhook mode: cloudflared, ngrok, localtunnel
--webhook-port <port> random Local port for webhook server
--no-mail watch mail Disable mail watching
--no-calendar watch calendar Disable calendar watching
--folder <name> Inbox Mail folder to watch
--jsonl text Output as JSON Lines
--heartbeat off Emit periodic heartbeat events
--heartbeat-interval <s> 300 Heartbeat interval in seconds
--account <alias> default Account to watch
--as <email> own mailbox Watch a delegate mailbox

Delta Token Management

Delta tokens are the backbone of the watch system. They allow the CLI to ask Microsoft Graph "what changed since I last checked?" instead of re-fetching everything. Tokens are stored per-account in ~/.outlook-cli/delta-{alias}.json:

{
  "mail:Inbox": "https://graph.microsoft.com/v1.0/me/mailFolders/Inbox/messages/delta?$deltatoken=...",
  "calendar:rolling30d": "https://graph.microsoft.com/v1.0/me/calendarView/delta?$deltatoken=..."
}
  • Persistence: Tokens survive process restarts — only new changes are reported. You can stop the watcher, restart your machine, and when you start watching again, it only reports changes that happened while you were away.
  • Expiry: Tokens expire after ~30 days of inactivity. When a token expires, the Graph API returns a 410 Gone response, and the watcher automatically falls back to a full re-sync (establishing a new baseline and getting a fresh token). The initial sync is suppressed so you don't get a flood of old messages.
  • Reset: Delete the delta file (~/.outlook-cli/delta-{alias}.json) to force a full re-sync. This is useful if you suspect the delta state is corrupted or out of sync.
  • Multiple watchers: Each account has its own delta file. You can watch multiple accounts simultaneously without conflicts. However, running two watchers for the same account simultaneously will cause delta token conflicts — the second watcher may miss events or report duplicates.

Graceful Shutdown

Press Ctrl+C to stop the watcher. It will:

  1. Complete any in-progress delta query
  2. Save delta tokens to disk
  3. Delete Graph webhook subscriptions (webhook mode)
  4. Stop tunnel process (webhook mode)
  5. Exit cleanly

Gateway Integration

For OpenClaw and NanoClaw integration, use the provided scripts in skill/openclaw/ and skill/nanoclaw/. These scripts automatically:

  • Detect the best watch mode (webhook if tunnel available, fast-poll otherwise)
  • Configure JSONL output for agent consumption
  • Handle binary detection and account selection

See skill/openclaw/SKILL.md and skill/nanoclaw/SKILL.md for full setup instructions.

Multi-Job Watch Configuration

For advanced scenarios, you can define multiple watch jobs in a JSON configuration file. Each job has its own account, folder, interval, and rules:

{
  "jobs": [
    {
      "name": "personal-inbox",
      "account": "personal",
      "folder": "Inbox",
      "interval": 60,
      "mode": "poll",
      "rules": [
        {
          "condition": { "from": "*@company.com" },
          "actions": ["mark-read", { "move": "Work" }]
        }
      ]
    },
    {
      "name": "work-urgent",
      "account": "work",
      "folder": "Inbox",
      "interval": 15,
      "mode": "fast-poll",
      "rules": [
        {
          "condition": { "importance": "high", "isRead": false },
          "actions": ["flag", { "run": "notify-send 'Urgent: ${subject}'" }]
        }
      ]
    }
  ]
}
# Run multi-job watch
outlook-cli watch --config watches.json

# Validate config without running
outlook-cli watch --config watches.json --validate

Each job runs independently with its own delta token and polling interval. Rules are evaluated on each change event, and matching actions are executed in order. The run action can execute arbitrary commands with variable substitution (${subject}, ${from}, ${id}).

C# Implementation Notes

The C# implementation supports poll and fast-poll modes. Webhook mode is not available in C# — when --mode webhook is specified, the C# binary displays a message directing the user to the Node.js implementation:

⚠️  Webhook mode is only available in the Node.js implementation.
   Run: outlook-cli watch --mode webhook
   (Requires Node.js runtime)
   Falling back to fast-poll mode...

This is because the webhook infrastructure (tunnel management, HTTP server, subscription lifecycle) is complex and Node.js-specific. For gateway scenarios requiring webhook mode, use the Node.js runtime.