Skip to content

Latest commit

 

History

7 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

courier-cli

Universal local CLI for sending files from Codex projects to messenger targets.

AI Disclosure

This utility was generated entirely by AI during an interactive Codex session. Review the code, configuration, and security behavior before relying on it for sensitive workflows.

License

0BSD. See LICENSE.

Design Goals

The project is intentionally small and conservative:

  • secrets live outside git in ~/.courier-cli/bot-credits;
  • every send operation requires interactive confirmation;
  • providers are implemented separately;
  • Telegram is implemented;
  • XMPP with HTTP File Upload is implemented.

Repository Contents

This repository contains two related parts:

  • courier-cli: the Python command line tool installed as courier;
  • skills/send-with-courier: a Codex skill that teaches Codex agents how to use courier safely.

Install and configure the CLI first. Then install the Codex skill if you want Codex agents to understand requests like "send this file to Telegram" or "отправь файл в Monal".

Requirements

  • Python 3.11+
  • Network access during installation, because dependencies are installed from PyPI
  • pipx is recommended for a user-wide CLI install

If your system python3 is older than 3.11, use an explicit python3.11 executable or the full path to a Python 3.11+ interpreter during installation.

Recommended Install With pipx

This is the preferred setup when Codex or any terminal should be able to run courier from any project.

  1. Install pipx if it is not installed yet.

    macOS with Homebrew:

    brew install pipx
    pipx ensurepath

    Generic Python install:

    python3.11 -m pip install --user pipx
    python3.11 -m pipx ensurepath
  2. Restart the terminal, or source your shell config.

    For zsh:

    source ~/.zshrc

    For bash:

    source ~/.bashrc
  3. Clone the repository:

    git clone <repo-url>
    cd courier-cli
  4. Install courier-cli:

    pipx install --python python3.11 .
  5. Check that the command is available without activating a virtual environment:

    command -v courier
    courier --help
    courier doctor

If command -v courier prints a path, most Codex terminal sessions should also be able to find the command through PATH. If Codex still cannot find courier, run command -v courier inside the Codex terminal and adjust the shell or app environment that Codex actually uses.

Updating a pipx Install

After pulling new code:

cd courier-cli
git pull
pipx install --force --python python3.11 .

Check again:

courier --help
courier doctor

Alternative Install With venv and PATH

Use this when you do not want to use pipx.

  1. Clone the repository:

    git clone <repo-url>
    cd courier-cli
  2. Create a virtual environment:

    python3.11 -m venv .venv
  3. Install the package and its dependencies into that virtual environment:

    .venv/bin/pip install -e .
  4. Add the virtual environment's bin directory to your shell PATH.

    For zsh:

    echo 'export PATH="$HOME/path/to/courier-cli/.venv/bin:$PATH"' >> ~/.zshrc
    source ~/.zshrc

    For bash:

    echo 'export PATH="$HOME/path/to/courier-cli/.venv/bin:$PATH"' >> ~/.bashrc
    source ~/.bashrc

    Replace "$HOME/path/to/courier-cli" with the real clone path.

  5. Check that courier is available globally:

    command -v courier
    courier --help
    courier doctor

Development Install

For local development inside this repository:

python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .
courier --help
courier doctor

This makes courier available while the virtual environment is active. For Codex usage across projects, prefer the pipx install or add .venv/bin to PATH.

Install On Another Computer Checklist

  1. Install Python 3.11+.

  2. Install pipx, or decide to use the venv + PATH method.

  3. Clone this repository.

  4. Install the package with pipx install --python python3.11 . or .venv/bin/pip install -e ..

  5. Confirm command -v courier prints a path.

  6. Create ~/.courier-cli/bot-credits.

  7. Configure at least one target, for example telegram.me or xmpp.me.

  8. Run:

    courier doctor
    courier config check
  9. Send a test file.

  10. If this machine will be used with Codex, install the bundled Codex skill from skills/send-with-courier.

Codex Agent Check

Codex skills and agents expect courier to be available through PATH. They should not rely on project-specific absolute paths.

Run this from any project directory:

command -v courier
courier doctor

If command -v courier prints nothing, install with pipx or add the venv bin directory to PATH.

Codex Skill Install

The bundled Codex skill is optional for manual terminal usage, but recommended when Codex agents should send files for you. The skill does not contain secrets. It only contains instructions for agents.

Install and verify courier first:

command -v courier
courier doctor
courier config check

Then install the skill from this repository:

cd courier-cli
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
mkdir -p "$CODEX_HOME/skills"
rm -rf "$CODEX_HOME/skills/send-with-courier"
cp -R skills/send-with-courier "$CODEX_HOME/skills/"

The install command replaces any existing installed copy of send-with-courier with the version from this repository.

Restart Codex after installing or updating the skill. New Codex chats should then be able to see send-with-courier when you type $.

To test natural triggering, open a new Codex chat in any project and ask something like:

Отправь README.md мне в Monal

or:

Send README.md to me in Telegram

The skill tells the agent to:

  • use only courier found through PATH;
  • ask for confirmation before sending;
  • run network commands with network access/escalation in sandboxed Codex environments;
  • run courier send interactively and write y to stdin only after the user confirms;
  • avoid showing CLI implementation details in normal user-facing chat.

PATH Setup Notes

If courier was installed with pipx, pipx ensurepath usually handles PATH setup. Verify with:

command -v courier
courier doctor

If you installed into a project venv, add that venv's bin directory to PATH as shown in Alternative Install With venv and PATH.

GUI apps may not always inherit the same PATH as an interactive terminal. When troubleshooting Codex, run command -v courier in the Codex terminal, not only in your regular shell.

Secrets

Create the secrets directory:

mkdir -p ~/.courier-cli
chmod 700 ~/.courier-cli

Create:

~/.courier-cli/bot-credits

Example TOML:

[telegram.me]
bot_token = "123456:telegram-token"
chat_id = "123456789"

[xmpp.me]
jid = "pavel@example.org"
password = "app-password"
recipient_jid = "pavel@example.org"

Protect the file:

chmod 600 ~/.courier-cli/bot-credits

Never commit this file to git.

Telegram Setup

  1. Open Telegram and find the verified @BotFather account.

  2. Send:

    /newbot
    
  3. Choose a display name for the bot.

  4. Choose a username ending with bot, for example my_courier_bot.

  5. BotFather will return a bot token. Keep it private.

  6. Send any message to your new bot from the Telegram account that should receive files.

  7. Add the token to ~/.courier-cli/bot-credits with a temporary chat id:

    [telegram.me]
    bot_token = "123456:telegram-token"
    chat_id = "TODO"
  8. Ask courier to list chats seen by the bot:

    courier telegram chats --target telegram.me

    The output should look like:

    123456789 [private] (your_name)
    
  9. Replace TODO with the printed chat id:

    [telegram.me]
    bot_token = "123456:telegram-token"
    chat_id = "123456789"

Manual chat_id lookup alternative:

curl "https://api.telegram.org/bot<YOUR_BOT_TOKEN>/getUpdates"

In the JSON response, find:

"chat":{"id":123456789}
  1. Check the config:
courier config check
courier targets list
  1. Send a test file:

    courier send ./README.md --target telegram.me

    The command will ask for confirmation before sending.

XMPP Setup

XMPP file delivery uses XEP-0363 HTTP File Upload:

  1. The CLI logs in as the configured XMPP account.
  2. It discovers an HTTP File Upload service.
  3. It uploads the file through that service.
  4. It sends a chat message to recipient_jid containing the uploaded file URL.

Add an XMPP target to ~/.courier-cli/bot-credits:

[xmpp.me]
jid = "pavel@example.org"
password = "app-password"
recipient_jid = "pavel@example.org"

jid is the sender account used by the CLI. password is the password or app password for that sender account. recipient_jid is the account that receives the message with the uploaded file URL. If you send files to yourself, jid and recipient_jid can be the same.

If DNS SRV discovery is unavailable or you want to force a server address, add:

host = "xmpp.example.org"
port = "5222"

Start without host and port when normal XMPP DNS discovery works. Add them only if your client needs a manually configured server address or if the JID domain is different from the actual XMPP host.

Check the config:

courier config check
courier targets list

Check XMPP login and HTTP File Upload discovery:

courier xmpp doctor --target xmpp.me

Successful output looks like:

XMPP OK.
HTTP File Upload service: upload.example.org

Send a file:

courier send ./README.md --target xmpp.me

The command will ask for confirmation before connecting and uploading.

XMPP support depends on the server. The server must support XEP-0363 HTTP File Upload, and the upload endpoint must be reachable from this machine.

Confirmation Flow

Every send operation asks for confirmation:

About to send:
  File: /absolute/path/to/file.pdf
  Size: 248.0 KB
  Via: telegram
  To: me

Proceed? [y/N]

The default answer is No. The file is sent only after an explicit y or yes.

Upload Progress

During Telegram and XMPP uploads, the CLI periodically prints progress to stderr:

XMPP upload: starting upload (24.0 MB)
XMPP upload: 4.0 MB / 24.0 MB (16.7%)
XMPP upload: 8.0 MB / 24.0 MB (33.3%)
XMPP upload: upload complete, waiting for server response

Progress is reported for bytes read from the local file and passed to the HTTP upload request. After the upload reaches 100%, the command may still wait for the messenger or upload server to finish processing the request.

Uploads use a 10 minute timeout. If a file is too large or the network is too slow, retry on a faster connection or split the file.

File Safety

The CLI refuses obvious sensitive files by name:

.env
.env.*
*.pem
*.key
id_rsa
id_ed25519
*.sqlite
*.db
*.kdbx

This denylist is only a guardrail. Always review the file path shown in the confirmation prompt before sending.

Current Commands

courier --help
courier doctor
courier config check
courier targets list
courier telegram chats --target telegram.me
courier xmpp doctor --target xmpp.me
courier send ./file.pdf --target telegram.me
courier send ./file.pdf --target xmpp.me

The send command validates the file, loads the target, asks for confirmation, and then dispatches through the selected provider.

Telegram and XMPP delivery are implemented.

Troubleshooting

If courier telegram chats --target telegram.me prints no chats, send a new normal message to the bot and run the command again. /start is often enough, but a regular text message is more reliable for getUpdates.

If courier config check reports a missing value, check the matching table in ~/.courier-cli/bot-credits.

If the CLI warns that bot-credits is too permissive, run:

chmod 600 ~/.courier-cli/bot-credits

If courier xmpp doctor --target xmpp.me cannot find an HTTP File Upload service, check your XMPP server configuration for XEP-0363 support. Common server-side modules include Prosody mod_http_upload and ejabberd mod_http_upload.

Tests

python3.11 -m unittest discover -v

About

Universal local CLI for sending files from Codex projects to messenger targets.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages