channelayer
v0.0.2
Published
CLI for AI Inbox — the same tools as the MCP server, plus login.
Maintainers
Readme
channelayer
CLI for AI Inbox. Same tools as the live MCP server
(/api/mcp), same arguments and descriptions, plus login for OAuth.
npx channelayer --help
pnpm channelayer --help # after building this packageInstall
# one-shot
npx channelayer login
npx channelayer list-threads
# or add the package
npm install -g channelayer
channelayer loginFrom this monorepo:
pnpm install
pnpm --filter channelayer build
pnpm --filter channelayer channelayer -- --helpCommand names
MCP tools are snake_case (list_threads). The CLI uses kebab-case (list-threads) and
keeps the snake_case name as an alias, so either works:
channelayer list-threads
channelayer list_threadsAdding a tool: put it in @repo/shared inboxTools (name, description, Zod schema, /api/v1
binding). The MCP server registers it from that catalog; this CLI generates the subcommand from the
same list. No second command file.
The only CLI-only command is login.
Auth
channelayer loginThis does not use a localhost callback.
- The CLI
POSTs/api/cli/auth/sessionsand gets an authorize URL plus a poll token. - Your browser opens Supabase OAuth (consent at
/oauth/consentif you are signed in). - After approve, Supabase redirects to the hosted page
/oauth/cli/callbackon the AI Inbox origin. - That page stores tokens on the pending session.
- The CLI polls
GET /api/cli/auth/sessions/:iduntil they are ready, then writes them to disk.
Token storage
| Platform | Path |
| ----------- | ------------------------------------------------------------------------------------------- |
| Linux / XDG | $XDG_CONFIG_HOME/channelayer/credentials.json or ~/.config/channelayer/credentials.json |
| macOS | ~/Library/Application Support/channelayer/credentials.json |
| Windows | %APPDATA%\channelayer\credentials.json |
Override the directory with CHANNELAYER_CONFIG_DIR. The file is mode 0600. Refresh tokens are
stored when Supabase issues them; the CLI refreshes via POST /api/cli/auth/refresh when the access
token is near expiry.
CHANNELAYER_TOKEN skips the file and is sent as the bearer token.
OAuth dashboard (Logan)
Create a public (native) OAuth client in Supabase → Authentication → OAuth Server → Add client:
| Field | Value |
| ------------------- | ------------------------------------------------- |
| Name | Channelayer CLI |
| Client type | Public (PKCE, no secret) |
| Token endpoint auth | none |
| Redirect URLs | https://<AI_INBOX_ORIGIN>/oauth/cli/callback |
| | Local: http://localhost:5173/oauth/cli/callback |
Copy the client id. Set it on the API (not a secret):
# env var name
CHANNELAYER_OAUTH_CLIENT_ID=<the public client id>Optional override for the exact redirect URL the authorize request sends (must be listed on the client):
CHANNELAYER_OAUTH_REDIRECT_URI=https://<AI_INBOX_ORIGIN>/oauth/cli/callbackIf unset, the API uses {PUBLIC_ORIGIN or request origin}/oauth/cli/callback.
Until that client exists, channelayer login fails with a 503 that names this env var.
Authorization Path in the same dashboard stays /oauth/consent (already used by MCP).
Config
| Variable | What it does |
| ------------------------ | -------------------------------------------------------------------------------------------------------------------------- |
| CHANNELAYER_API_URL | API origin. Default https://ai-inbox.demos.cool. Local: http://localhost:8787 (or the web origin that proxies /api). |
| CHANNELAYER_TOKEN | Bearer token; skips login storage. |
| CHANNELAYER_CONFIG_DIR | Where credentials.json is written. |
| --api-url / -u | Same as CHANNELAYER_API_URL for one invocation. |
| --json | JSON on stdout (tool commands always print JSON). |
Examples
channelayer login
channelayer list-mailboxes
channelayer list-threads --channel email
channelayer get-thread --thread-id <uuid>
channelayer send-email --to [email protected] --subject Hello --body "Hi Ada"
channelayer send-sms --to +15551234567 --body Hello
channelayer create-contact --name Ada --email [email protected]Every tool command prints JSON (the /api/v1 response). Errors go to stderr with a non-zero exit.
channelayer list-threads --helpHelp text is the MCP tool description plus generated flags from the same Zod / JSON Schema the server advertises.
Publishing
Published to npm as channelayer. Publish with
pnpm, not npm: zod uses the catalog: protocol, which pnpm publish rewrites to a real
version range and npm publish does not.
npm login # once
pnpm install
pnpm --filter channelayer test
pnpm --filter channelayer build
cd packages/cli
pnpm version patch # or minor / major; skip for the first 0.1.0 release
pnpm publish --dry-run # expect dist/cli.js, README.md, package.json
pnpm publish@repo/shared is inlined into dist/cli.js by vite.config.ts, so it stays a private dev
dependency. publishConfig.access is already public.
Before announcing a release, make sure the production API has CHANNELAYER_OAUTH_CLIENT_ID set (see
OAuth dashboard), or channelayer login returns 503.
