Cleanup: remove deprecated up alias and update ingress docs
This commit is contained in:
@@ -3,14 +3,14 @@
|
||||
## 0.1.0 — 2025-11-25
|
||||
|
||||
### CLI & Providers
|
||||
- Bundles a single `warelay` CLI with commands for `send`, `relay`, `status`, `webhook`, `up`, `login`, and tmux helpers `relay:tmux` / `relay:tmux:attach` (see `src/cli/program.ts`); `webhook` now accepts `--ingress tailscale|none` with `up` as an alias for the Tailscale path.
|
||||
- Bundles a single `warelay` CLI with commands for `send`, `relay`, `status`, `webhook`, `login`, and tmux helpers `relay:tmux` / `relay:tmux:attach` (see `src/cli/program.ts`); `webhook` accepts `--ingress tailscale|none`.
|
||||
- Supports two messaging backends: **Twilio** (default) and **personal WhatsApp Web**; `relay --provider auto` selects Web when a cached login exists, otherwise falls back to Twilio polling (`provider-web.ts`, `cli/program.ts`).
|
||||
- `send` can target either provider, optionally wait for delivery status (Twilio only), output JSON, dry-run payloads, and attach media (`commands/send.ts`).
|
||||
- `status` merges inbound + outbound Twilio traffic with formatted lines or JSON output (`commands/status.ts`, `twilio/messages.ts`).
|
||||
|
||||
### Webhook, Funnel & Port Management
|
||||
- `webhook` starts an Express server for inbound Twilio callbacks, logs requests, and optionally auto-replies with static text or config-driven replies (`twilio/webhook.ts`, `commands/webhook.ts`).
|
||||
- `up` automates end-to-end webhook setup: ensures required binaries, enables Tailscale Funnel, starts the webhook on the chosen port/path, discovers the WhatsApp sender SID, and updates Twilio webhook URLs with multiple fallbacks (`commands/up.ts`, `infra/tailscale.ts`, `twilio/update-webhook.ts`, `twilio/senders.ts`).
|
||||
- `webhook --ingress tailscale` automates end-to-end webhook setup: ensures required binaries, enables Tailscale Funnel, starts the webhook on the chosen port/path, discovers the WhatsApp sender SID, and updates Twilio webhook URLs with multiple fallbacks (`commands/up.ts`, `infra/tailscale.ts`, `twilio/update-webhook.ts`, `twilio/senders.ts`).
|
||||
- Guardrails detect busy ports with helpful diagnostics and aborts when conflicts are found (`infra/ports.ts`).
|
||||
|
||||
### Auto-Reply Engine
|
||||
|
||||
11
README.md
11
README.md
@@ -15,7 +15,7 @@ Install from npm (global): `npm install -g warelay` (Node 22+). Then choose **on
|
||||
2. Send a message: `warelay send --to +12345550000 --message "Hi from warelay"`.
|
||||
3. Receive replies:
|
||||
- Polling (no ingress): `warelay relay --provider twilio --interval 5 --lookback 10`
|
||||
- Webhook + public URL via Tailscale Funnel: `warelay webhook --ingress tailscale --port 42873 --path /webhook/whatsapp --verbose` (alias: `warelay up`)
|
||||
- Webhook + public URL via Tailscale Funnel: `warelay webhook --ingress tailscale --port 42873 --path /webhook/whatsapp --verbose`
|
||||
|
||||
> Already developing locally? You can still run `pnpm install` and `pnpm warelay ...` from the repo, but end users only need the npm package.
|
||||
|
||||
@@ -23,7 +23,7 @@ Install from npm (global): `npm install -g warelay` (Node 22+). Then choose **on
|
||||
- **Two providers:** Twilio (default) for reliable delivery + status; Web provider for quick personal sends/receives via QR login.
|
||||
- **Auto-replies:** Static templates or external commands (Claude-aware), with per-sender or global sessions and `/new` resets.
|
||||
- Claude setup guide: see `docs/claude-config.md` for the exact Claude CLI configuration we support.
|
||||
- **Webhook in one go:** `warelay webhook --ingress tailscale` enables Tailscale Funnel, runs the webhook server, and updates the Twilio sender callback URL (alias: `warelay up`).
|
||||
- **Webhook in one go:** `warelay webhook --ingress tailscale` enables Tailscale Funnel, runs the webhook server, and updates the Twilio sender callback URL.
|
||||
- **Polling fallback:** `relay` polls Twilio when webhooks aren’t available; works headless.
|
||||
- **Status + delivery tracking:** `status` shows recent inbound/outbound; `send` can wait for final Twilio status.
|
||||
|
||||
@@ -34,11 +34,10 @@ Install from npm (global): `npm install -g warelay` (Node 22+). Then choose **on
|
||||
| `warelay relay` | Auto-reply loop (poll Twilio or listen on Web) | `--provider <auto\|twilio\|web>` `--interval <sec>` `--lookback <min>` `--verbose` |
|
||||
| `warelay status` | Show recent sent/received messages | `--limit <n>` `--lookback <min>` `--json` |
|
||||
| `warelay webhook` | Run inbound webhook (`ingress=tailscale` updates Twilio; `none` is local-only) | `--ingress tailscale\|none` `--port <port>` `--path <path>` `--reply <text>` `--verbose` `--yes` `--dry-run` |
|
||||
| `warelay up` | Alias: `warelay webhook --ingress tailscale` | `--port <port>` `--path <path>` `--verbose` `--yes` `--dry-run` |
|
||||
| `warelay login` | Link personal WhatsApp Web via QR | `--verbose` |
|
||||
|
||||
### Sending images
|
||||
- Twilio: `warelay send --to +1... --message "Hi" --media ./pic.jpg --serve-media` (needs `warelay webhook`/`up` or `--serve-media` to auto-host via Funnel; max 5 MB).
|
||||
- Twilio: `warelay send --to +1... --message "Hi" --media ./pic.jpg --serve-media` (needs `warelay webhook --ingress tailscale` or `--serve-media` to auto-host via Funnel; max 5 MB).
|
||||
- Web: `warelay send --provider web --media ./pic.jpg --message "Hi"` (local path or URL; no hosting needed).
|
||||
- Auto-replies can attach `mediaUrl` in `~/.warelay/warelay.json` (used alongside `text` when present).
|
||||
|
||||
@@ -111,7 +110,7 @@ Templating tokens: `{{Body}}`, `{{BodyStripped}}`, `{{From}}`, `{{To}}`, `{{Mess
|
||||
|
||||
## Webhook & Tailscale Flow
|
||||
- `warelay webhook --ingress none` starts the local Express server on your chosen port/path; add `--reply "Got it"` for a static reply when no config file is present.
|
||||
- `warelay webhook --ingress tailscale` (alias: `warelay up`) enables Tailscale Funnel, prints the public URL (`https://<tailnet-host><path>`), starts the webhook, discovers the WhatsApp sender SID, and updates Twilio callbacks to the Funnel URL.
|
||||
- `warelay webhook --ingress tailscale` enables Tailscale Funnel, prints the public URL (`https://<tailnet-host><path>`), starts the webhook, discovers the WhatsApp sender SID, and updates Twilio callbacks to the Funnel URL.
|
||||
- If Funnel is not allowed on your tailnet, the CLI exits with guidance; you can still use `relay --provider twilio` to poll without webhooks.
|
||||
|
||||
## Troubleshooting Tips
|
||||
@@ -125,5 +124,5 @@ Templating tokens: `{{Body}}`, `{{BodyStripped}}`, `{{From}}`, `{{To}}`, `{{Mess
|
||||
- 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 up ...` in a systemd service or macOS LaunchAgent; ensure environment variables are loaded in that context.
|
||||
- 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.
|
||||
- Rotating credentials: Update `.env` (Twilio keys), rerun your process; for Web provider, delete `~/.warelay/credentials/` and rerun `pnpm warelay login` to relink.
|
||||
|
||||
@@ -70,7 +70,7 @@ Notes on this configuration:
|
||||
## Troubleshooting tips
|
||||
- Command takes too long: lower `timeoutSeconds` or simplify the prompt. Timeouts kill the Claude process.
|
||||
- No reply: ensure the sender number is in `allowFrom` (or remove the allowlist), and confirm `claude login` was run in the same environment.
|
||||
- Media fails on Twilio: run `warelay up` (or `warelay webhook --serve-media` via `send --serve-media`) so the media host is reachable over HTTPS.
|
||||
- Media fails on Twilio: run `warelay webhook --ingress tailscale` (or `warelay webhook --serve-media` via `send --serve-media`) so the media host is reachable over HTTPS.
|
||||
- Stuck queue: enable `--verbose` to see “queued for …ms” messages and confirm commands are draining. Use `pnpm vitest` to run unit tests if you change queue logic.
|
||||
|
||||
## Minimal text-only variant
|
||||
|
||||
@@ -28,12 +28,12 @@ This document defines how `warelay` should handle sending and replying with imag
|
||||
### Twilio
|
||||
- Twilio API requires a public HTTPS `MediaUrl`; it will not accept local paths.
|
||||
- Hosting strategy: reuse the webhook/Funnel port.
|
||||
- When `--media` is a local path, copy to temp dir (`~/.warelay/media/<uuid>`), serve at `/media/<uuid>` on the existing Express app started for webhook/up, or spin up a short-lived server on demand for `send`.
|
||||
- When `--media` is a local path, copy to temp dir (`~/.warelay/media/<uuid>`), serve at `/media/<uuid>` on the existing Express app started for webhook, or spin up a short-lived server on demand for `send`.
|
||||
- `MediaUrl` = `https://<tailnet-host>.ts.net/media/<uuid>`.
|
||||
- Files auto-removed after TTL (default 2 minutes) or after first successful fetch (best-effort).
|
||||
- Enforce size limit 5 MB; reject early with clear error.
|
||||
- If `--media` is already an HTTPS URL, pass through unchanged.
|
||||
- Fallback: if Funnel is not enabled (or host unknown) and a local path is provided, fail with guidance to run `warelay up` (or pass a URL instead).
|
||||
- Fallback: if Funnel is not enabled (or host unknown) and a local path is provided, fail with guidance to run `warelay webhook --ingress tailscale` (or pass a URL instead).
|
||||
|
||||
## Hosting/Server Details
|
||||
- Extend `startWebhook` Express app:
|
||||
@@ -58,7 +58,7 @@ This document defines how `warelay` should handle sending and replying with imag
|
||||
- Size guard: only download if ≤5 MB; else skip and log.
|
||||
|
||||
## Errors & Messaging
|
||||
- Local path with twilio + Funnel disabled → error: “Twilio media needs a public URL; start `warelay up`/`warelay webhook` with Funnel or pass an https:// URL.”
|
||||
- Local path with twilio + Funnel disabled → error: “Twilio media needs a public URL; start `warelay webhook --ingress tailscale` or pass an https:// URL.”
|
||||
- File too large (>5 MB) → “Media exceeds 5 MB limit; resize or host elsewhere.”
|
||||
- Download failure for web provider → “Failed to load media from <source>; skipping send.”
|
||||
|
||||
@@ -71,4 +71,4 @@ This document defines how `warelay` should handle sending and replying with imag
|
||||
## Open Decisions (confirm before coding)
|
||||
- TTL for temp media (proposal: 2 minutes, cleanup at start + interval).
|
||||
- One-file-per-send vs. batching: default to one-file-per-send; multi-attach not supported.
|
||||
- Should `warelay send --provider twilio --media` implicitly start the media server (even if webhook not running), or require `warelay up/webhook` already active? (Proposal: auto-start lightweight server on demand, auto-stop after media is fetched or TTL.)
|
||||
- Should `warelay send --provider twilio --media` implicitly start the media server (even if webhook not running), or require `warelay webhook` already active? (Proposal: auto-start lightweight server on demand, auto-stop after media is fetched or TTL.)
|
||||
|
||||
@@ -2,7 +2,6 @@ import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
const sendCommand = vi.fn();
|
||||
const statusCommand = vi.fn();
|
||||
const upCommand = vi.fn().mockResolvedValue({ server: undefined });
|
||||
const webhookCommand = vi.fn().mockResolvedValue(undefined);
|
||||
const ensureTwilioEnv = vi.fn();
|
||||
const loginWeb = vi.fn();
|
||||
@@ -24,7 +23,6 @@ const runtime = {
|
||||
|
||||
vi.mock("../commands/send.js", () => ({ sendCommand }));
|
||||
vi.mock("../commands/status.js", () => ({ statusCommand }));
|
||||
vi.mock("../commands/up.js", () => ({ upCommand }));
|
||||
vi.mock("../commands/webhook.js", () => ({ webhookCommand }));
|
||||
vi.mock("../env.js", () => ({ ensureTwilioEnv }));
|
||||
vi.mock("../runtime.js", () => ({ defaultRuntime: runtime }));
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Command } from "commander";
|
||||
import { sendCommand } from "../commands/send.js";
|
||||
import { statusCommand } from "../commands/status.js";
|
||||
import { upCommand } from "../commands/up.js";
|
||||
import { webhookCommand } from "../commands/webhook.js";
|
||||
import { ensureTwilioEnv } from "../env.js";
|
||||
import { danger, info, setVerbose, setYes, warn } from "../globals.js";
|
||||
@@ -221,44 +220,6 @@ Examples:
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command("up")
|
||||
.description(
|
||||
"Alias: webhook --ingress tailscale (Funnel + Twilio callback)",
|
||||
)
|
||||
.option("-p, --port <port>", "Port to listen on", "42873")
|
||||
.option("--path <path>", "Webhook path", "/webhook/whatsapp")
|
||||
.option("--verbose", "Verbose logging during setup/webhook", false)
|
||||
.option("-y, --yes", "Auto-confirm prompts when possible", false)
|
||||
.option(
|
||||
"--dry-run",
|
||||
"Print planned actions without touching network",
|
||||
false,
|
||||
)
|
||||
// istanbul ignore next
|
||||
.action(async (opts) => {
|
||||
setVerbose(Boolean(opts.verbose));
|
||||
setYes(Boolean(opts.yes));
|
||||
const deps = createDefaultDeps();
|
||||
try {
|
||||
const { server } = await upCommand(opts, deps, defaultRuntime);
|
||||
if (!server) {
|
||||
defaultRuntime.log(info("Up dry-run complete; no server started."));
|
||||
return;
|
||||
}
|
||||
process.on("SIGINT", () => {
|
||||
server.close(() => {
|
||||
console.log("\n👋 Webhook stopped");
|
||||
defaultRuntime.exit(0);
|
||||
});
|
||||
});
|
||||
await deps.waitForever();
|
||||
} catch (err) {
|
||||
defaultRuntime.error(String(err));
|
||||
defaultRuntime.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
program
|
||||
.command("relay:tmux")
|
||||
.description(
|
||||
|
||||
Reference in New Issue
Block a user