@mulmobridge/twilio-sms 0.1.0 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +59 -0
  2. package/dist/index.js +12 -10
  3. package/package.json +7 -5
package/README.md CHANGED
@@ -51,6 +51,24 @@ Text the Twilio number — you'll get a reply.
51
51
  | `MULMOCLAUDE_AUTH_TOKEN` | no | auto | MulmoClaude bearer token override |
52
52
  | `MULMOCLAUDE_API_URL` | no | `http://localhost:3001` | MulmoClaude server URL |
53
53
 
54
+ ### Auth token persistence across server restarts
55
+
56
+ The MulmoClaude server regenerates a fresh bearer token on every startup and writes it to `~/mulmoclaude/.session-token`. The bridge reads that file once at launch and keeps the token in memory — so if the server restarts while the bridge is running, the bridge keeps using the **old** token and every API call returns **401**, silently.
57
+
58
+ **Fix**: set `MULMOCLAUDE_AUTH_TOKEN` to the same long random value on **both** the server and the bridge. The server uses it verbatim instead of regenerating, so the token survives restarts and the bridge stays authenticated.
59
+
60
+ ```bash
61
+ # Server (one-time setup — same value across restarts)
62
+ MULMOCLAUDE_AUTH_TOKEN=long-random-string yarn dev
63
+
64
+ # Bridge (separate process / machine — same value)
65
+ MULMOCLAUDE_AUTH_TOKEN=long-random-string \
66
+ <bridge-specific-envs> \
67
+ npx <this-package>@latest
68
+ ```
69
+
70
+ Recommended: at least 32 characters of random data (the server logs a warning at startup for shorter values).
71
+
54
72
  ## How it works
55
73
 
56
74
  1. Twilio posts form-encoded `From`, `To`, `Body`, `MessageSid` to `/sms` every time an SMS arrives.
@@ -72,3 +90,44 @@ Text the Twilio number — you'll get a reply.
72
90
  - `TWILIO_PUBLIC_URL` is strongly recommended — without it, anyone who finds your webhook can impersonate Twilio and converse with your agent.
73
91
  - Trial Twilio accounts can only SMS pre-verified numbers. Upgrade to production before real use.
74
92
  - SMS is plaintext — don't discuss secrets over it. Use Signal / WhatsApp / Matrix instead for sensitive content.
93
+
94
+ ## Ecosystem
95
+
96
+ Part of the [`@mulmobridge/*`](https://www.npmjs.com/~mulmobridge) package family.
97
+
98
+ **Shared libraries:**
99
+
100
+ - [`@mulmobridge/client`](https://www.npmjs.com/package/@mulmobridge/client) — socket.io client library used by every bridge below
101
+ - [`@mulmobridge/protocol`](https://www.npmjs.com/package/@mulmobridge/protocol) — wire types and constants
102
+ - [`@mulmobridge/chat-service`](https://www.npmjs.com/package/@mulmobridge/chat-service) — server-side relay + session store
103
+ - [`@mulmobridge/relay`](https://www.npmjs.com/package/@mulmobridge/relay) — Cloudflare Workers webhook proxy
104
+ - [`@mulmobridge/mock-server`](https://www.npmjs.com/package/@mulmobridge/mock-server) — mock server for local bridge development
105
+
106
+ **Bridges** (one npm package per platform):
107
+
108
+ - [`@mulmobridge/bluesky`](https://www.npmjs.com/package/@mulmobridge/bluesky) — Bluesky DMs over atproto
109
+ - [`@mulmobridge/chatwork`](https://www.npmjs.com/package/@mulmobridge/chatwork) — Chatwork (Japanese business chat)
110
+ - [`@mulmobridge/cli`](https://www.npmjs.com/package/@mulmobridge/cli) — interactive terminal bridge
111
+ - [`@mulmobridge/discord`](https://www.npmjs.com/package/@mulmobridge/discord) — Discord bot via Gateway
112
+ - [`@mulmobridge/email`](https://www.npmjs.com/package/@mulmobridge/email) — IMAP poll + SMTP reply, threading preserved
113
+ - [`@mulmobridge/google-chat`](https://www.npmjs.com/package/@mulmobridge/google-chat) — Google Chat via MulmoBridge relay
114
+ - [`@mulmobridge/irc`](https://www.npmjs.com/package/@mulmobridge/irc) — IRC (Libera, Freenode, custom)
115
+ - [`@mulmobridge/line`](https://www.npmjs.com/package/@mulmobridge/line) — LINE Messaging API via MulmoBridge relay
116
+ - [`@mulmobridge/line-works`](https://www.npmjs.com/package/@mulmobridge/line-works) — LINE Works (enterprise LINE)
117
+ - [`@mulmobridge/mastodon`](https://www.npmjs.com/package/@mulmobridge/mastodon) — Mastodon DMs + mentions
118
+ - [`@mulmobridge/matrix`](https://www.npmjs.com/package/@mulmobridge/matrix) — Matrix / Element
119
+ - [`@mulmobridge/mattermost`](https://www.npmjs.com/package/@mulmobridge/mattermost) — Mattermost
120
+ - [`@mulmobridge/messenger`](https://www.npmjs.com/package/@mulmobridge/messenger) — Facebook Messenger via MulmoBridge relay
121
+ - [`@mulmobridge/nostr`](https://www.npmjs.com/package/@mulmobridge/nostr) — Nostr NIP-04 encrypted DMs
122
+ - [`@mulmobridge/rocketchat`](https://www.npmjs.com/package/@mulmobridge/rocketchat) — Rocket.Chat
123
+ - [`@mulmobridge/signal`](https://www.npmjs.com/package/@mulmobridge/signal) — Signal via signal-cli-rest-api
124
+ - [`@mulmobridge/slack`](https://www.npmjs.com/package/@mulmobridge/slack) — Slack Socket Mode
125
+ - [`@mulmobridge/teams`](https://www.npmjs.com/package/@mulmobridge/teams) — Microsoft Teams via Bot Framework
126
+ - [`@mulmobridge/telegram`](https://www.npmjs.com/package/@mulmobridge/telegram) — Telegram bot
127
+ - [`@mulmobridge/twilio-sms`](https://www.npmjs.com/package/@mulmobridge/twilio-sms) — SMS via Twilio Programmable Messaging ← **this package**
128
+ - [`@mulmobridge/viber`](https://www.npmjs.com/package/@mulmobridge/viber) — Viber Public Account bots
129
+ - [`@mulmobridge/webhook`](https://www.npmjs.com/package/@mulmobridge/webhook) — generic HTTP webhook bridge
130
+ - [`@mulmobridge/whatsapp`](https://www.npmjs.com/package/@mulmobridge/whatsapp) — WhatsApp Cloud API via MulmoBridge relay
131
+ - [`@mulmobridge/xmpp`](https://www.npmjs.com/package/@mulmobridge/xmpp) — XMPP / Jabber
132
+ - [`@mulmobridge/zulip`](https://www.npmjs.com/package/@mulmobridge/zulip) — Zulip
133
+
package/dist/index.js CHANGED
@@ -27,17 +27,22 @@ import "dotenv/config";
27
27
  import crypto from "crypto";
28
28
  import express from "express";
29
29
  import { createBridgeClient, chunkText } from "@mulmobridge/client";
30
+ import { parseCsvSet } from "@mulmoclaude/common";
30
31
  const TRANSPORT_ID = "twilio-sms";
31
32
  const MAX_SMS_LEN = 1_600; // Twilio concatenates segments up to 1600 chars
32
33
  const FETCH_TIMEOUT_MS = 15_000;
33
34
  const PORT = Number(process.env.TWILIO_WEBHOOK_PORT) || 3010;
34
- const accountSid = process.env.TWILIO_ACCOUNT_SID;
35
- const authToken = process.env.TWILIO_AUTH_TOKEN;
36
- const fromNumber = process.env.TWILIO_FROM_NUMBER;
37
- if (!accountSid || !authToken || !fromNumber) {
38
- console.error("TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_FROM_NUMBER are required.\n" + "See README for setup instructions.");
39
- process.exit(1);
35
+ function readRequiredEnv() {
36
+ const accountSid = process.env.TWILIO_ACCOUNT_SID;
37
+ const authToken = process.env.TWILIO_AUTH_TOKEN;
38
+ const fromNumber = process.env.TWILIO_FROM_NUMBER;
39
+ if (!accountSid || !authToken || !fromNumber) {
40
+ console.error("TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN, and TWILIO_FROM_NUMBER are required.\nSee README for setup instructions.");
41
+ process.exit(1);
42
+ }
43
+ return { accountSid, authToken, fromNumber };
40
44
  }
45
+ const { accountSid, authToken, fromNumber } = readRequiredEnv();
41
46
  const publicUrl = process.env.TWILIO_PUBLIC_URL?.replace(/\/$/, "");
42
47
  const allowUnverified = process.env.TWILIO_ALLOW_UNVERIFIED === "1";
43
48
  if (!publicUrl && !allowUnverified) {
@@ -49,10 +54,7 @@ if (!publicUrl && !allowUnverified) {
49
54
  if (!publicUrl && allowUnverified) {
50
55
  console.warn("[twilio-sms] ⚠ TWILIO_ALLOW_UNVERIFIED=1 — signature verification DISABLED. Use only in local testing.");
51
56
  }
52
- const allowedNumbers = new Set((process.env.TWILIO_ALLOWED_NUMBERS ?? "")
53
- .split(",")
54
- .map((num) => num.trim())
55
- .filter(Boolean));
57
+ const allowedNumbers = parseCsvSet(process.env.TWILIO_ALLOWED_NUMBERS);
56
58
  const allowAll = allowedNumbers.size === 0;
57
59
  const mulmo = createBridgeClient({ transportId: TRANSPORT_ID });
58
60
  mulmo.onPush((pushEvent) => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulmobridge/twilio-sms",
3
- "version": "0.1.0",
3
+ "version": "1.0.0",
4
4
  "description": "Twilio SMS bridge for MulmoBridge — inbound SMS via webhook, outbound via Twilio REST API",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -16,19 +16,21 @@
16
16
  "build": "tsc",
17
17
  "prepack": "yarn build",
18
18
  "typecheck": "tsc --noEmit",
19
- "test": "echo no tests yet"
19
+ "test": "echo no tests yet",
20
+ "lint": "eslint src"
20
21
  },
21
22
  "license": "MIT",
22
23
  "author": "Receptron Team",
23
24
  "dependencies": {
24
- "@mulmobridge/client": "^0.1.0",
25
- "@mulmobridge/protocol": "^0.1.0",
25
+ "@mulmobridge/client": "^1.0.0",
26
+ "@mulmobridge/protocol": "^1.0.0",
27
+ "@mulmoclaude/common": "^1.1.0",
26
28
  "dotenv": "^17.0.0",
27
29
  "express": "^5.0.0"
28
30
  },
29
31
  "devDependencies": {
30
32
  "@types/express": "^5.0.0",
31
- "@types/node": "^25.6.0",
33
+ "@types/node": "^26.1.1",
32
34
  "typescript": "^6.0.3"
33
35
  }
34
36
  }