CLI command target

View as Markdown

A CLI command target is the simplest way to hook a local script into Dial. You register an executable with the listen daemon and, for every account event, the daemon spawns that executable with the event JSON as its final positional argument. No HTTP server, no signature header — just a process you’d normally run from a shell.

When to use it

  • Quick automations that already live in a script you’d otherwise call by hand.
  • Glue work where the agent or service you’re notifying doesn’t expose a webhook listener.
  • One-shot side effects: write to a file, kick a build, ring a bell.

If the receiver already speaks HTTP, prefer a Local URL target — it scales better and lets the receiver run as a long-lived process.

Prerequisites

$dial listen install

The listen daemon owns the fan-out queue and is what actually spawns your executable. See Listen service.

Register

$dial local-target add cmd /usr/local/bin/handle-dial-event

For each event, the daemon invokes:

/usr/local/bin/handle-dial-event '<event_json>'

You can attach extra arguments — anything after the path is forwarded verbatim before the JSON argument:

$dial local-target add cmd /usr/local/bin/handle-dial-event --log-level info --label dial

Per-event invocation:

/usr/local/bin/handle-dial-event --log-level info --label dial '<event_json>'

See dial local-target add cmd for the full reference.

Example handler

The most useful thing a CLI target can do is wake a fresh Claude Code session with the event as the prompt. The handler shapes the event JSON into a prompt, spawns Claude Code detached in the background, and exits immediately — so the daemon sees a fast 0 and the agent does the heavy lifting on its own time.

$#!/usr/bin/env bash
$# /usr/local/bin/handle-dial-event
$event="$1"
$
$type=$(jq -r '.type' <<<"$event")
$from=$(jq -r '.from // ""' <<<"$event")
$body=$(jq -r '.body // ""' <<<"$event")
$
$prompt=$(cat <<EOF
>A Dial event just arrived. Decide what to do and act on it.
>
> type: $type
> from: $from
> body: $body
>
>raw event JSON:
>$event
>EOF
>)
$
$# Detach a fresh Claude Code session and return right away.
$nohup claude -p "$prompt" </dev/null >>~/.dial/agent.log 2>&1 &
$disown
$exit 0

The nohup … & disown pattern is what makes this safe inside a cmd target: the spawned claude keeps running after the handler exits, so even a multi-minute agent run never bumps into the daemon’s per-attempt --timeout.

Make it executable, then register it:

$chmod +x /usr/local/bin/handle-dial-event
$dial local-target add cmd /usr/local/bin/handle-dial-event

Delivery semantics

  • Exit code 0 — treated as delivered.
  • Non-zero exit, or a timeout — the daemon retries the invocation once. If the retry also fails, the event is logged and dropped; nothing is queued for later.
  • Timeout--timeout <seconds> per attempt, defaulting to 5.
  • Stdout / stderr — captured into the listen log so you can see what the executable printed.
  • Environment — each invocation runs with a clean environment except for PATH and HOME. Anything else your handler needs must be looked up from inside the handler (or wrapped in a shell script that sets it).
  • Concurrency — invocations of the same target run independently; the daemon doesn’t serialize them, so make your handler safe to run twice in close succession (the once-retry can produce that).

Verify, list, remove

$dial local-target list
$# url http://127.0.0.1:8787/dial-events
$# cmd /usr/local/bin/handle-dial-event
$
$dial local-target remove /usr/local/bin/handle-dial-event

No daemon restart is required for any of these — the fan-out registry updates on the fly.