Compare commits

...

7 Commits

Author SHA1 Message Date
Peter Steinberger 80d9d288c2 chore: bump to 0.1.2 and fix commander typings
CI / build (push) Failing after 31s
2025-11-25 14:26:55 +01:00
Peter Steinberger dd55a80430 chore: prep 0.1.1 (version bump, lowercase branding, ua update)
CI / build (push) Failing after 44s
2025-11-25 14:13:17 +01:00
Peter Steinberger f7cdb12101 chore: document 0.1.1 changes in changelog 2025-11-25 14:10:42 +01:00
Peter Steinberger 3b8783ec0e docs: colorized help and example footer 2025-11-25 14:09:59 +01:00
Peter Steinberger 4d2a8a80d4 Restore original tagline 2025-11-25 13:51:13 +01:00
Peter Steinberger d51a3e9b48 Align header with steipete tagline 2025-11-25 13:50:18 +01:00
Peter Steinberger cadd29e147 fix: make CLI bin invoke program parse 2025-11-25 13:22:54 +01:00
7 changed files with 97 additions and 11 deletions
+14
View File
@@ -1,5 +1,19 @@
# Changelog
## 0.1.1 — 2025-11-25
### CLI polish
- Added a proper executable shim so `npx warelay@0.1.x --help` runs the CLI directly.
- Help/version banner now uses the README tagline with color, and the help footer includes colored examples with short explanations.
- `send` and `status` gained a `--verbose` flag for consistent noisy output when debugging.
- Lowercased branding in docs/UA; web provider UA is `warelay/cli/0.1.1`.
## 0.1.2 — 2025-11-25
### CI/build fix
- Fixed commander help configuration (`subcommandTerm`) so TypeScript builds pass in CI.
- Bumped version/UA to 0.1.2; no functional changes beyond the CI fix.
## 0.1.0 — 2025-11-25
### CLI & Providers
+1 -1
View File
@@ -124,7 +124,7 @@ Templating tokens: `{{Body}}`, `{{BodyStripped}}`, `{{From}}`, `{{To}}`, `{{Mess
## FAQ & Safety (quick answers)
- Twilio errors: **63016 “permission to send an SMS has not been enabled”** → ensure your number is WhatsApp-enabled; **63007 template not approved** → send a free-form session message within 24h or use an approved template; **63112 policy violation** → adjust content, shorten to <1600 chars, avoid links that trigger spam filters. Re-run `pnpm warelay status` to see the exact Twilio response body.
- Does this store my messages? Warelay only writes `~/.warelay/warelay.json` (config), `~/.warelay/credentials/` (WhatsApp Web auth), and `~/.warelay/sessions.json` (session IDs + timestamps). It does **not** persist message bodies beyond the session store. Logs print to stdout/stderr; redirect or rotate if needed.
- Does this store my messages? warelay only writes `~/.warelay/warelay.json` (config), `~/.warelay/credentials/` (WhatsApp Web auth), and `~/.warelay/sessions.json` (session IDs + timestamps). It does **not** persist message bodies beyond the session store. Logs print to stdout/stderr; redirect or rotate if needed.
- Personal WhatsApp safety: Automation on personal accounts can be rate-limited or logged out by WhatsApp. Use `--provider web` sparingly, keep messages human-like, and re-run `login` if the session is dropped.
- Limits to remember: WhatsApp text limit ~1600 chars; avoid rapid bursts—space sends by a few seconds; keep webhook replies under a couple seconds for good UX; command auto-replies time out after 600s by default.
- Deploy / keep running: Use `tmux` or `screen` for ad-hoc (`tmux new -s warelay -- pnpm warelay relay --provider twilio`). For long-running hosts, wrap `pnpm warelay relay ...` or `pnpm warelay webhook --ingress tailscale ...` in a systemd service or macOS LaunchAgent; ensure environment variables are loaded in that context.
+6
View File
@@ -0,0 +1,6 @@
#!/usr/bin/env node
import("../dist/index.js").then((mod) => {
if (mod?.program?.parseAsync) {
mod.program.parseAsync(process.argv);
}
});
+4 -4
View File
@@ -12,7 +12,7 @@ This guide shows the exact way to wire **warelay** to the Claude CLI so inbound
- Optional: set `ANTHROPIC_API_KEY` in your shell profile for non-interactive use.
## Create your warelay config
Warelay reads `~/.warelay/warelay.json` (JSON5 accepted). Add a command-mode reply that points at the Claude CLI:
warelay reads `~/.warelay/warelay.json` (JSON5 accepted). Add a command-mode reply that points at the Claude CLI:
```json5
{
@@ -38,13 +38,13 @@ Warelay reads `~/.warelay/warelay.json` (JSON5 accepted). Add a command-mode rep
```
Notes on this configuration:
- Warelay automatically injects a Claude identity prefix and the correct `--output-format`/`-p` flags when `command[0]` is `claude` and `claudeOutputFormat` is set.
- warelay automatically injects a Claude identity prefix and the correct `--output-format`/`-p` flags when `command[0]` is `claude` and `claudeOutputFormat` is set.
- Sessions are stored in `~/.warelay/sessions.json`; `scope: per-sender` keeps separate threads for each contact.
- `bodyPrefix` is added before the inbound message body that reaches Claude. The string above mirrors the built-in 1500-character WhatsApp guardrail.
## How the flow works
1. An inbound message (Twilio webhook, Twilio poller, or WhatsApp Web listener) arrives.
2. Warelay enqueues the command in a process-wide FIFO queue so only one Claude run happens at a time (`src/process/command-queue.ts`).
2. warelay enqueues the command in a process-wide FIFO queue so only one Claude run happens at a time (`src/process/command-queue.ts`).
3. Typing indicators are sent (Twilio) or `composing` presence is sent (Web) while Claude runs.
4. Claude stdout is parsed:
- JSON mode is handled automatically if you set `claudeOutputFormat: "json"`; otherwise text is used.
@@ -52,7 +52,7 @@ Notes on this configuration:
5. The reply (text and optional media) is sent back via the same provider that received the message.
## Media and attachments
- To send an image from Claude, include a line like `MEDIA:https://example.com/pic.jpg` in the output. Warelay will:
- To send an image from Claude, include a line like `MEDIA:https://example.com/pic.jpg` in the output. warelay will:
- Host local paths for Twilio using the media server/Tailscale Funnel.
- Send buffers directly for the Web provider.
- Inbound media is downloaded (≤5MB) and exposed to your templates as `{{MediaPath}}`, `{{MediaUrl}}`, and `{{MediaType}}`. You can mention this in your prompt if you want Claude to reason about the attachment.
+4 -4
View File
@@ -1,13 +1,13 @@
{
"name": "warelay",
"version": "0.1.0",
"version": "0.1.2",
"description": "WhatsApp relay CLI (send, monitor, webhook, auto-reply) using Twilio",
"type": "module",
"main": "dist/index.js",
"bin": {
"warelay": "dist/index.js",
"warely": "dist/index.js",
"wa": "dist/index.js"
"warelay": "bin/warelay.js",
"warely": "bin/warelay.js",
"wa": "bin/warelay.js"
},
"scripts": {
"dev": "tsx src/index.ts",
+67 -1
View File
@@ -1,3 +1,4 @@
import chalk from "chalk";
import { Command } from "commander";
import { sendCommand } from "../commands/send.js";
import { statusCommand } from "../commands/status.js";
@@ -17,11 +18,76 @@ import { spawnRelayTmux } from "./relay_tmux.js";
export function buildProgram() {
const program = new Command();
const PROGRAM_VERSION = "0.1.2";
const TAGLINE =
"Send, receive, and auto-reply on WhatsApp—Twilio-backed or QR-linked.";
program
.name("warelay")
.description("WhatsApp relay CLI (Twilio or WhatsApp Web session)")
.version("1.0.0");
.version(PROGRAM_VERSION);
const formatIntroLine = (version: string, rich = true) => {
const base = `📡 warelay ${version}${TAGLINE}`;
return rich && chalk.level > 0
? `${chalk.bold.cyan("📡 warelay")} ${chalk.white(version)} ${chalk.gray("—")} ${chalk.green(TAGLINE)}`
: base;
};
program.configureHelp({
optionTerm: (option) => chalk.yellow(option.flags),
subcommandTerm: (cmd) => chalk.green(cmd.name()),
});
program.configureOutput({
writeOut: (str) => {
const colored = str
.replace(/^Usage:/gm, chalk.bold.cyan("Usage:"))
.replace(/^Options:/gm, chalk.bold.cyan("Options:"))
.replace(/^Commands:/gm, chalk.bold.cyan("Commands:"));
process.stdout.write(colored);
},
writeErr: (str) => process.stderr.write(str),
outputError: (str, write) => write(chalk.red(str)),
});
if (process.argv.includes("-V") || process.argv.includes("--version")) {
console.log(formatIntroLine(PROGRAM_VERSION));
process.exit(0);
}
program.addHelpText("beforeAll", `\n${formatIntroLine(PROGRAM_VERSION)}\n`);
const examples = [
[
"warelay login --verbose",
"Link personal WhatsApp Web and show QR + connection logs.",
],
[
'warelay send --to +15551234567 --message "Hi" --provider web --json',
"Send via your web session and print JSON result.",
],
[
"warelay relay --provider auto --interval 5 --lookback 15 --verbose",
"Auto-reply loop: prefer Web when logged in, otherwise Twilio polling.",
],
[
"warelay webhook --ingress tailscale --port 42873 --path /webhook/whatsapp --verbose",
"Start webhook + Tailscale Funnel and update Twilio callbacks.",
],
[
"warelay status --limit 10 --lookback 60 --json",
"Show last 10 messages from the past hour as JSON.",
],
] as const;
const fmtExamples = examples
.map(([cmd, desc]) => ` ${chalk.green(cmd)}\n ${chalk.gray(desc)}`)
.join("\n");
program.addHelpText(
"afterAll",
`\n${chalk.bold.cyan("Examples:")}\n${fmtExamples}\n`,
);
program
.command("login")
+1 -1
View File
@@ -48,7 +48,7 @@ export async function createWaSocket(printQr: boolean, verbose: boolean) {
version,
logger,
printQRInTerminal: false,
browser: ["Warelay", "CLI", "1.0.0"],
browser: ["warelay", "cli", "0.1.2"],
syncFullHistory: false,
markOnlineOnConnect: false,
});