Skip to content

Latest commit

 

History

History

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

README.md

imessage-cli

Provider-neutral terminal access to iMessage infrastructure through imessage-sdk.

imessage-cli is designed for both local users and AI agents. It bundles every official provider, keeps one provider connection per command, accepts flags or validated JSON input, and emits stable machine-readable results.

Installation

Install globally:

npm install --global imessage-cli

Or run it without a global installation:

npx imessage-cli --help

The CLI requires Node.js ^20.19.0, ^22.13.0, or >=24.

Live CLI verification (maintainers)

The repository includes an opt-in end-to-end suite that invokes the built CLI against real provider accounts. It sends messages and performs supported provider mutations, but does not start a webhook server or create a tunnel.

Create an ignored repository-root .env.cli-test file with the credentials and dedicated test recipient/assets for all four providers. It must explicitly opt in:

IMESSAGE_CLI_RUN_LIVE=1

BLOOIO_API_KEY=
BLOOIO_FROM_NUMBER=
COMMS_API_KEY=
IMESSAGE_CLI_TEST_RECIPIENT=
IMESSAGE_CLI_TEST_IMAGE_URL=
IMESSAGE_CLI_TEST_VIDEO_URL=
IMESSAGE_CLI_TEST_FILE_URL=

PHOTON_PROJECT_ID=
PHOTON_PROJECT_SECRET=
PHOTON_PHONE_NUMBER=

SENDBLUE_API_KEY=
SENDBLUE_API_SECRET=
SENDBLUE_FROM_NUMBER=

Then run:

pnpm --filter imessage-cli test:integration

To verify default saved connections instead of environment credentials, keep the fixture variables in .env.cli-test and run:

IMESSAGE_CLI_USE_SAVED_CONNECTIONS=1 \
pnpm --filter imessage-cli test:integration

In this mode, the spawned CLI processes receive none of the provider credential environment variables, so each --provider selection must resolve its saved default connection.

The test file is ignored by Git. The runner loads it directly, redacts known secrets from its own failure messages, and uses only per-command environment credentials—no persistent CLI connection or keychain record is created.

Providers

Every provider published from packages/providers/* is bundled with the CLI:

blooio
comms
photon
sendblue

Inspect the installed providers and their normalized capabilities:

imessage-cli provider list
imessage-cli provider show blooio
imessage-cli provider show photon --json

Send a message

Use provider environment variables or a saved connection and send directly:

imessage-cli send \
  --provider blooio \
  --to +15551234567 \
  --text 'Hello'

Addresses may be written as a phone number, email address, or an explicit address:

+15551234567
hello@example.com
phone:+15551234567
email:hello@example.com

Send through an existing provider-native conversation:

imessage-cli send \
  --provider photon \
  --conversation 'provider-conversation-id' \
  --text 'Hello again'

Send a reply:

imessage-cli send \
  --provider blooio \
  --conversation 'provider-conversation-id' \
  --text 'Replying in this thread' \
  --reply-to 'provider-message-id' \
  --reply-part 0

Attachments

Attachment flags are repeatable and accept a public HTTP(S) URL or local path:

imessage-cli send \
  --provider photon \
  --to +15551234567 \
  --text 'See these files' \
  --image ./screenshot.png \
  --video https://cdn.example.com/video.mp4 \
  --file ./report.pdf

Current provider restrictions still apply:

  • Blooio requires public attachment URLs.
  • Comms currently supports plain-text messages only.
  • Photon accepts URLs and local files.
  • Sendblue accepts one URL or local file per message.
  • Group messaging remains disabled for the current official provider configurations.

For predictable memory use, local attachments are read sequentially and limited to 100 MiB in total per command. Provider limits may be lower.

The CLI never uploads a local Blooio attachment to third-party storage implicitly.

Agent input

Use --input - to read a complete validated input document from stdin. --json controls the output format; it does not change how stdin is parsed.

imessage-cli send \
  --provider photon \
  --input - \
  --json <<'JSON'
{
  "to": [
    {
      "kind": "phone",
      "value": "+15551234567"
    }
  ],
  "text": "Generated by an agent",
  "attachments": [
    {
      "kind": "image",
      "source": {
        "type": "path",
        "path": "./screenshot.png"
      }
    }
  ],
  "idempotencyKey": "agent-run-018fd6"
}
JSON

An input file works as well:

imessage-cli send --provider photon --input ./send.json --json

Input flags and --input are mutually exclusive. Discover the current agent-facing schema with:

imessage-cli schema
imessage-cli schema send --json

Validate a send without contacting the provider:

imessage-cli send \
  --provider blooio \
  --to +15551234567 \
  --text 'Hello' \
  --dry-run \
  --json

Credentials and saved connections

There are three credential modes.

Saved connection

Create a durable named connection:

imessage-cli connection add support --provider blooio

Interactive input is masked where appropriate. Credentials and selected sending identities are stored in the operating system credential store:

  • macOS Keychain
  • Windows Credential Manager
  • Linux Secret Service when available

On headless Linux, the native keyring dependency may fall back to the kernel keyring, whose entries can be session-scoped. Use a Secret Service daemon for durable saved connections, or use environment variables when no durable operating-system credential service is available.

The JSON configuration contains only connection metadata and non-sensitive behavioral settings. It is written with user-only permissions. The CLI never falls back to a plaintext credential file.

The first saved connection for a provider becomes that provider's default. It can then be selected with either form:

imessage-cli send --provider blooio --to +15551234567 --text 'Hello'
imessage-cli send --connection support --to +15551234567 --text 'Hello'

Manage saved connections:

imessage-cli connection list
imessage-cli connection show support
imessage-cli connection doctor support
imessage-cli connection doctor support --offline
imessage-cli connection remove support

show reports credential presence and masks identities. It never prints secret values. doctor performs only non-mutating checks and never sends a message. remove deletes local configuration and keychain entries; it does not change the remote provider account or revoke an API key.

Set or replace one credential safely from a masked prompt:

imessage-cli connection credential set support webhookSecret

For non-interactive setup, read it from stdin:

imessage-cli connection credential set support webhookSecret --input - < secret-value-file

Environment variables

Environment variables provide an ephemeral automation and CI interface:

Provider Variables
Blooio BLOOIO_API_KEY, BLOOIO_FROM_NUMBER, BLOOIO_WEBHOOK_SECRET
Photon PHOTON_PROJECT_ID, PHOTON_PROJECT_SECRET, PHOTON_PHONE_NUMBER, PHOTON_WEBHOOK_SECRET
Sendblue SENDBLUE_API_KEY, SENDBLUE_API_SECRET, SENDBLUE_FROM_NUMBER, SENDBLUE_WEBHOOK_SECRET

Environment values override a saved connection for the current process and are never persisted.

One-time options

Provider credential options can be supplied for one invocation:

imessage-cli send \
  --provider blooio \
  --api-key "$BLOOIO_API_KEY" \
  --from-number +15551234567 \
  --to +15557654321 \
  --text 'Hello'

One-time options are never saved or logged by the CLI. Supplying a literal secret as a command-line argument is nevertheless discouraged because operating-system process inspection and shell history may expose it. Prefer a saved connection, a masked prompt, or environment injection.

Resolution order is:

command option
    -> environment variable
    -> selected/default saved connection
    -> provider default

Machine-readable and non-interactive commands never prompt. A missing required value becomes a structured error.

Experimental local webhooks

The CLI webhook server is experimental in the initial release. You must explicitly acknowledge that status with --experimental; otherwise the command exits with a usage error before loading provider credentials or binding a port. The server application is implemented with Hono and runs through Hono's official Node.js adapter.

Start a signed-webhook receiver on the loopback interface:

imessage-cli webhook serve \
  --experimental \
  --provider blooio \
  --host 127.0.0.1 \
  --port 8787 \
  --path /webhooks \
  --json

The server:

  • passes the untouched body and headers to the provider verifier;
  • emits normalized events as JSON Lines on stdout;
  • writes startup information and diagnostics to stderr;
  • returns 204 for an accepted webhook, including an irrelevant but valid event;
  • returns 401 for an invalid signature;
  • limits request body size;
  • exposes GET /healthz by default;
  • closes the selected SDK client on SIGINT or SIGTERM.

It binds 127.0.0.1 by default and does not install or supervise a tunnel.

ngrok

Run the webhook server in one terminal and ngrok in another:

ngrok http 8787

If ngrok assigns https://example.ngrok.app, register this full callback URL in the provider dashboard:

https://example.ngrok.app/webhooks

For private message content, consider disabling ngrok request inspection:

ngrok http 8787 --url https://example.ngrok.app --inspect=false

Cloudflare Quick Tunnel

cloudflared tunnel --url http://127.0.0.1:8787

Append /webhooks to the generated HTTPS origin. Quick Tunnel hostnames are temporary and may need to be updated in the provider dashboard after a restart.

The webhook command is intended for local development and agent experiments. It does not provide a durable queue, high availability, delivery persistence, or replay ledger. Production webhook consumers should deploy the SDK handler behind a stable HTTPS endpoint.

External agent process

Normalized webhook events can be piped into any program:

imessage-cli webhook serve --experimental --provider photon --json | node ./my-agent.mjs

That program can invoke its preferred AI model and reply with a separate imessage-cli send command. The CLI does not bundle a model SDK or tunnel provider.

Other normalized commands

message get
conversation open
conversation get
conversation mark-read
attachment download
reaction add
reaction remove
typing start
typing stop

Provider-specific extensions remain namespaced:

provider blooio numbers list
provider blooio message status
provider photon line show
provider sendblue message status
provider sendblue tapback add

Unsupported normalized operations return UnsupportedCapabilityError; the CLI does not silently approximate provider behavior.

Machine output

--json writes one success object to stdout:

{
  "schemaVersion": 1,
  "ok": true,
  "command": "send",
  "context": {
    "provider": "blooio",
    "connectionId": "support"
  },
  "data": {
    "id": "provider-message-id"
  }
}

Failures are written to stderr:

{
  "schemaVersion": 1,
  "ok": false,
  "command": "send",
  "error": {
    "type": "AmbiguousDeliveryError",
    "code": "ambiguous_delivery",
    "message": "The provider may have accepted the message.",
    "retryable": true,
    "deliveryAmbiguous": true,
    "safeToRetry": false
  }
}

Provider raw payloads, credentials, and authorization headers are omitted. The CLI never retries an ambiguous send automatically.

Exit codes:

Code Meaning
0 Success
1 Unexpected internal failure
2 Invalid command input, configuration, or local credential store
3 Authentication failure
4 Unsupported or permanent provider operation
5 Rate limit or provider availability failure
6 Ambiguous message delivery; do not retry blindly
130 Interrupted with SIGINT
143 Terminated with SIGTERM

Shell completion

imessage-cli completion bash
imessage-cli completion zsh
imessage-cli completion fish
imessage-cli completion powershell

Follow the installation convention for the selected shell and source the generated script.