@fabric-harness/channels 0.16.1 → 0.17.1

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 (83) hide show
  1. package/NOTICE +4 -0
  2. package/README.md +6 -1
  3. package/dist/compatibility.d.ts +143 -0
  4. package/dist/compatibility.d.ts.map +1 -0
  5. package/dist/compatibility.js +30 -0
  6. package/dist/compatibility.js.map +1 -0
  7. package/dist/discord.d.ts +53 -0
  8. package/dist/discord.d.ts.map +1 -0
  9. package/dist/discord.js +95 -0
  10. package/dist/discord.js.map +1 -0
  11. package/dist/github.d.ts +37 -1
  12. package/dist/github.d.ts.map +1 -1
  13. package/dist/github.js +5 -5
  14. package/dist/github.js.map +1 -1
  15. package/dist/google-chat.d.ts +59 -0
  16. package/dist/google-chat.d.ts.map +1 -0
  17. package/dist/google-chat.js +127 -0
  18. package/dist/google-chat.js.map +1 -0
  19. package/dist/index.d.ts +4 -2
  20. package/dist/index.d.ts.map +1 -1
  21. package/dist/index.js +2 -1
  22. package/dist/index.js.map +1 -1
  23. package/dist/intercom.d.ts +47 -0
  24. package/dist/intercom.d.ts.map +1 -0
  25. package/dist/intercom.js +40 -0
  26. package/dist/intercom.js.map +1 -0
  27. package/dist/linear.d.ts +46 -0
  28. package/dist/linear.d.ts.map +1 -0
  29. package/dist/linear.js +27 -0
  30. package/dist/linear.js.map +1 -0
  31. package/dist/messenger.d.ts +59 -0
  32. package/dist/messenger.d.ts.map +1 -0
  33. package/dist/messenger.js +49 -0
  34. package/dist/messenger.js.map +1 -0
  35. package/dist/notion.d.ts +46 -0
  36. package/dist/notion.d.ts.map +1 -0
  37. package/dist/notion.js +32 -0
  38. package/dist/notion.js.map +1 -0
  39. package/dist/resend.d.ts +44 -0
  40. package/dist/resend.d.ts.map +1 -0
  41. package/dist/resend.js +49 -0
  42. package/dist/resend.js.map +1 -0
  43. package/dist/salesforce-marketing-cloud.d.ts +39 -0
  44. package/dist/salesforce-marketing-cloud.d.ts.map +1 -0
  45. package/dist/salesforce-marketing-cloud.js +21 -0
  46. package/dist/salesforce-marketing-cloud.js.map +1 -0
  47. package/dist/shopify.d.ts +38 -0
  48. package/dist/shopify.d.ts.map +1 -0
  49. package/dist/shopify.js +29 -0
  50. package/dist/shopify.js.map +1 -0
  51. package/dist/slack.d.ts +19 -1
  52. package/dist/slack.d.ts.map +1 -1
  53. package/dist/slack.js +19 -7
  54. package/dist/slack.js.map +1 -1
  55. package/dist/stripe.d.ts +40 -0
  56. package/dist/stripe.d.ts.map +1 -0
  57. package/dist/stripe.js +58 -0
  58. package/dist/stripe.js.map +1 -0
  59. package/dist/teams.d.ts +58 -0
  60. package/dist/teams.d.ts.map +1 -0
  61. package/dist/teams.js +154 -0
  62. package/dist/teams.js.map +1 -0
  63. package/dist/telegram.d.ts +53 -0
  64. package/dist/telegram.d.ts.map +1 -0
  65. package/dist/telegram.js +76 -0
  66. package/dist/telegram.js.map +1 -0
  67. package/dist/twilio.d.ts +39 -0
  68. package/dist/twilio.d.ts.map +1 -0
  69. package/dist/twilio.js +83 -0
  70. package/dist/twilio.js.map +1 -0
  71. package/dist/webhook-shared.d.ts +43 -0
  72. package/dist/webhook-shared.d.ts.map +1 -0
  73. package/dist/webhook-shared.js +100 -0
  74. package/dist/webhook-shared.js.map +1 -0
  75. package/dist/whatsapp.d.ts +53 -0
  76. package/dist/whatsapp.d.ts.map +1 -0
  77. package/dist/whatsapp.js +98 -0
  78. package/dist/whatsapp.js.map +1 -0
  79. package/dist/zendesk.d.ts +48 -0
  80. package/dist/zendesk.d.ts.map +1 -0
  81. package/dist/zendesk.js +36 -0
  82. package/dist/zendesk.js.map +1 -0
  83. package/package.json +74 -5
package/dist/teams.js ADDED
@@ -0,0 +1,154 @@
1
+ import { conversationKey, defineChannel, defineTool, parseConversationKey, readJsonBody } from '@fabric-harness/sdk/channel';
2
+ export function teamsConversationKey(ref) {
3
+ return conversationKey('teams', 'v1', ref.tenantId, ref.conversationId, ref.serviceUrl, ref.activityId ?? 'root');
4
+ }
5
+ export function parseTeamsConversationKey(id) {
6
+ const parsed = parseConversationKey(id);
7
+ if (parsed.provider !== 'teams' || parsed.version !== 'v1' || parsed.segments.length !== 4)
8
+ throw new Error(`Invalid Teams conversation key: "${id}".`);
9
+ const [tenantId, conversationId, serviceUrl, activityId] = parsed.segments;
10
+ return { tenantId, conversationId, serviceUrl, ...(activityId !== 'root' ? { activityId } : {}) };
11
+ }
12
+ export function createTeamsChannel(options) {
13
+ const authenticate = options.authenticate ?? (options.applicationId
14
+ ? createBotFrameworkAuthenticator({ applicationId: options.applicationId, ...options.botFramework })
15
+ : undefined);
16
+ if (!authenticate)
17
+ throw new Error('createTeamsChannel requires applicationId or authenticate.');
18
+ const verifyActivity = authenticate;
19
+ async function activities(request, ctx) {
20
+ const body = await readJsonBody(request);
21
+ if (!body)
22
+ return new Response(null, { status: 413 });
23
+ const activity = body.json;
24
+ if (!activity)
25
+ return new Response(null, { status: 400 });
26
+ if (!(await verifyActivity(request, activity)))
27
+ return new Response(null, { status: 401 });
28
+ if (activity.type !== 'message' && activity.type !== 'messageUpdate' && activity.type !== 'messageDelete')
29
+ return new Response(null, { status: 200 });
30
+ if (activity.type !== 'messageDelete' && typeof activity.text !== 'string')
31
+ return new Response(null, { status: 200 });
32
+ const tenantId = String(activity.channelData?.tenant?.id ?? activity.channelId ?? 'teams');
33
+ const conversationId = String(activity.conversation?.id ?? '');
34
+ const serviceUrl = String(activity.serviceUrl ?? '');
35
+ if (!conversationId || !serviceUrl)
36
+ return new Response(null, { status: 400 });
37
+ const ref = { tenantId, conversationId, serviceUrl, ...(activity.replyToId ? { activityId: String(activity.replyToId) } : {}) };
38
+ await ctx.dispatch(options.agent, {
39
+ instanceId: teamsConversationKey(ref),
40
+ input: { type: `teams.${activity.type}`, text: activity.text ?? '', activityId: activity.id, channelId: activity.channelId },
41
+ ...(activity.id ? { dedupeKey: String(activity.id) } : {}),
42
+ tenantId,
43
+ ...(activity.from?.id ? { actor: { onBehalfOf: String(activity.from.id) } } : {}),
44
+ });
45
+ return new Response(null, { status: 202 });
46
+ }
47
+ return defineChannel({
48
+ name: 'teams', routes: [{ method: 'POST', path: '/activities', handler: activities }],
49
+ conversationKey: (ref) => teamsConversationKey(ref),
50
+ parseConversationKey: (id) => parseTeamsConversationKey(id),
51
+ });
52
+ }
53
+ /** Verify Microsoft Bot Connector signatures, issuer, audience, lifetime, service URL, and endorsement. */
54
+ export function createBotFrameworkAuthenticator(options) {
55
+ const fetchImpl = options.fetchImpl ?? fetch;
56
+ const metadataUrl = options.openIdMetadataUrl ?? 'https://login.botframework.com/v1/.well-known/openidconfiguration';
57
+ const ttl = options.cacheTtlMs ?? 60 * 60 * 1000;
58
+ const skew = options.clockSkewSeconds ?? 300;
59
+ const now = options.now ?? Date.now;
60
+ let cached;
61
+ async function load(force = false) {
62
+ if (!force && cached && cached.expiresAt > now())
63
+ return cached;
64
+ const metadataResponse = await fetchImpl(metadataUrl);
65
+ if (!metadataResponse.ok)
66
+ throw new Error(`Bot Framework metadata request failed with ${metadataResponse.status}.`);
67
+ const metadata = await metadataResponse.json();
68
+ if (!metadata.issuer || !metadata.jwks_uri)
69
+ throw new Error('Bot Framework metadata is missing issuer or jwks_uri.');
70
+ const keysResponse = await fetchImpl(metadata.jwks_uri);
71
+ if (!keysResponse.ok)
72
+ throw new Error(`Bot Framework key request failed with ${keysResponse.status}.`);
73
+ const keys = await keysResponse.json();
74
+ if (!Array.isArray(keys.keys))
75
+ throw new Error('Bot Framework key response is missing keys.');
76
+ cached = { metadata, keys, expiresAt: now() + ttl };
77
+ return cached;
78
+ }
79
+ return async (request, activity) => {
80
+ try {
81
+ const authorization = request.headers.get('authorization');
82
+ if (!authorization?.startsWith('Bearer '))
83
+ return false;
84
+ const token = authorization.slice(7).trim();
85
+ const parts = token.split('.');
86
+ if (parts.length !== 3)
87
+ return false;
88
+ const [encodedHeader, encodedPayload, encodedSignature] = parts;
89
+ const header = decodeJwtPart(encodedHeader);
90
+ const payload = decodeJwtPart(encodedPayload);
91
+ if (header.alg !== 'RS256' || typeof header.kid !== 'string')
92
+ return false;
93
+ let discovery = await load();
94
+ let jwk = discovery.keys.keys.find((candidate) => candidate.kid === header.kid);
95
+ if (!jwk) {
96
+ discovery = await load(true);
97
+ jwk = discovery.keys.keys.find((candidate) => candidate.kid === header.kid);
98
+ }
99
+ if (!jwk)
100
+ return false;
101
+ if (discovery.metadata.id_token_signing_alg_values_supported && !discovery.metadata.id_token_signing_alg_values_supported.includes('RS256'))
102
+ return false;
103
+ if (payload.iss !== discovery.metadata.issuer)
104
+ return false;
105
+ const audience = payload.aud;
106
+ if (audience !== options.applicationId && !(Array.isArray(audience) && audience.includes(options.applicationId)))
107
+ return false;
108
+ const current = Math.floor(now() / 1000);
109
+ if (typeof payload.exp !== 'number' || current - skew >= payload.exp)
110
+ return false;
111
+ if (typeof payload.nbf === 'number' && current + skew < payload.nbf)
112
+ return false;
113
+ const serviceUrl = activity.serviceUrl;
114
+ if (typeof serviceUrl !== 'string' || payload.serviceurl !== serviceUrl)
115
+ return false;
116
+ const channelId = activity.channelId;
117
+ if (typeof channelId === 'string' && jwk.endorsements && !jwk.endorsements.includes(channelId))
118
+ return false;
119
+ const key = await crypto.subtle.importKey('jwk', jwk, { name: 'RSASSA-PKCS1-v1_5', hash: 'SHA-256' }, false, ['verify']);
120
+ return await crypto.subtle.verify('RSASSA-PKCS1-v1_5', key, viewToBuffer(base64UrlBytes(encodedSignature)), viewToBuffer(new TextEncoder().encode(`${encodedHeader}.${encodedPayload}`)));
121
+ }
122
+ catch {
123
+ return false;
124
+ }
125
+ };
126
+ }
127
+ function decodeJwtPart(value) {
128
+ return JSON.parse(new TextDecoder().decode(base64UrlBytes(value)));
129
+ }
130
+ function base64UrlBytes(value) {
131
+ const normalized = value.replace(/-/g, '+').replace(/_/g, '/').padEnd(Math.ceil(value.length / 4) * 4, '=');
132
+ const decoded = atob(normalized);
133
+ return Uint8Array.from(decoded, (character) => character.charCodeAt(0));
134
+ }
135
+ function viewToBuffer(bytes) {
136
+ return bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength);
137
+ }
138
+ export function replyInTeamsConversation(ref, options) {
139
+ const fetchImpl = options.fetchImpl ?? fetch;
140
+ return defineTool({
141
+ name: 'reply_in_teams_conversation', description: 'Reply in the Microsoft Teams conversation this agent is handling.',
142
+ inputSchema: { type: 'object', additionalProperties: false, properties: { text: { type: 'string', minLength: 1 } }, required: ['text'] },
143
+ metadata: { effect: 'write', provider: 'microsoft-teams', channel: 'teams' },
144
+ execute: async ({ text }) => {
145
+ const token = typeof options.accessToken === 'function' ? await options.accessToken() : options.accessToken;
146
+ const base = `${ref.serviceUrl.replace(/\/$/, '')}/v3/conversations/${encodeURIComponent(ref.conversationId)}/activities`;
147
+ const response = await fetchImpl(ref.activityId ? `${base}/${encodeURIComponent(ref.activityId)}` : base, {
148
+ method: 'POST', headers: { authorization: `Bearer ${token}`, 'content-type': 'application/json' }, body: JSON.stringify({ type: 'message', text }),
149
+ });
150
+ return response.json();
151
+ },
152
+ });
153
+ }
154
+ //# sourceMappingURL=teams.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"teams.js","sourceRoot":"","sources":["../src/teams.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,53 @@
1
+ import type { Channel, ToolDef } from '@fabric-harness/sdk/channel';
2
+ export interface TelegramConversationRef {
3
+ botId: string;
4
+ chatId: string;
5
+ threadId?: string;
6
+ }
7
+ export interface TelegramMessagePayload {
8
+ message_id?: number;
9
+ message_thread_id?: number;
10
+ text?: string;
11
+ chat?: {
12
+ id?: string | number;
13
+ };
14
+ from?: {
15
+ id?: string | number;
16
+ };
17
+ }
18
+ export interface TelegramUpdatePayload {
19
+ update_id?: number;
20
+ message?: TelegramMessagePayload;
21
+ edited_message?: TelegramMessagePayload;
22
+ channel_post?: TelegramMessagePayload;
23
+ edited_channel_post?: TelegramMessagePayload;
24
+ callback_query?: {
25
+ id?: string;
26
+ data?: string;
27
+ from?: {
28
+ id?: string | number;
29
+ };
30
+ message?: TelegramMessagePayload;
31
+ };
32
+ }
33
+ export interface TelegramChannelOptions {
34
+ botId: string;
35
+ botToken: string;
36
+ secretToken: string;
37
+ agent: string;
38
+ tenantId?: string | ((update: TelegramUpdatePayload) => string | undefined);
39
+ }
40
+ export interface TelegramChannel extends Channel {
41
+ conversationKey(ref: TelegramConversationRef): string;
42
+ parseConversationKey(id: string): TelegramConversationRef;
43
+ }
44
+ export declare function telegramConversationKey(ref: TelegramConversationRef): string;
45
+ export declare function parseTelegramConversationKey(id: string): TelegramConversationRef;
46
+ export declare function createTelegramChannel(options: TelegramChannelOptions): TelegramChannel;
47
+ export declare function replyInTelegram(ref: TelegramConversationRef, options: {
48
+ botToken: string;
49
+ fetchImpl?: typeof fetch;
50
+ }): ToolDef<{
51
+ text: string;
52
+ }, unknown>;
53
+ //# sourceMappingURL=telegram.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.d.ts","sourceRoot":"","sources":["../src/telegram.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAkB,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAGpF,MAAM,WAAW,uBAAuB;IAAG,KAAK,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;CAAE;AAC7F,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;IAChC,IAAI,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;KAAE,CAAC;CACjC;AACD,MAAM,WAAW,qBAAqB;IACpC,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,sBAAsB,CAAC;IACjC,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,YAAY,CAAC,EAAE,sBAAsB,CAAC;IACtC,mBAAmB,CAAC,EAAE,sBAAsB,CAAC;IAC7C,cAAc,CAAC,EAAE;QAAE,EAAE,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAC;QAAC,IAAI,CAAC,EAAE;YAAE,EAAE,CAAC,EAAE,MAAM,GAAG,MAAM,CAAA;SAAE,CAAC;QAAC,OAAO,CAAC,EAAE,sBAAsB,CAAA;KAAE,CAAC;CACpH;AACD,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,MAAM,EAAE,qBAAqB,KAAK,MAAM,GAAG,SAAS,CAAC,CAAC;CAC7E;AACD,MAAM,WAAW,eAAgB,SAAQ,OAAO;IAC9C,eAAe,CAAC,GAAG,EAAE,uBAAuB,GAAG,MAAM,CAAC;IACtD,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,uBAAuB,CAAC;CAC3D;AAED,wBAAgB,uBAAuB,CAAC,GAAG,EAAE,uBAAuB,GAAG,MAAM,CAE5E;AAED,wBAAgB,4BAA4B,CAAC,EAAE,EAAE,MAAM,GAAG,uBAAuB,CAKhF;AAED,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CA2CtF;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,uBAAuB,EAAE,OAAO,EAAE;IAAE,QAAQ,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,CAAC,CAezJ"}
@@ -0,0 +1,76 @@
1
+ import { conversationKey, defineChannel, defineTool, parseConversationKey, readJsonBody } from '@fabric-harness/sdk/channel';
2
+ import { timingSafeString } from './webhook-shared.js';
3
+ export function telegramConversationKey(ref) {
4
+ return conversationKey('telegram', 'v1', ref.botId, ref.chatId, ref.threadId ?? 'root');
5
+ }
6
+ export function parseTelegramConversationKey(id) {
7
+ const parsed = parseConversationKey(id);
8
+ if (parsed.provider !== 'telegram' || parsed.version !== 'v1' || parsed.segments.length !== 3)
9
+ throw new Error(`Invalid Telegram conversation key: "${id}".`);
10
+ const [botId, chatId, threadId] = parsed.segments;
11
+ return { botId, chatId, ...(threadId !== 'root' ? { threadId } : {}) };
12
+ }
13
+ export function createTelegramChannel(options) {
14
+ async function webhook(request, ctx) {
15
+ const secretToken = request.headers.get('x-telegram-bot-api-secret-token');
16
+ if (!secretToken || !timingSafeString(secretToken, options.secretToken))
17
+ return new Response(null, { status: 401 });
18
+ const body = await readJsonBody(request);
19
+ if (!body)
20
+ return new Response(null, { status: 413 });
21
+ const update = body.json;
22
+ if (!update)
23
+ return new Response(null, { status: 400 });
24
+ const callback = update.callback_query;
25
+ const message = update.message ?? update.edited_message ?? update.channel_post ?? update.edited_channel_post ?? callback?.message;
26
+ const chat = message?.chat;
27
+ const from = callback?.from ?? message?.from;
28
+ const text = callback?.data ?? message?.text;
29
+ if (message && chat?.id !== undefined && typeof text === 'string') {
30
+ const ref = {
31
+ botId: options.botId,
32
+ chatId: String(chat.id),
33
+ ...(message.message_thread_id !== undefined ? { threadId: String(message.message_thread_id) } : {}),
34
+ };
35
+ const tenantId = typeof options.tenantId === 'function' ? options.tenantId(update) : options.tenantId;
36
+ await ctx.dispatch(options.agent, {
37
+ instanceId: telegramConversationKey(ref),
38
+ input: {
39
+ type: callback ? 'telegram.callback' : update.edited_message || update.edited_channel_post ? 'telegram.message_edited' : 'telegram.message',
40
+ text,
41
+ messageId: message.message_id,
42
+ callbackId: callback?.id,
43
+ chatId: chat.id,
44
+ userId: from?.id,
45
+ },
46
+ ...(update.update_id !== undefined ? { dedupeKey: String(update.update_id) } : callback?.id ? { dedupeKey: callback.id } : {}),
47
+ ...(tenantId ? { tenantId } : {}),
48
+ ...(from?.id !== undefined ? { actor: { onBehalfOf: String(from.id) } } : {}),
49
+ });
50
+ }
51
+ return new Response(null, { status: 200 });
52
+ }
53
+ return defineChannel({
54
+ name: 'telegram',
55
+ routes: [{ method: 'POST', path: '/webhook', handler: webhook }],
56
+ conversationKey: (ref) => telegramConversationKey(ref),
57
+ parseConversationKey: (id) => parseTelegramConversationKey(id),
58
+ });
59
+ }
60
+ export function replyInTelegram(ref, options) {
61
+ const fetchImpl = options.fetchImpl ?? fetch;
62
+ return defineTool({
63
+ name: 'reply_in_telegram',
64
+ description: 'Reply in the Telegram conversation this agent is handling.',
65
+ inputSchema: { type: 'object', additionalProperties: false, properties: { text: { type: 'string', minLength: 1 } }, required: ['text'] },
66
+ metadata: { effect: 'write', provider: 'telegram', channel: 'telegram' },
67
+ execute: async ({ text }) => {
68
+ const response = await fetchImpl(`https://api.telegram.org/bot${options.botToken}/sendMessage`, {
69
+ method: 'POST', headers: { 'content-type': 'application/json' },
70
+ body: JSON.stringify({ chat_id: ref.chatId, text, ...(ref.threadId ? { message_thread_id: ref.threadId } : {}) }),
71
+ });
72
+ return response.json();
73
+ },
74
+ });
75
+ }
76
+ //# sourceMappingURL=telegram.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"telegram.js","sourceRoot":"","sources":["../src/telegram.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,39 @@
1
+ import type { Channel, ToolDef } from '@fabric-harness/sdk/channel';
2
+ export interface TwilioConversationRef {
3
+ accountSid: string;
4
+ from: string;
5
+ to: string;
6
+ }
7
+ export interface TwilioMessagePayload {
8
+ AccountSid?: string;
9
+ MessageSid?: string;
10
+ From?: string;
11
+ To?: string;
12
+ Body?: string;
13
+ NumMedia?: string;
14
+ MessageStatus?: string;
15
+ ErrorCode?: string;
16
+ }
17
+ export interface TwilioChannelOptions {
18
+ accountSid: string;
19
+ authToken: string;
20
+ agent: string;
21
+ /** Public webhook URL configured in Twilio. Required when a proxy rewrites the request URL. */
22
+ webhookUrl?: string;
23
+ tenantId?: string;
24
+ }
25
+ export interface TwilioChannel extends Channel {
26
+ conversationKey(ref: TwilioConversationRef): string;
27
+ parseConversationKey(id: string): TwilioConversationRef;
28
+ }
29
+ export declare function twilioConversationKey(ref: TwilioConversationRef): string;
30
+ export declare function parseTwilioConversationKey(id: string): TwilioConversationRef;
31
+ export declare function createTwilioChannel(options: TwilioChannelOptions): TwilioChannel;
32
+ export declare function verifyTwilioSignature(authToken: string, url: string, params: URLSearchParams, signature: string): Promise<boolean>;
33
+ export declare function replyWithTwilio(ref: TwilioConversationRef, options: {
34
+ authToken: string;
35
+ fetchImpl?: typeof fetch;
36
+ }): ToolDef<{
37
+ text: string;
38
+ }, unknown>;
39
+ //# sourceMappingURL=twilio.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twilio.d.ts","sourceRoot":"","sources":["../src/twilio.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,OAAO,EAAkB,OAAO,EAAE,MAAM,6BAA6B,CAAC;AAGpF,MAAM,WAAW,qBAAqB;IAAG,UAAU,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,EAAE,EAAE,MAAM,CAAA;CAAE;AACvF,MAAM,WAAW,oBAAoB;IACnC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AACD,MAAM,WAAW,oBAAoB;IACnC,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,KAAK,EAAE,MAAM,CAAC;IACd,+FAA+F;IAC/F,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AACD,MAAM,WAAW,aAAc,SAAQ,OAAO;IAC5C,eAAe,CAAC,GAAG,EAAE,qBAAqB,GAAG,MAAM,CAAC;IACpD,oBAAoB,CAAC,EAAE,EAAE,MAAM,GAAG,qBAAqB,CAAC;CACzD;AAED,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,qBAAqB,GAAG,MAAM,CAExE;AACD,wBAAgB,0BAA0B,CAAC,EAAE,EAAE,MAAM,GAAG,qBAAqB,CAK5E;AAED,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,oBAAoB,GAAG,aAAa,CAgChF;AAED,wBAAsB,qBAAqB,CAAC,SAAS,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,EAAE,MAAM,EAAE,eAAe,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CASxI;AAWD,wBAAgB,eAAe,CAAC,GAAG,EAAE,qBAAqB,EAAE,OAAO,EAAE;IAAE,SAAS,EAAE,MAAM,CAAC;IAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;CAAE,GAAG,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,OAAO,CAAC,CAcxJ"}
package/dist/twilio.js ADDED
@@ -0,0 +1,83 @@
1
+ import { conversationKey, defineChannel, defineTool, parseConversationKey, readRequestBody } from '@fabric-harness/sdk/channel';
2
+ import { timingSafeString } from './webhook-shared.js';
3
+ export function twilioConversationKey(ref) {
4
+ return conversationKey('twilio', 'v1', ref.accountSid, ref.from, ref.to);
5
+ }
6
+ export function parseTwilioConversationKey(id) {
7
+ const parsed = parseConversationKey(id);
8
+ if (parsed.provider !== 'twilio' || parsed.version !== 'v1' || parsed.segments.length !== 3)
9
+ throw new Error(`Invalid Twilio conversation key: "${id}".`);
10
+ const [accountSid, from, to] = parsed.segments;
11
+ return { accountSid, from, to };
12
+ }
13
+ export function createTwilioChannel(options) {
14
+ async function messages(request, ctx) {
15
+ const body = await readRequestBody(request);
16
+ if (!body)
17
+ return new Response(null, { status: 413 });
18
+ const params = new URLSearchParams(new TextDecoder().decode(body));
19
+ const signature = request.headers.get('x-twilio-signature') ?? '';
20
+ const valid = await verifyTwilioSignature(options.authToken, options.webhookUrl ?? request.url, params, signature);
21
+ if (!valid)
22
+ return new Response(null, { status: 401 });
23
+ const from = params.get('From');
24
+ const to = params.get('To');
25
+ const text = params.get('Body');
26
+ const status = params.get('MessageStatus');
27
+ const messageSid = params.get('MessageSid');
28
+ if (from && to && (text !== null || status)) {
29
+ const ref = { accountSid: params.get('AccountSid') ?? options.accountSid, from, to };
30
+ await ctx.dispatch(options.agent, {
31
+ instanceId: twilioConversationKey(ref),
32
+ input: status
33
+ ? { type: 'twilio.status', status, messageId: params.get('MessageSid'), errorCode: params.get('ErrorCode'), from, to }
34
+ : { type: from.startsWith('whatsapp:') ? 'twilio.whatsapp' : 'twilio.sms', text, from, to, mediaCount: params.get('NumMedia') },
35
+ ...(messageSid ? { dedupeKey: status ? `${messageSid}:status:${status}` : messageSid } : {}),
36
+ ...(options.tenantId ? { tenantId: options.tenantId } : {}),
37
+ actor: { onBehalfOf: from },
38
+ });
39
+ }
40
+ return new Response('<?xml version="1.0" encoding="UTF-8"?><Response></Response>', { status: 200, headers: { 'content-type': 'text/xml' } });
41
+ }
42
+ return defineChannel({
43
+ name: 'twilio', routes: [{ method: 'POST', path: '/messages', handler: messages }],
44
+ conversationKey: (ref) => twilioConversationKey(ref),
45
+ parseConversationKey: (id) => parseTwilioConversationKey(id),
46
+ });
47
+ }
48
+ export async function verifyTwilioSignature(authToken, url, params, signature) {
49
+ let value = url;
50
+ for (const key of [...new Set(params.keys())].sort()) {
51
+ for (const item of params.getAll(key).sort())
52
+ value += `${key}${item}`;
53
+ }
54
+ const cryptoKey = await crypto.subtle.importKey('raw', new TextEncoder().encode(authToken), { name: 'HMAC', hash: 'SHA-1' }, false, ['sign']);
55
+ const digest = new Uint8Array(await crypto.subtle.sign('HMAC', cryptoKey, new TextEncoder().encode(value)));
56
+ const expected = bytesToBase64(digest);
57
+ return timingSafeString(expected, signature);
58
+ }
59
+ function bytesToBase64(bytes) {
60
+ let binary = '';
61
+ for (const byte of bytes)
62
+ binary += String.fromCharCode(byte);
63
+ return btoa(binary);
64
+ }
65
+ function stringToBase64(value) {
66
+ return bytesToBase64(new TextEncoder().encode(value));
67
+ }
68
+ export function replyWithTwilio(ref, options) {
69
+ const fetchImpl = options.fetchImpl ?? fetch;
70
+ return defineTool({
71
+ name: 'reply_with_twilio', description: 'Reply by SMS or WhatsApp through Twilio.',
72
+ inputSchema: { type: 'object', additionalProperties: false, properties: { text: { type: 'string', minLength: 1 } }, required: ['text'] },
73
+ metadata: { effect: 'write', provider: 'twilio', channel: 'twilio' },
74
+ execute: async ({ text }) => {
75
+ const body = new URLSearchParams({ From: ref.to, To: ref.from, Body: text });
76
+ const response = await fetchImpl(`https://api.twilio.com/2010-04-01/Accounts/${encodeURIComponent(ref.accountSid)}/Messages.json`, {
77
+ method: 'POST', headers: { authorization: `Basic ${stringToBase64(`${ref.accountSid}:${options.authToken}`)}`, 'content-type': 'application/x-www-form-urlencoded' }, body,
78
+ });
79
+ return response.json();
80
+ },
81
+ });
82
+ }
83
+ //# sourceMappingURL=twilio.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"twilio.js","sourceRoot":"","sources":["../src/twilio.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,aAAa,EAAE,UAAU,EAAE,oBAAoB,EAAE,eAAe,EAAE,MAAM,6BAA6B,CAAC;AAEhI,OAAO,EAAE,gBAAgB,EAAE,MAAM,qBAAqB,CAAC;AA0BvD,MAAM,UAAU,qBAAqB,CAAC,GAA0B;IAC9D,OAAO,eAAe,CAAC,QAAQ,EAAE,IAAI,EAAE,GAAG,CAAC,UAAU,EAAE,GAAG,CAAC,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,CAAC;AAC3E,CAAC;AACD,MAAM,UAAU,0BAA0B,CAAC,EAAU;IACnD,MAAM,MAAM,GAAG,oBAAoB,CAAC,EAAE,CAAC,CAAC;IACxC,IAAI,MAAM,CAAC,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,OAAO,KAAK,IAAI,IAAI,MAAM,CAAC,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,MAAM,IAAI,KAAK,CAAC,qCAAqC,EAAE,IAAI,CAAC,CAAC;IAC1J,MAAM,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,CAAC,GAAG,MAAM,CAAC,QAAoC,CAAC;IAC3E,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;AAClC,CAAC;AAED,MAAM,UAAU,mBAAmB,CAAC,OAA6B;IAC/D,KAAK,UAAU,QAAQ,CAAC,OAAgB,EAAE,GAAmB;QAC3D,MAAM,IAAI,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,IAAI;YAAE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACtD,MAAM,MAAM,GAAG,IAAI,eAAe,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;QACnE,MAAM,SAAS,GAAG,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC;QAClE,MAAM,KAAK,GAAG,MAAM,qBAAqB,CAAC,OAAO,CAAC,SAAS,EAAE,OAAO,CAAC,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,CAAC,CAAC;QACnH,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,QAAQ,CAAC,IAAI,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;QACvD,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,EAAE,GAAG,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;QAC5B,MAAM,IAAI,GAAG,MAAM,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,CAAC,GAAG,CAAC,eAAe,CAAC,CAAC;QAC3C,MAAM,UAAU,GAAG,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,CAAC;QAC5C,IAAI,IAAI,IAAI,EAAE,IAAI,CAAC,IAAI,KAAK,IAAI,IAAI,MAAM,CAAC,EAAE,CAAC;YAC5C,MAAM,GAAG,GAAG,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,IAAI,OAAO,CAAC,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YACrF,MAAM,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE;gBAChC,UAAU,EAAE,qBAAqB,CAAC,GAAG,CAAC;gBACtC,KAAK,EAAE,MAAM;oBACX,CAAC,CAAC,EAAE,IAAI,EAAE,eAAe,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,YAAY,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,GAAG,CAAC,WAAW,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE;oBACtH,CAAC,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,UAAU,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,YAAY,EAAE,IAAI,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,MAAM,CAAC,GAAG,CAAC,UAAU,CAAC,EAAE;gBACjI,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC,GAAG,UAAU,WAAW,MAAM,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC5F,GAAG,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,OAAO,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;gBAC3D,KAAK,EAAE,EAAE,UAAU,EAAE,IAAI,EAAE;aAC5B,CAAC,CAAC;QACL,CAAC;QACD,OAAO,IAAI,QAAQ,CAAC,6DAA6D,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,OAAO,EAAE,EAAE,cAAc,EAAE,UAAU,EAAE,EAAE,CAAC,CAAC;IAC/I,CAAC;IACD,OAAO,aAAa,CAAC;QACnB,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,QAAQ,EAAE,CAAC;QAClF,eAAe,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,qBAAqB,CAAC,GAA4B,CAAC;QAC7E,oBAAoB,EAAE,CAAC,EAAE,EAAE,EAAE,CAAC,0BAA0B,CAAC,EAAE,CAAC;KAC7D,CAAkB,CAAC;AACtB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,qBAAqB,CAAC,SAAiB,EAAE,GAAW,EAAE,MAAuB,EAAE,SAAiB;IACpH,IAAI,KAAK,GAAG,GAAG,CAAC;IAChB,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG,IAAI,GAAG,CAAC,MAAM,CAAC,IAAI,EAAE,CAAC,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC;QACrD,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE;YAAE,KAAK,IAAI,GAAG,GAAG,GAAG,IAAI,EAAE,CAAC;IACzE,CAAC;IACD,MAAM,SAAS,GAAG,MAAM,MAAM,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,SAAS,CAAC,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,CAAC;IAC9I,MAAM,MAAM,GAAG,IAAI,UAAU,CAAC,MAAM,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,SAAS,EAAE,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IAC5G,MAAM,QAAQ,GAAG,aAAa,CAAC,MAAM,CAAC,CAAC;IACvC,OAAO,gBAAgB,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,aAAa,CAAC,KAAiB;IACtC,IAAI,MAAM,GAAG,EAAE,CAAC;IAChB,KAAK,MAAM,IAAI,IAAI,KAAK;QAAE,MAAM,IAAI,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,CAAC;IAC9D,OAAO,IAAI,CAAC,MAAM,CAAC,CAAC;AACtB,CAAC;AACD,SAAS,cAAc,CAAC,KAAa;IACnC,OAAO,aAAa,CAAC,IAAI,WAAW,EAAE,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;AACxD,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAA0B,EAAE,OAAwD;IAClH,MAAM,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,KAAK,CAAC;IAC7C,OAAO,UAAU,CAAC;QAChB,IAAI,EAAE,mBAAmB,EAAE,WAAW,EAAE,0CAA0C;QAClF,WAAW,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,oBAAoB,EAAE,KAAK,EAAE,UAAU,EAAE,EAAE,IAAI,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,QAAQ,EAAE,CAAC,MAAM,CAAC,EAAE;QACxI,QAAQ,EAAE,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE;QACpE,OAAO,EAAE,KAAK,EAAE,EAAE,IAAI,EAAoB,EAAE,EAAE;YAC5C,MAAM,IAAI,GAAG,IAAI,eAAe,CAAC,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,EAAE,EAAE,GAAG,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,CAAC,CAAC;YAC7E,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,8CAA8C,kBAAkB,CAAC,GAAG,CAAC,UAAU,CAAC,gBAAgB,EAAE;gBACjI,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,aAAa,EAAE,SAAS,cAAc,CAAC,GAAG,GAAG,CAAC,UAAU,IAAI,OAAO,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE,cAAc,EAAE,mCAAmC,EAAE,EAAE,IAAI;aAC3K,CAAC,CAAC;YACH,OAAO,QAAQ,CAAC,IAAI,EAAE,CAAC;QACzB,CAAC;KACF,CAAC,CAAC;AACL,CAAC"}
@@ -0,0 +1,43 @@
1
+ import type { Channel, ToolDef } from '@fabric-harness/sdk/channel';
2
+ export interface SimpleConversationRef {
3
+ tenantId: string;
4
+ conversationId: string;
5
+ }
6
+ export interface NormalizedWebhookEvent {
7
+ ref: SimpleConversationRef;
8
+ dedupeKey: string;
9
+ actorId: string;
10
+ input: Record<string, unknown>;
11
+ }
12
+ export interface JsonWebhookChannelOptions<TPayload> {
13
+ name: string;
14
+ agent: string;
15
+ authenticate(request: Request, raw: Uint8Array, payload: TPayload): boolean | Promise<boolean>;
16
+ normalize(payload: TPayload, request: Request): NormalizedWebhookEvent | readonly NormalizedWebhookEvent[] | undefined;
17
+ handshake?(payload: TPayload, request: Request): Response | undefined | Promise<Response | undefined>;
18
+ successStatus?: number;
19
+ }
20
+ export interface SimpleWebhookChannel extends Channel {
21
+ conversationKey(ref: SimpleConversationRef): string;
22
+ parseConversationKey(id: string): SimpleConversationRef;
23
+ }
24
+ export declare function simpleConversationKey(provider: string, ref: SimpleConversationRef): string;
25
+ export declare function parseSimpleConversationKey(provider: string, id: string): SimpleConversationRef;
26
+ export declare function createJsonWebhookChannel<TPayload>(options: JsonWebhookChannelOptions<TPayload>): SimpleWebhookChannel;
27
+ export declare function verifyHexHmac(secret: string, raw: Uint8Array, signature: string | null, prefix?: string): Promise<boolean>;
28
+ export declare function verifyBase64Hmac(secret: string, raw: Uint8Array, signature: string | null): Promise<boolean>;
29
+ /** Constant-time string comparison for shared-secret webhook tokens and signatures. */
30
+ export declare function timingSafeString(a: string, b: string): boolean;
31
+ export declare function bearerAuthenticator(token: string): (request: Request) => boolean;
32
+ export declare function jsonWriteTool<TInput extends object>(options: {
33
+ name: string;
34
+ description: string;
35
+ provider: string;
36
+ inputSchema: Record<string, unknown>;
37
+ url: string | ((input: TInput) => string);
38
+ headers?: Record<string, string> | ((input: TInput) => Record<string, string>);
39
+ body?: (input: TInput) => unknown;
40
+ method?: string;
41
+ fetchImpl?: typeof fetch | undefined;
42
+ }): ToolDef<TInput, unknown>;
43
+ //# sourceMappingURL=webhook-shared.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-shared.d.ts","sourceRoot":"","sources":["../src/webhook-shared.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,100 @@
1
+ import { conversationKey, defineChannel, defineTool, hexToBytes, parseConversationKey, readJsonBody, verifyHmacSha256, } from '@fabric-harness/sdk/channel';
2
+ export function simpleConversationKey(provider, ref) {
3
+ return conversationKey(provider, 'v1', ref.tenantId, ref.conversationId);
4
+ }
5
+ export function parseSimpleConversationKey(provider, id) {
6
+ const parsed = parseConversationKey(id);
7
+ if (parsed.provider !== provider || parsed.version !== 'v1' || parsed.segments.length !== 2) {
8
+ throw new Error(`Invalid ${provider} conversation key: "${id}".`);
9
+ }
10
+ const [tenantId, conversationId] = parsed.segments;
11
+ return { tenantId, conversationId };
12
+ }
13
+ export function createJsonWebhookChannel(options) {
14
+ async function webhook(request, context) {
15
+ const body = await readJsonBody(request);
16
+ if (!body)
17
+ return new Response(null, { status: 413 });
18
+ const payload = body.json;
19
+ if (!payload || typeof payload !== 'object')
20
+ return new Response(null, { status: 400 });
21
+ if (!(await options.authenticate(request, body.raw, payload)))
22
+ return new Response(null, { status: 401 });
23
+ const handshake = await options.handshake?.(payload, request);
24
+ if (handshake)
25
+ return handshake;
26
+ const normalized = options.normalize(payload, request);
27
+ for (const event of normalized ? Array.isArray(normalized) ? normalized : [normalized] : []) {
28
+ await context.dispatch(options.agent, {
29
+ instanceId: simpleConversationKey(options.name, event.ref),
30
+ dedupeKey: event.dedupeKey,
31
+ tenantId: event.ref.tenantId,
32
+ actor: { onBehalfOf: event.actorId },
33
+ input: event.input,
34
+ });
35
+ }
36
+ return new Response(null, { status: options.successStatus ?? 200 });
37
+ }
38
+ return defineChannel({
39
+ name: options.name,
40
+ routes: [{ method: 'POST', path: '/webhook', handler: webhook }],
41
+ conversationKey: (ref) => simpleConversationKey(options.name, ref),
42
+ parseConversationKey: (id) => parseSimpleConversationKey(options.name, id),
43
+ });
44
+ }
45
+ export async function verifyHexHmac(secret, raw, signature, prefix = '') {
46
+ if (!signature || !signature.startsWith(prefix))
47
+ return false;
48
+ try {
49
+ return await verifyHmacSha256(secret, raw, hexToBytes(signature.slice(prefix.length)));
50
+ }
51
+ catch {
52
+ return false;
53
+ }
54
+ }
55
+ export async function verifyBase64Hmac(secret, raw, signature) {
56
+ if (!signature)
57
+ return false;
58
+ try {
59
+ const key = await crypto.subtle.importKey('raw', new TextEncoder().encode(secret), { name: 'HMAC', hash: 'SHA-256' }, false, ['verify']);
60
+ const signatureBytes = Uint8Array.from(atob(signature), (value) => value.charCodeAt(0));
61
+ return await crypto.subtle.verify('HMAC', key, new Uint8Array(signatureBytes).buffer, new Uint8Array(raw).buffer);
62
+ }
63
+ catch {
64
+ return false;
65
+ }
66
+ }
67
+ /** Constant-time string comparison for shared-secret webhook tokens and signatures. */
68
+ export function timingSafeString(a, b) {
69
+ if (a.length !== b.length)
70
+ return false;
71
+ let diff = 0;
72
+ for (let i = 0; i < a.length; i += 1)
73
+ diff |= a.charCodeAt(i) ^ b.charCodeAt(i);
74
+ return diff === 0;
75
+ }
76
+ export function bearerAuthenticator(token) {
77
+ return (request) => Boolean(token) && request.headers.get('authorization') === `Bearer ${token}`;
78
+ }
79
+ export function jsonWriteTool(options) {
80
+ const fetchImpl = options.fetchImpl ?? fetch;
81
+ return defineTool({
82
+ name: options.name,
83
+ description: options.description,
84
+ inputSchema: { type: 'object', additionalProperties: false, ...options.inputSchema },
85
+ metadata: { effect: 'write', provider: options.provider, channel: options.provider },
86
+ execute: async (input) => {
87
+ const response = await fetchImpl(typeof options.url === 'function' ? options.url(input) : options.url, {
88
+ method: options.method ?? 'POST',
89
+ headers: { 'content-type': 'application/json', ...(typeof options.headers === 'function' ? options.headers(input) : options.headers) },
90
+ ...(options.body ? { body: JSON.stringify(options.body(input)) } : {}),
91
+ });
92
+ if (!response.ok)
93
+ throw new Error(`${options.provider} outbound request failed with status ${response.status}.`);
94
+ if (response.status === 204)
95
+ return { ok: true };
96
+ return response.json();
97
+ },
98
+ });
99
+ }
100
+ //# sourceMappingURL=webhook-shared.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webhook-shared.js","sourceRoot":"","sources":["../src/webhook-shared.ts"],"names":[],"mappings":"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"}