@diegoaltoworks/talker 0.8.0 → 0.10.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.
@@ -2,7 +2,13 @@
2
2
  * Structured JSON logger
3
3
  *
4
4
  * Silent during tests unless DEBUG=true.
5
+ * Automatically redacts phone numbers in structured log data.
5
6
  */
7
+ /**
8
+ * Redact a phone number, keeping only the last 4 digits.
9
+ * E.g. "+15551234567" -> "***4567"
10
+ */
11
+ export declare function redactPhone(phone: string): string;
6
12
  export declare const logger: {
7
13
  info: (message: string, data?: Record<string, unknown>) => void;
8
14
  warn: (message: string, data?: Record<string, unknown>) => void;
@@ -1 +1 @@
1
- {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAuBH,eAAO,MAAM,MAAM;oBACD,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;oBACtC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;qBACrC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CACxD,CAAC"}
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/core/logger.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAYH;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKjD;AA+BD,eAAO,MAAM,MAAM;oBACD,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;oBACtC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;qBACrC,MAAM,SAAS,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC;CACxD,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=logger.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.test.d.ts","sourceRoot":"","sources":["../../src/core/logger.test.ts"],"names":[],"mappings":""}
package/dist/index.d.ts CHANGED
@@ -58,5 +58,8 @@ export { persistFinalSession, persistSession } from "./db/persist";
58
58
  export type { SessionRecord, MessageRecord } from "./db/sessions";
59
59
  export { callRoutes } from "./routes/call";
60
60
  export { smsRoutes } from "./routes/sms";
61
- export { logger } from "./core/logger";
61
+ export { logger, redactPhone } from "./core/logger";
62
+ export { twilioSignatureMiddleware, validateTwilioSignature } from "./middleware/twilio-signature";
63
+ export { rateLimitMiddleware } from "./middleware/rate-limit";
64
+ export { inputSanitizeMiddleware } from "./middleware/input-sanitize";
62
65
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAGjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,OAAO,GACR,MAAM,SAAS,CAAC;AAGjB,YAAY,EACV,cAAc,EACd,UAAU,EACV,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,UAAU,GACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,WAAW,EACX,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,YAAY,GACb,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAG5C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGlE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EAAE,MAAM,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AAGH,OAAO,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAGjD,OAAO,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AACtD,YAAY,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAGrD,YAAY,EACV,YAAY,EACZ,kBAAkB,EAClB,qBAAqB,EACrB,YAAY,EACZ,aAAa,EACb,gBAAgB,EAChB,OAAO,EACP,WAAW,EACX,cAAc,EACd,gBAAgB,EAChB,OAAO,GACR,MAAM,SAAS,CAAC;AAGjB,YAAY,EACV,cAAc,EACd,UAAU,EACV,kBAAkB,EAClB,SAAS,EACT,WAAW,EACX,iBAAiB,EACjB,kBAAkB,EAClB,WAAW,EACX,oBAAoB,EACpB,UAAU,EACV,eAAe,EACf,UAAU,GACX,MAAM,SAAS,CAAC;AAGjB,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAChE,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,gBAAgB,CAAC;AACxB,OAAO,EACL,kBAAkB,EAClB,UAAU,EACV,YAAY,EACZ,gBAAgB,EAChB,mBAAmB,EACnB,mBAAmB,EACnB,UAAU,EACV,iBAAiB,EACjB,aAAa,EACb,aAAa,EACb,gBAAgB,EAChB,eAAe,EACf,wBAAwB,EACxB,oBAAoB,EACpB,aAAa,EACb,aAAa,EACb,YAAY,EACZ,WAAW,GACZ,MAAM,gBAAgB,CAAC;AAGxB,OAAO,EACL,WAAW,EACX,QAAQ,EACR,aAAa,EACb,mBAAmB,EACnB,aAAa,EACb,YAAY,GACb,MAAM,cAAc,CAAC;AAGtB,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,EAAE,WAAW,EAAE,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAC9D,OAAO,EAAE,sBAAsB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAG/C,OAAO,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAG5C,OAAO,EAAE,YAAY,EAAE,WAAW,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAC;AAC7C,OAAO,EAAE,mBAAmB,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AACnE,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,MAAM,eAAe,CAAC;AAGlE,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAGzC,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AAGpD,OAAO,EAAE,yBAAyB,EAAE,uBAAuB,EAAE,MAAM,+BAA+B,CAAC;AACnG,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,6BAA6B,CAAC"}
package/dist/index.js CHANGED
@@ -86,6 +86,7 @@ __export(index_exports, {
86
86
  getVoiceConfig: () => getVoiceConfig,
87
87
  incrementNoSpeechRetries: () => incrementNoSpeechRetries,
88
88
  initDbClient: () => initDbClient,
89
+ inputSanitizeMiddleware: () => inputSanitizeMiddleware,
89
90
  loadFlowsFromDirectory: () => loadFlowsFromDirectory,
90
91
  loadPhrases: () => loadPhrases,
91
92
  logger: () => logger,
@@ -95,6 +96,8 @@ __export(index_exports, {
95
96
  processFlow: () => processFlow,
96
97
  processIncoming: () => processIncoming,
97
98
  processOutgoing: () => processOutgoing,
99
+ rateLimitMiddleware: () => rateLimitMiddleware,
100
+ redactPhone: () => redactPhone,
98
101
  resetNoSpeechRetries: () => resetNoSpeechRetries,
99
102
  runMigrations: () => runMigrations,
100
103
  sayTwiml: () => sayTwiml,
@@ -107,7 +110,9 @@ __export(index_exports, {
107
110
  startCleanup: () => startCleanup,
108
111
  stopCleanup: () => stopCleanup,
109
112
  transferTwiml: () => transferTwiml,
110
- updateFlowParams: () => updateFlowParams
113
+ twilioSignatureMiddleware: () => twilioSignatureMiddleware,
114
+ updateFlowParams: () => updateFlowParams,
115
+ validateTwilioSignature: () => validateTwilioSignature
111
116
  });
112
117
  module.exports = __toCommonJS(index_exports);
113
118
 
@@ -116,13 +121,31 @@ var isTestEnv = process.env.NODE_ENV === "test" || typeof Bun !== "undefined" &&
116
121
  var isDebug = process.env.DEBUG === "true";
117
122
  var isSilent = isTestEnv && !isDebug;
118
123
  var timestamp = () => (/* @__PURE__ */ new Date()).toISOString();
124
+ function redactPhone(phone) {
125
+ if (!phone || phone === "unknown") return phone;
126
+ const digits = phone.replace(/\D/g, "");
127
+ if (digits.length <= 4) return "***";
128
+ return `***${digits.slice(-4)}`;
129
+ }
130
+ function redactData(data) {
131
+ if (!data) return data;
132
+ const redacted = {};
133
+ for (const [key, value] of Object.entries(data)) {
134
+ if ((key === "phoneNumber" || key === "phone") && typeof value === "string") {
135
+ redacted[key] = redactPhone(value);
136
+ } else {
137
+ redacted[key] = value;
138
+ }
139
+ }
140
+ return redacted;
141
+ }
119
142
  var log = (level, message, data) => {
120
143
  if (isSilent) return;
121
144
  const entry = {
122
145
  timestamp: timestamp(),
123
146
  level,
124
147
  message,
125
- ...data
148
+ ...redactData(data)
126
149
  };
127
150
  console.log(JSON.stringify(entry));
128
151
  };
@@ -553,6 +576,140 @@ function testModeDetectIntent(message) {
553
576
  // src/routes/call/index.ts
554
577
  var import_hono = require("hono");
555
578
 
579
+ // src/middleware/input-sanitize.ts
580
+ var DEFAULT_MAX_INPUT_LENGTH = 1e3;
581
+ function truncateInput(input, maxLength) {
582
+ if (input.length <= maxLength) return input;
583
+ return input.substring(0, maxLength);
584
+ }
585
+ function inputSanitizeMiddleware(maxInputLength) {
586
+ const maxLen = maxInputLength ?? DEFAULT_MAX_INPUT_LENGTH;
587
+ return async (c, next) => {
588
+ const body = await c.req.parseBody();
589
+ let truncated = false;
590
+ if (typeof body.SpeechResult === "string" && body.SpeechResult.length > maxLen) {
591
+ logger.warn("input truncated: SpeechResult", {
592
+ original: body.SpeechResult.length,
593
+ max: maxLen
594
+ });
595
+ body.SpeechResult = truncateInput(body.SpeechResult, maxLen);
596
+ truncated = true;
597
+ }
598
+ if (typeof body.Body === "string" && body.Body.length > maxLen) {
599
+ logger.warn("input truncated: Body", {
600
+ original: body.Body.length,
601
+ max: maxLen
602
+ });
603
+ body.Body = truncateInput(body.Body, maxLen);
604
+ truncated = true;
605
+ }
606
+ if (truncated) {
607
+ c.set("sanitizedBody", body);
608
+ }
609
+ return next();
610
+ };
611
+ }
612
+
613
+ // src/middleware/rate-limit.ts
614
+ var DEFAULT_MAX_REQUESTS = 30;
615
+ var DEFAULT_WINDOW_MS = 6e4;
616
+ var rateLimitStore = /* @__PURE__ */ new Map();
617
+ var cleanupTimer2 = null;
618
+ function ensureCleanup(windowMs) {
619
+ if (cleanupTimer2) return;
620
+ cleanupTimer2 = setInterval(() => {
621
+ const cutoff = Date.now() - windowMs * 2;
622
+ for (const [key, entry] of rateLimitStore) {
623
+ entry.timestamps = entry.timestamps.filter((t) => t > cutoff);
624
+ if (entry.timestamps.length === 0) {
625
+ rateLimitStore.delete(key);
626
+ }
627
+ }
628
+ }, windowMs);
629
+ }
630
+ function checkRateLimit(phoneNumber, maxRequests, windowMs) {
631
+ const now = Date.now();
632
+ const cutoff = now - windowMs;
633
+ let entry = rateLimitStore.get(phoneNumber);
634
+ if (!entry) {
635
+ entry = { timestamps: [] };
636
+ rateLimitStore.set(phoneNumber, entry);
637
+ }
638
+ entry.timestamps = entry.timestamps.filter((t) => t > cutoff);
639
+ if (entry.timestamps.length >= maxRequests) {
640
+ return false;
641
+ }
642
+ entry.timestamps.push(now);
643
+ return true;
644
+ }
645
+ function rateLimitMiddleware(config) {
646
+ const maxRequests = config?.maxRequests ?? DEFAULT_MAX_REQUESTS;
647
+ const windowMs = config?.windowMs ?? DEFAULT_WINDOW_MS;
648
+ ensureCleanup(windowMs);
649
+ return async (c, next) => {
650
+ const body = await c.req.parseBody();
651
+ const phoneNumber = (body.From || "unknown").trim();
652
+ if (!checkRateLimit(phoneNumber, maxRequests, windowMs)) {
653
+ logger.warn("rate limit exceeded", { phoneNumber, path: c.req.path });
654
+ const twiml = `<?xml version="1.0" encoding="UTF-8"?>
655
+ <Response>
656
+ <Say>Please try again in a moment.</Say>
657
+ </Response>`;
658
+ return c.text(twiml, 429, { "Content-Type": "text/xml" });
659
+ }
660
+ return next();
661
+ };
662
+ }
663
+
664
+ // src/middleware/twilio-signature.ts
665
+ var import_node_crypto = require("node:crypto");
666
+ function computeTwilioSignature(authToken, url, params) {
667
+ const sortedKeys = Object.keys(params).sort();
668
+ let data = url;
669
+ for (const key of sortedKeys) {
670
+ data += key + params[key];
671
+ }
672
+ return (0, import_node_crypto.createHmac)("sha1", authToken).update(data).digest("base64");
673
+ }
674
+ function validateTwilioSignature(authToken, signature, url, params) {
675
+ const expected = computeTwilioSignature(authToken, url, params);
676
+ if (expected.length !== signature.length) return false;
677
+ let mismatch = 0;
678
+ for (let i = 0; i < expected.length; i++) {
679
+ mismatch |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
680
+ }
681
+ return mismatch === 0;
682
+ }
683
+ function twilioSignatureMiddleware(authToken, baseUrl) {
684
+ return async (c, next) => {
685
+ if (!authToken) {
686
+ return next();
687
+ }
688
+ const signature = c.req.header("x-twilio-signature");
689
+ if (!signature) {
690
+ logger.warn("rejected request: missing X-Twilio-Signature header", {
691
+ path: c.req.path
692
+ });
693
+ return c.text("", 403);
694
+ }
695
+ const requestUrl = baseUrl ? `${baseUrl.replace(/\/$/, "")}${c.req.path}` : c.req.url;
696
+ const body = await c.req.parseBody();
697
+ const params = {};
698
+ for (const [key, value] of Object.entries(body)) {
699
+ if (typeof value === "string") {
700
+ params[key] = value;
701
+ }
702
+ }
703
+ if (!validateTwilioSignature(authToken, signature, requestUrl, params)) {
704
+ logger.warn("rejected request: invalid Twilio signature", {
705
+ path: c.req.path
706
+ });
707
+ return c.text("", 403);
708
+ }
709
+ return next();
710
+ };
711
+ }
712
+
556
713
  // src/core/errors.ts
557
714
  function getErrorMessage(error) {
558
715
  if (error instanceof Error) {
@@ -1488,9 +1645,10 @@ async function processCall(deps, registry, phoneNumber, speechResult) {
1488
1645
  deps.config.voices
1489
1646
  );
1490
1647
  const transferNumber = deps.config.transferNumber || "";
1648
+ const escapedFlowResponse = escapeXml(flowResult.response);
1491
1649
  return `<?xml version="1.0" encoding="UTF-8"?>
1492
1650
  <Response>
1493
- <Say voice="${voice}" language="${lang}">${flowResult.response}</Say>
1651
+ <Say voice="${voice}" language="${lang}">${escapedFlowResponse}</Say>
1494
1652
  <Dial>${transferNumber}</Dial>
1495
1653
  </Response>`;
1496
1654
  }
@@ -1583,6 +1741,12 @@ async function handleStatus(c) {
1583
1741
  // src/routes/call/index.ts
1584
1742
  function callRoutes(deps, registry) {
1585
1743
  const app = new import_hono.Hono();
1744
+ app.use("/call/*", twilioSignatureMiddleware(deps.config.twilio?.authToken));
1745
+ app.use("/call/*", rateLimitMiddleware(deps.config.rateLimit));
1746
+ app.use("/call/*", inputSanitizeMiddleware(deps.config.maxInputLength));
1747
+ app.post("/call", twilioSignatureMiddleware(deps.config.twilio?.authToken));
1748
+ app.post("/call", rateLimitMiddleware(deps.config.rateLimit));
1749
+ app.post("/call", inputSanitizeMiddleware(deps.config.maxInputLength));
1586
1750
  app.post("/call", (c) => handleInitialCall(c, deps.config));
1587
1751
  app.post("/call/respond", (c) => handleRespond(c, deps, registry));
1588
1752
  app.post("/call/answer", (c) => handleAnswer(c, deps.config));
@@ -1653,6 +1817,9 @@ async function handleIncomingSMS(c, deps, registry) {
1653
1817
  // src/routes/sms/index.ts
1654
1818
  function smsRoutes(deps, registry) {
1655
1819
  const app = new import_hono2.Hono();
1820
+ app.post("/sms", twilioSignatureMiddleware(deps.config.twilio?.authToken));
1821
+ app.post("/sms", rateLimitMiddleware(deps.config.rateLimit));
1822
+ app.post("/sms", inputSanitizeMiddleware(deps.config.maxInputLength));
1656
1823
  app.post("/sms", (c) => handleIncomingSMS(c, deps, registry));
1657
1824
  app.get("/sms", (c) => c.text("SMS endpoint active"));
1658
1825
  return app;
@@ -1815,6 +1982,7 @@ async function sendSMS(config, to, message) {
1815
1982
  getVoiceConfig,
1816
1983
  incrementNoSpeechRetries,
1817
1984
  initDbClient,
1985
+ inputSanitizeMiddleware,
1818
1986
  loadFlowsFromDirectory,
1819
1987
  loadPhrases,
1820
1988
  logger,
@@ -1824,6 +1992,8 @@ async function sendSMS(config, to, message) {
1824
1992
  processFlow,
1825
1993
  processIncoming,
1826
1994
  processOutgoing,
1995
+ rateLimitMiddleware,
1996
+ redactPhone,
1827
1997
  resetNoSpeechRetries,
1828
1998
  runMigrations,
1829
1999
  sayTwiml,
@@ -1836,5 +2006,7 @@ async function sendSMS(config, to, message) {
1836
2006
  startCleanup,
1837
2007
  stopCleanup,
1838
2008
  transferTwiml,
1839
- updateFlowParams
2009
+ twilioSignatureMiddleware,
2010
+ updateFlowParams,
2011
+ validateTwilioSignature
1840
2012
  });
package/dist/index.mjs CHANGED
@@ -48,13 +48,31 @@ var isTestEnv = process.env.NODE_ENV === "test" || typeof Bun !== "undefined" &&
48
48
  var isDebug = process.env.DEBUG === "true";
49
49
  var isSilent = isTestEnv && !isDebug;
50
50
  var timestamp = () => (/* @__PURE__ */ new Date()).toISOString();
51
+ function redactPhone(phone) {
52
+ if (!phone || phone === "unknown") return phone;
53
+ const digits = phone.replace(/\D/g, "");
54
+ if (digits.length <= 4) return "***";
55
+ return `***${digits.slice(-4)}`;
56
+ }
57
+ function redactData(data) {
58
+ if (!data) return data;
59
+ const redacted = {};
60
+ for (const [key, value] of Object.entries(data)) {
61
+ if ((key === "phoneNumber" || key === "phone") && typeof value === "string") {
62
+ redacted[key] = redactPhone(value);
63
+ } else {
64
+ redacted[key] = value;
65
+ }
66
+ }
67
+ return redacted;
68
+ }
51
69
  var log = (level, message, data) => {
52
70
  if (isSilent) return;
53
71
  const entry = {
54
72
  timestamp: timestamp(),
55
73
  level,
56
74
  message,
57
- ...data
75
+ ...redactData(data)
58
76
  };
59
77
  console.log(JSON.stringify(entry));
60
78
  };
@@ -485,6 +503,140 @@ function testModeDetectIntent(message) {
485
503
  // src/routes/call/index.ts
486
504
  import { Hono } from "hono";
487
505
 
506
+ // src/middleware/input-sanitize.ts
507
+ var DEFAULT_MAX_INPUT_LENGTH = 1e3;
508
+ function truncateInput(input, maxLength) {
509
+ if (input.length <= maxLength) return input;
510
+ return input.substring(0, maxLength);
511
+ }
512
+ function inputSanitizeMiddleware(maxInputLength) {
513
+ const maxLen = maxInputLength ?? DEFAULT_MAX_INPUT_LENGTH;
514
+ return async (c, next) => {
515
+ const body = await c.req.parseBody();
516
+ let truncated = false;
517
+ if (typeof body.SpeechResult === "string" && body.SpeechResult.length > maxLen) {
518
+ logger.warn("input truncated: SpeechResult", {
519
+ original: body.SpeechResult.length,
520
+ max: maxLen
521
+ });
522
+ body.SpeechResult = truncateInput(body.SpeechResult, maxLen);
523
+ truncated = true;
524
+ }
525
+ if (typeof body.Body === "string" && body.Body.length > maxLen) {
526
+ logger.warn("input truncated: Body", {
527
+ original: body.Body.length,
528
+ max: maxLen
529
+ });
530
+ body.Body = truncateInput(body.Body, maxLen);
531
+ truncated = true;
532
+ }
533
+ if (truncated) {
534
+ c.set("sanitizedBody", body);
535
+ }
536
+ return next();
537
+ };
538
+ }
539
+
540
+ // src/middleware/rate-limit.ts
541
+ var DEFAULT_MAX_REQUESTS = 30;
542
+ var DEFAULT_WINDOW_MS = 6e4;
543
+ var rateLimitStore = /* @__PURE__ */ new Map();
544
+ var cleanupTimer2 = null;
545
+ function ensureCleanup(windowMs) {
546
+ if (cleanupTimer2) return;
547
+ cleanupTimer2 = setInterval(() => {
548
+ const cutoff = Date.now() - windowMs * 2;
549
+ for (const [key, entry] of rateLimitStore) {
550
+ entry.timestamps = entry.timestamps.filter((t) => t > cutoff);
551
+ if (entry.timestamps.length === 0) {
552
+ rateLimitStore.delete(key);
553
+ }
554
+ }
555
+ }, windowMs);
556
+ }
557
+ function checkRateLimit(phoneNumber, maxRequests, windowMs) {
558
+ const now = Date.now();
559
+ const cutoff = now - windowMs;
560
+ let entry = rateLimitStore.get(phoneNumber);
561
+ if (!entry) {
562
+ entry = { timestamps: [] };
563
+ rateLimitStore.set(phoneNumber, entry);
564
+ }
565
+ entry.timestamps = entry.timestamps.filter((t) => t > cutoff);
566
+ if (entry.timestamps.length >= maxRequests) {
567
+ return false;
568
+ }
569
+ entry.timestamps.push(now);
570
+ return true;
571
+ }
572
+ function rateLimitMiddleware(config) {
573
+ const maxRequests = config?.maxRequests ?? DEFAULT_MAX_REQUESTS;
574
+ const windowMs = config?.windowMs ?? DEFAULT_WINDOW_MS;
575
+ ensureCleanup(windowMs);
576
+ return async (c, next) => {
577
+ const body = await c.req.parseBody();
578
+ const phoneNumber = (body.From || "unknown").trim();
579
+ if (!checkRateLimit(phoneNumber, maxRequests, windowMs)) {
580
+ logger.warn("rate limit exceeded", { phoneNumber, path: c.req.path });
581
+ const twiml = `<?xml version="1.0" encoding="UTF-8"?>
582
+ <Response>
583
+ <Say>Please try again in a moment.</Say>
584
+ </Response>`;
585
+ return c.text(twiml, 429, { "Content-Type": "text/xml" });
586
+ }
587
+ return next();
588
+ };
589
+ }
590
+
591
+ // src/middleware/twilio-signature.ts
592
+ import { createHmac } from "node:crypto";
593
+ function computeTwilioSignature(authToken, url, params) {
594
+ const sortedKeys = Object.keys(params).sort();
595
+ let data = url;
596
+ for (const key of sortedKeys) {
597
+ data += key + params[key];
598
+ }
599
+ return createHmac("sha1", authToken).update(data).digest("base64");
600
+ }
601
+ function validateTwilioSignature(authToken, signature, url, params) {
602
+ const expected = computeTwilioSignature(authToken, url, params);
603
+ if (expected.length !== signature.length) return false;
604
+ let mismatch = 0;
605
+ for (let i = 0; i < expected.length; i++) {
606
+ mismatch |= expected.charCodeAt(i) ^ signature.charCodeAt(i);
607
+ }
608
+ return mismatch === 0;
609
+ }
610
+ function twilioSignatureMiddleware(authToken, baseUrl) {
611
+ return async (c, next) => {
612
+ if (!authToken) {
613
+ return next();
614
+ }
615
+ const signature = c.req.header("x-twilio-signature");
616
+ if (!signature) {
617
+ logger.warn("rejected request: missing X-Twilio-Signature header", {
618
+ path: c.req.path
619
+ });
620
+ return c.text("", 403);
621
+ }
622
+ const requestUrl = baseUrl ? `${baseUrl.replace(/\/$/, "")}${c.req.path}` : c.req.url;
623
+ const body = await c.req.parseBody();
624
+ const params = {};
625
+ for (const [key, value] of Object.entries(body)) {
626
+ if (typeof value === "string") {
627
+ params[key] = value;
628
+ }
629
+ }
630
+ if (!validateTwilioSignature(authToken, signature, requestUrl, params)) {
631
+ logger.warn("rejected request: invalid Twilio signature", {
632
+ path: c.req.path
633
+ });
634
+ return c.text("", 403);
635
+ }
636
+ return next();
637
+ };
638
+ }
639
+
488
640
  // src/core/errors.ts
489
641
  function getErrorMessage(error) {
490
642
  if (error instanceof Error) {
@@ -1420,9 +1572,10 @@ async function processCall(deps, registry, phoneNumber, speechResult) {
1420
1572
  deps.config.voices
1421
1573
  );
1422
1574
  const transferNumber = deps.config.transferNumber || "";
1575
+ const escapedFlowResponse = escapeXml(flowResult.response);
1423
1576
  return `<?xml version="1.0" encoding="UTF-8"?>
1424
1577
  <Response>
1425
- <Say voice="${voice}" language="${lang}">${flowResult.response}</Say>
1578
+ <Say voice="${voice}" language="${lang}">${escapedFlowResponse}</Say>
1426
1579
  <Dial>${transferNumber}</Dial>
1427
1580
  </Response>`;
1428
1581
  }
@@ -1515,6 +1668,12 @@ async function handleStatus(c) {
1515
1668
  // src/routes/call/index.ts
1516
1669
  function callRoutes(deps, registry) {
1517
1670
  const app = new Hono();
1671
+ app.use("/call/*", twilioSignatureMiddleware(deps.config.twilio?.authToken));
1672
+ app.use("/call/*", rateLimitMiddleware(deps.config.rateLimit));
1673
+ app.use("/call/*", inputSanitizeMiddleware(deps.config.maxInputLength));
1674
+ app.post("/call", twilioSignatureMiddleware(deps.config.twilio?.authToken));
1675
+ app.post("/call", rateLimitMiddleware(deps.config.rateLimit));
1676
+ app.post("/call", inputSanitizeMiddleware(deps.config.maxInputLength));
1518
1677
  app.post("/call", (c) => handleInitialCall(c, deps.config));
1519
1678
  app.post("/call/respond", (c) => handleRespond(c, deps, registry));
1520
1679
  app.post("/call/answer", (c) => handleAnswer(c, deps.config));
@@ -1585,6 +1744,9 @@ async function handleIncomingSMS(c, deps, registry) {
1585
1744
  // src/routes/sms/index.ts
1586
1745
  function smsRoutes(deps, registry) {
1587
1746
  const app = new Hono2();
1747
+ app.post("/sms", twilioSignatureMiddleware(deps.config.twilio?.authToken));
1748
+ app.post("/sms", rateLimitMiddleware(deps.config.rateLimit));
1749
+ app.post("/sms", inputSanitizeMiddleware(deps.config.maxInputLength));
1588
1750
  app.post("/sms", (c) => handleIncomingSMS(c, deps, registry));
1589
1751
  app.get("/sms", (c) => c.text("SMS endpoint active"));
1590
1752
  return app;
@@ -1746,6 +1908,7 @@ export {
1746
1908
  getVoiceConfig,
1747
1909
  incrementNoSpeechRetries,
1748
1910
  initDbClient,
1911
+ inputSanitizeMiddleware,
1749
1912
  loadFlowsFromDirectory,
1750
1913
  loadPhrases,
1751
1914
  logger,
@@ -1755,6 +1918,8 @@ export {
1755
1918
  processFlow,
1756
1919
  processIncoming,
1757
1920
  processOutgoing,
1921
+ rateLimitMiddleware,
1922
+ redactPhone,
1758
1923
  resetNoSpeechRetries,
1759
1924
  runMigrations,
1760
1925
  sayTwiml,
@@ -1767,5 +1932,7 @@ export {
1767
1932
  startCleanup,
1768
1933
  stopCleanup,
1769
1934
  transferTwiml,
1770
- updateFlowParams
1935
+ twilioSignatureMiddleware,
1936
+ updateFlowParams,
1937
+ validateTwilioSignature
1771
1938
  };
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Input Sanitization Middleware
3
+ *
4
+ * Enforces a maximum length on user-supplied input fields (SpeechResult, Body).
5
+ * Truncates silently rather than rejecting, since Twilio expects a TwiML response.
6
+ */
7
+ import type { Context, Next } from "hono";
8
+ /**
9
+ * Truncate a string to the max length, appending an ellipsis if truncated.
10
+ */
11
+ export declare function truncateInput(input: string, maxLength: number): string;
12
+ /**
13
+ * Hono middleware factory for input sanitization.
14
+ *
15
+ * Intercepts the request body and truncates SpeechResult and Body fields
16
+ * to the configured maximum length. Stores the sanitized values on the
17
+ * context so downstream handlers see the truncated values.
18
+ */
19
+ export declare function inputSanitizeMiddleware(maxInputLength?: number): (c: Context, next: Next) => Promise<void>;
20
+ //# sourceMappingURL=input-sanitize.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-sanitize.d.ts","sourceRoot":"","sources":["../../src/middleware/input-sanitize.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAK1C;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAGtE;AAED;;;;;;GAMG;AACH,wBAAgB,uBAAuB,CAAC,cAAc,CAAC,EAAE,MAAM,IAG/C,GAAG,OAAO,EAAE,MAAM,IAAI,mBA6BrC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=input-sanitize.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"input-sanitize.test.d.ts","sourceRoot":"","sources":["../../src/middleware/input-sanitize.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Rate Limiting Middleware
3
+ *
4
+ * Sliding-window rate limiter keyed by caller phone number.
5
+ * Prevents abuse by limiting requests per phone number within a time window.
6
+ */
7
+ import type { Context, Next } from "hono";
8
+ /**
9
+ * Check if a phone number has exceeded the rate limit.
10
+ * Returns true if the request should be allowed.
11
+ */
12
+ export declare function checkRateLimit(phoneNumber: string, maxRequests: number, windowMs: number): boolean;
13
+ /**
14
+ * Hono middleware factory for rate limiting.
15
+ *
16
+ * Limits requests per phone number (from body.From).
17
+ * Returns a 429 TwiML response when the limit is exceeded.
18
+ */
19
+ export declare function rateLimitMiddleware(config?: {
20
+ maxRequests?: number;
21
+ windowMs?: number;
22
+ }): (c: Context, next: Next) => Promise<void | (Response & import("hono").TypedResponse<"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<Response>\n <Say>Please try again in a moment.</Say>\n</Response>", 429, "text">)>;
23
+ /**
24
+ * Reset rate limit store (for testing)
25
+ */
26
+ export declare function resetRateLimitStore(): void;
27
+ //# sourceMappingURL=rate-limit.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AA4B1C;;;GAGG;AACH,wBAAgB,cAAc,CAC5B,WAAW,EAAE,MAAM,EACnB,WAAW,EAAE,MAAM,EACnB,QAAQ,EAAE,MAAM,GACf,OAAO,CAmBT;AAED;;;;;GAKG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,CAAC,EAAE;IAAE,WAAW,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE,IAMxE,GAAG,OAAO,EAAE,MAAM,IAAI,iMAerC;AAED;;GAEG;AACH,wBAAgB,mBAAmB,IAAI,IAAI,CAM1C"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=rate-limit.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rate-limit.test.d.ts","sourceRoot":"","sources":["../../src/middleware/rate-limit.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Twilio Webhook Signature Validation Middleware
3
+ *
4
+ * Validates that incoming requests are genuinely from Twilio by verifying
5
+ * the X-Twilio-Signature header using HMAC-SHA1 per Twilio's spec.
6
+ *
7
+ * @see https://www.twilio.com/docs/usage/security#validating-requests
8
+ */
9
+ import type { Context, Next } from "hono";
10
+ /**
11
+ * Compute the expected Twilio signature for a request.
12
+ *
13
+ * Algorithm (per Twilio docs):
14
+ * 1. Take the full URL of the request
15
+ * 2. Sort POST body params alphabetically by key
16
+ * 3. Append each key-value pair to the URL (no separators)
17
+ * 4. HMAC-SHA1 the result with the auth token, then Base64 encode
18
+ */
19
+ export declare function computeTwilioSignature(authToken: string, url: string, params: Record<string, string>): string;
20
+ /**
21
+ * Validate a Twilio request signature
22
+ */
23
+ export declare function validateTwilioSignature(authToken: string, signature: string, url: string, params: Record<string, string>): boolean;
24
+ /**
25
+ * Hono middleware factory for Twilio signature validation.
26
+ *
27
+ * When authToken is provided, rejects any request without a valid
28
+ * X-Twilio-Signature header. When authToken is not configured,
29
+ * the middleware is a pass-through (allows development/testing without Twilio).
30
+ */
31
+ export declare function twilioSignatureMiddleware(authToken?: string, baseUrl?: string): (c: Context, next: Next) => Promise<void | (Response & import("hono").TypedResponse<"", 403, "text">)>;
32
+ //# sourceMappingURL=twilio-signature.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twilio-signature.d.ts","sourceRoot":"","sources":["../../src/middleware/twilio-signature.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAG1C;;;;;;;;GAQG;AACH,wBAAgB,sBAAsB,CACpC,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,MAAM,CAOR;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CACrC,SAAS,EAAE,MAAM,EACjB,SAAS,EAAE,MAAM,EACjB,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAC7B,OAAO,CAWT;AAED;;;;;;GAMG;AACH,wBAAgB,yBAAyB,CAAC,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,IAC9D,GAAG,OAAO,EAAE,MAAM,IAAI,gFAkCrC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=twilio-signature.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twilio-signature.test.d.ts","sourceRoot":"","sources":["../../src/middleware/twilio-signature.test.ts"],"names":[],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/call/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAOtD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,8EAU1E"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/call/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAOtD;;GAEG;AACH,wBAAgB,UAAU,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,8EAoB1E"}
@@ -1 +1 @@
1
- {"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../../../src/routes/call/processor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,kBAAkB,EACxB,QAAQ,EAAE,YAAY,EACtB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CA6DjB"}
1
+ {"version":3,"file":"processor.d.ts","sourceRoot":"","sources":["../../../src/routes/call/processor.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAWH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAEtD;;GAEG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,kBAAkB,EACxB,QAAQ,EAAE,YAAY,EACtB,WAAW,EAAE,MAAM,EACnB,YAAY,EAAE,MAAM,GACnB,OAAO,CAAC,MAAM,CAAC,CA8DjB"}
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/sms/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AACzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGtD;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,8EAOzE"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/routes/sms/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAC5B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAIzD,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAGtD;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,kBAAkB,EAAE,QAAQ,EAAE,YAAY,8EAYzE"}
package/dist/types.d.ts CHANGED
@@ -98,6 +98,15 @@ export interface TalkerConfig {
98
98
  cleanupIntervalMs?: number;
99
99
  /** Maximum no-speech retries before ending call. Default: 3 */
100
100
  maxNoSpeechRetries?: number;
101
+ /** Rate limiting configuration */
102
+ rateLimit?: {
103
+ /** Max requests per window per phone number. Default: 30 */
104
+ maxRequests?: number;
105
+ /** Window size in milliseconds. Default: 60000 (1 minute) */
106
+ windowMs?: number;
107
+ };
108
+ /** Maximum input length for speech/SMS messages in characters. Default: 1000 */
109
+ maxInputLength?: number;
101
110
  /** Chat function override. By default, talker queries chatter's RAG pipeline directly */
102
111
  chatFn?: (phoneNumber: string, message: string) => Promise<string>;
103
112
  }
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAIlE;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,KAAK,CAAC;AAErC;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,iDAAiD;IACjD,KAAK,EAAE,MAAM,CAAC;IACd,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,yBAAyB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,wBAAwB;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,2CAA2C;IAC3C,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;GAKG;AACH,MAAM,WAAW,aAAa;IAC5B,wEAAwE;IACxE,GAAG,EAAE,MAAM,CAAC;IACZ,yDAAyD;IACzD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,sEAAsE;IACtE,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,iEAAiE;IACjE,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,6CAA6C;IAC7C,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,6CAA6C;IAC7C,kBAAkB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC,8EAA8E;IAC9E,6BAA6B,CAAC,EAAE,OAAO,CAAC;CACzC;AAED;;GAEG;AACH,MAAM,WAAW,YAAY;IAC3B,0CAA0C;IAC1C,MAAM,CAAC,EAAE,YAAY,CAAC;IAEtB,wEAAwE;IACxE,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,yFAAyF;IACzF,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,CAAC,CAAC;IAErC,qHAAqH;IACrH,QAAQ,CAAC,EAAE,MAAM,CAAC;IAElB,0EAA0E;IAC1E,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,wCAAwC;IACxC,UAAU,CAAC,EAAE,gBAAgB,CAAC;IAE9B,oBAAoB;IACpB,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IAEjC,uGAAuG;IACvG,OAAO,CAAC,EAAE,aAAa,CAAC;IAExB,uGAAuG;IACvG,QAAQ,CAAC,EAAE;QACT,gCAAgC;QAChC,GAAG,EAAE,MAAM,CAAC;QACZ,uBAAuB;QACvB,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IAEF,iGAAiG;IACjG,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,mFAAmF;IACnF,WAAW,CAAC,EAAE,MAAM,CAAC;IAErB,8EAA8E;IAC9E,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAE3B,+DAA+D;IAC/D,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B,kCAAkC;IAClC,SAAS,CAAC,EAAE;QACV,4DAA4D;QAC5D,WAAW,CAAC,EAAE,MAAM,CAAC;QACrB,6DAA6D;QAC7D,QAAQ,CAAC,EAAE,MAAM,CAAC;KACnB,CAAC;IAEF,gFAAgF;IAChF,cAAc,CAAC,EAAE,MAAM,CAAC;IAExB,yFAAyF;IACzF,MAAM,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;CACpE;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,OAAO,EAAE,kBAAkB,CAAC;IAC5B,oCAAoC;IACpC,MAAM,EAAE,YAAY,CAAC;IACrB,0DAA0D;IAC1D,YAAY,EAAE,MAAM,CAAC;IACrB,wDAAwD;IACxD,WAAW,EAAE,MAAM,CAAC;CACrB;AAED;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0DAA0D;IAC1D,cAAc,EAAE,OAAO,CAAC;IACxB,uDAAuD;IACvD,aAAa,EAAE,OAAO,CAAC;IACvB,yCAAyC;IACzC,gBAAgB,EAAE,MAAM,CAAC;IACzB,oCAAoC;IACpC,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;GAEG;AAEH,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,QAAQ,GAAG,QAAQ,GAAG,SAAS,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,QAAQ,CAAC;IACf,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC;IAC/C,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,cAAc;IAC7B,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,MAAM,EAAE,UAAU,CAAC;CACpB;AAED,MAAM,WAAW,SAAS;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IAChC,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,iCAAiC;IACjC,GAAG,EAAE,MAAM,CAAC;IACZ,0CAA0C;IAC1C,GAAG,CAAC,EAAE,MAAM,CAAC;CACd;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;CAClB;AAED,MAAM,MAAM,WAAW,GAAG,CACxB,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAC/B,OAAO,EAAE,kBAAkB,KACxB,OAAO,CAAC,iBAAiB,CAAC,CAAC;AAEhC,MAAM,MAAM,WAAW,GAAG,CACxB,WAAW,EAAE,MAAM,EACnB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAC7B,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAE7B,MAAM,WAAW,oBAAoB;IACnC,eAAe,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACzC,eAAe,EAAE,OAAO,CAAC;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,eAAe;IAC9B,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,UAAU;IACzB,UAAU,EAAE,cAAc,CAAC;IAC3B,OAAO,EAAE,WAAW,CAAC;IACrB,gBAAgB,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,WAAW,CAAC;CACvB;AAED,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,OAAO,CAAC;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,OAAO,CAAC;CACvB;AAED;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,OAAO,CAAC;IACjB,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,cAAc,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,GAAG,WAAW,CAAC;QAAC,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAC1F,UAAU,EAAE,SAAS,GAAG,IAAI,CAAC;IAC7B,eAAe,EAAE,MAAM,CAAC;IACxB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,OAAO;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,QAAQ,EAAE,MAAM,CAAC;IACjB,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,EAAE;QACR,OAAO,EAAE,MAAM,CAAC;QAChB,SAAS,EAAE,MAAM,CAAC;QAClB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;IACF,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,IAAI,EAAE;QACJ,SAAS,EAAE,MAAM,CAAC;KACnB,CAAC;IACF,GAAG,EAAE;QACH,QAAQ,EAAE,MAAM,CAAC;QACjB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,eAAe,EAAE,MAAM,CAAC;QACxB,YAAY,EAAE,MAAM,CAAC;KACtB,CAAC;CACH"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@diegoaltoworks/talker",
3
- "version": "0.8.0",
3
+ "version": "0.10.0",
4
4
  "description": "Telephony plugin for Chatter — adds voice call and SMS support via Twilio",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.mjs",
@@ -85,8 +85,8 @@
85
85
  },
86
86
  "devDependencies": {
87
87
  "@biomejs/biome": "^1.9.4",
88
- "@diegoaltoworks/chatter": "^0.1.8",
89
- "@libsql/client": "^0.14.0",
88
+ "@diegoaltoworks/chatter": "^0.10.0",
89
+ "@libsql/client": "^0.17.0",
90
90
  "@types/bun": "^1.1.12",
91
91
  "@types/node": "^22.9.1",
92
92
  "esbuild": "^0.27.4",