@cogenta/channels 0.1.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 (177) hide show
  1. package/dist/adapter.d.ts +111 -0
  2. package/dist/adapter.d.ts.map +1 -0
  3. package/dist/adapter.js +2 -0
  4. package/dist/adapter.js.map +1 -0
  5. package/dist/approvals/commands.d.ts +28 -0
  6. package/dist/approvals/commands.d.ts.map +1 -0
  7. package/dist/approvals/commands.js +66 -0
  8. package/dist/approvals/commands.js.map +1 -0
  9. package/dist/approvals/dispatch.d.ts +22 -0
  10. package/dist/approvals/dispatch.d.ts.map +1 -0
  11. package/dist/approvals/dispatch.js +22 -0
  12. package/dist/approvals/dispatch.js.map +1 -0
  13. package/dist/approvals/message.d.ts +18 -0
  14. package/dist/approvals/message.d.ts.map +1 -0
  15. package/dist/approvals/message.js +40 -0
  16. package/dist/approvals/message.js.map +1 -0
  17. package/dist/approvals/signed-link.d.ts +19 -0
  18. package/dist/approvals/signed-link.d.ts.map +1 -0
  19. package/dist/approvals/signed-link.js +42 -0
  20. package/dist/approvals/signed-link.js.map +1 -0
  21. package/dist/approvals/store.d.ts +41 -0
  22. package/dist/approvals/store.d.ts.map +1 -0
  23. package/dist/approvals/store.js +41 -0
  24. package/dist/approvals/store.js.map +1 -0
  25. package/dist/approvals/token.d.ts +7 -0
  26. package/dist/approvals/token.d.ts.map +1 -0
  27. package/dist/approvals/token.js +40 -0
  28. package/dist/approvals/token.js.map +1 -0
  29. package/dist/formats/alert.d.ts +18 -0
  30. package/dist/formats/alert.d.ts.map +1 -0
  31. package/dist/formats/alert.js +49 -0
  32. package/dist/formats/alert.js.map +1 -0
  33. package/dist/formats/budget.d.ts +14 -0
  34. package/dist/formats/budget.d.ts.map +1 -0
  35. package/dist/formats/budget.js +13 -0
  36. package/dist/formats/budget.js.map +1 -0
  37. package/dist/formats/notification.d.ts +8 -0
  38. package/dist/formats/notification.d.ts.map +1 -0
  39. package/dist/formats/notification.js +26 -0
  40. package/dist/formats/notification.js.map +1 -0
  41. package/dist/formats/report.d.ts +18 -0
  42. package/dist/formats/report.d.ts.map +1 -0
  43. package/dist/formats/report.js +44 -0
  44. package/dist/formats/report.js.map +1 -0
  45. package/dist/inbound/authorize.d.ts +57 -0
  46. package/dist/inbound/authorize.d.ts.map +1 -0
  47. package/dist/inbound/authorize.js +32 -0
  48. package/dist/inbound/authorize.js.map +1 -0
  49. package/dist/inbound/router.d.ts +67 -0
  50. package/dist/inbound/router.d.ts.map +1 -0
  51. package/dist/inbound/router.js +69 -0
  52. package/dist/inbound/router.js.map +1 -0
  53. package/dist/index.d.ts +77 -0
  54. package/dist/index.d.ts.map +1 -0
  55. package/dist/index.js +42 -0
  56. package/dist/index.js.map +1 -0
  57. package/dist/linking/codes.d.ts +6 -0
  58. package/dist/linking/codes.d.ts.map +1 -0
  59. package/dist/linking/codes.js +37 -0
  60. package/dist/linking/codes.js.map +1 -0
  61. package/dist/linking/store.d.ts +39 -0
  62. package/dist/linking/store.d.ts.map +1 -0
  63. package/dist/linking/store.js +77 -0
  64. package/dist/linking/store.js.map +1 -0
  65. package/dist/linking/tables.d.ts +14 -0
  66. package/dist/linking/tables.d.ts.map +1 -0
  67. package/dist/linking/tables.js +52 -0
  68. package/dist/linking/tables.js.map +1 -0
  69. package/dist/preferences/dispatcher.d.ts +37 -0
  70. package/dist/preferences/dispatcher.d.ts.map +1 -0
  71. package/dist/preferences/dispatcher.js +103 -0
  72. package/dist/preferences/dispatcher.js.map +1 -0
  73. package/dist/preferences/quiet-hours.d.ts +12 -0
  74. package/dist/preferences/quiet-hours.d.ts.map +1 -0
  75. package/dist/preferences/quiet-hours.js +22 -0
  76. package/dist/preferences/quiet-hours.js.map +1 -0
  77. package/dist/preferences/store.d.ts +8 -0
  78. package/dist/preferences/store.d.ts.map +1 -0
  79. package/dist/preferences/store.js +60 -0
  80. package/dist/preferences/store.js.map +1 -0
  81. package/dist/preferences/tables.d.ts +12 -0
  82. package/dist/preferences/tables.d.ts.map +1 -0
  83. package/dist/preferences/tables.js +55 -0
  84. package/dist/preferences/tables.js.map +1 -0
  85. package/dist/preferences/types.d.ts +37 -0
  86. package/dist/preferences/types.d.ts.map +1 -0
  87. package/dist/preferences/types.js +31 -0
  88. package/dist/preferences/types.js.map +1 -0
  89. package/dist/providers/discord/adapter.d.ts +38 -0
  90. package/dist/providers/discord/adapter.d.ts.map +1 -0
  91. package/dist/providers/discord/adapter.js +97 -0
  92. package/dist/providers/discord/adapter.js.map +1 -0
  93. package/dist/providers/discord/client.d.ts +67 -0
  94. package/dist/providers/discord/client.d.ts.map +1 -0
  95. package/dist/providers/discord/client.js +65 -0
  96. package/dist/providers/discord/client.js.map +1 -0
  97. package/dist/providers/discord/gateway.d.ts +55 -0
  98. package/dist/providers/discord/gateway.d.ts.map +1 -0
  99. package/dist/providers/discord/gateway.js +114 -0
  100. package/dist/providers/discord/gateway.js.map +1 -0
  101. package/dist/providers/discord/inbound.d.ts +22 -0
  102. package/dist/providers/discord/inbound.d.ts.map +1 -0
  103. package/dist/providers/discord/inbound.js +75 -0
  104. package/dist/providers/discord/inbound.js.map +1 -0
  105. package/dist/providers/discord/render.d.ts +9 -0
  106. package/dist/providers/discord/render.d.ts.map +1 -0
  107. package/dist/providers/discord/render.js +73 -0
  108. package/dist/providers/discord/render.js.map +1 -0
  109. package/dist/providers/email/adapter.d.ts +18 -0
  110. package/dist/providers/email/adapter.d.ts.map +1 -0
  111. package/dist/providers/email/adapter.js +41 -0
  112. package/dist/providers/email/adapter.js.map +1 -0
  113. package/dist/providers/email/file-transport.d.ts +14 -0
  114. package/dist/providers/email/file-transport.d.ts.map +1 -0
  115. package/dist/providers/email/file-transport.js +33 -0
  116. package/dist/providers/email/file-transport.js.map +1 -0
  117. package/dist/providers/email/render.d.ts +20 -0
  118. package/dist/providers/email/render.d.ts.map +1 -0
  119. package/dist/providers/email/render.js +89 -0
  120. package/dist/providers/email/render.js.map +1 -0
  121. package/dist/providers/email/transport.d.ts +25 -0
  122. package/dist/providers/email/transport.d.ts.map +1 -0
  123. package/dist/providers/email/transport.js +2 -0
  124. package/dist/providers/email/transport.js.map +1 -0
  125. package/dist/providers/slack/adapter.d.ts +35 -0
  126. package/dist/providers/slack/adapter.d.ts.map +1 -0
  127. package/dist/providers/slack/adapter.js +91 -0
  128. package/dist/providers/slack/adapter.js.map +1 -0
  129. package/dist/providers/slack/client.d.ts +45 -0
  130. package/dist/providers/slack/client.d.ts.map +1 -0
  131. package/dist/providers/slack/client.js +54 -0
  132. package/dist/providers/slack/client.js.map +1 -0
  133. package/dist/providers/slack/inbound.d.ts +23 -0
  134. package/dist/providers/slack/inbound.d.ts.map +1 -0
  135. package/dist/providers/slack/inbound.js +76 -0
  136. package/dist/providers/slack/inbound.js.map +1 -0
  137. package/dist/providers/slack/render.d.ts +8 -0
  138. package/dist/providers/slack/render.d.ts.map +1 -0
  139. package/dist/providers/slack/render.js +72 -0
  140. package/dist/providers/slack/render.js.map +1 -0
  141. package/dist/providers/slack/socket.d.ts +34 -0
  142. package/dist/providers/slack/socket.d.ts.map +1 -0
  143. package/dist/providers/slack/socket.js +70 -0
  144. package/dist/providers/slack/socket.js.map +1 -0
  145. package/dist/providers/telegram/adapter.d.ts +35 -0
  146. package/dist/providers/telegram/adapter.d.ts.map +1 -0
  147. package/dist/providers/telegram/adapter.js +109 -0
  148. package/dist/providers/telegram/adapter.js.map +1 -0
  149. package/dist/providers/telegram/client.d.ts +78 -0
  150. package/dist/providers/telegram/client.d.ts.map +1 -0
  151. package/dist/providers/telegram/client.js +53 -0
  152. package/dist/providers/telegram/client.js.map +1 -0
  153. package/dist/providers/telegram/inbound.d.ts +21 -0
  154. package/dist/providers/telegram/inbound.d.ts.map +1 -0
  155. package/dist/providers/telegram/inbound.js +72 -0
  156. package/dist/providers/telegram/inbound.js.map +1 -0
  157. package/dist/providers/telegram/render.d.ts +15 -0
  158. package/dist/providers/telegram/render.d.ts.map +1 -0
  159. package/dist/providers/telegram/render.js +87 -0
  160. package/dist/providers/telegram/render.js.map +1 -0
  161. package/dist/providers/webhook/adapter.d.ts +40 -0
  162. package/dist/providers/webhook/adapter.d.ts.map +1 -0
  163. package/dist/providers/webhook/adapter.js +71 -0
  164. package/dist/providers/webhook/adapter.js.map +1 -0
  165. package/dist/providers/webhook/render.d.ts +26 -0
  166. package/dist/providers/webhook/render.d.ts.map +1 -0
  167. package/dist/providers/webhook/render.js +41 -0
  168. package/dist/providers/webhook/render.js.map +1 -0
  169. package/dist/providers/webhook/signing.d.ts +59 -0
  170. package/dist/providers/webhook/signing.d.ts.map +1 -0
  171. package/dist/providers/webhook/signing.js +108 -0
  172. package/dist/providers/webhook/signing.js.map +1 -0
  173. package/dist/registry.d.ts +16 -0
  174. package/dist/registry.d.ts.map +1 -0
  175. package/dist/registry.js +39 -0
  176. package/dist/registry.js.map +1 -0
  177. package/package.json +42 -0
@@ -0,0 +1,45 @@
1
+ /**
2
+ * A minimal, hand-typed client for the two Slack Web API methods this
3
+ * adapter needs to send/edit a message — plain HTTPS/JSON, same
4
+ * zero-dependency reasoning as `providers/telegram/client.ts`.
5
+ */
6
+ export interface SlackBlock {
7
+ readonly type: string;
8
+ readonly [key: string]: unknown;
9
+ }
10
+ export interface SlackPostMessageParams {
11
+ readonly channel: string;
12
+ readonly text: string;
13
+ readonly blocks?: readonly SlackBlock[];
14
+ }
15
+ export interface SlackUpdateMessageParams {
16
+ readonly channel: string;
17
+ readonly ts: string;
18
+ readonly text: string;
19
+ readonly blocks?: readonly SlackBlock[];
20
+ }
21
+ export interface SlackMessageResult {
22
+ readonly channel: string;
23
+ readonly ts: string;
24
+ }
25
+ export interface SlackClientConfig {
26
+ readonly botToken: string;
27
+ /** Overridable for tests; defaults to the real Slack API host. */
28
+ readonly baseUrl?: string;
29
+ readonly fetchImpl?: typeof fetch;
30
+ /** Overridable for tests, so a 429 retry doesn't really wait. */
31
+ readonly sleepImpl?: (ms: number) => Promise<void>;
32
+ }
33
+ export interface SlackClient {
34
+ postMessage(params: SlackPostMessageParams): Promise<SlackMessageResult>;
35
+ updateMessage(params: SlackUpdateMessageParams): Promise<void>;
36
+ }
37
+ /**
38
+ * Slack's rate-limit signal is an HTTP `Retry-After` header (seconds) on a
39
+ * 429, not a JSON body field like Telegram's `retry_after` — same "wait the
40
+ * value the platform actually gave us" principle either way (`## Pièges
41
+ * connus`: "Prévoir la file, le backoff et le regroupement dès le premier
42
+ * adaptateur").
43
+ */
44
+ export declare function createSlackClient(config: SlackClientConfig): SlackClient;
45
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../../../src/providers/slack/client.ts"],"names":[],"mappings":"AAEA;;;;GAIG;AAEH,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAA;CAChC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;CACxC;AAED,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,CAAC,EAAE,SAAS,UAAU,EAAE,CAAA;CACxC;AAED,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;IACxB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;CACpB;AAeD,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,kEAAkE;IAClE,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACjC,iEAAiE;IACjE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAA;CACnD;AAED,MAAM,WAAW,WAAW;IAC1B,WAAW,CAAC,MAAM,EAAE,sBAAsB,GAAG,OAAO,CAAC,kBAAkB,CAAC,CAAA;IACxE,aAAa,CAAC,MAAM,EAAE,wBAAwB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC/D;AAQD;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,iBAAiB,GAAG,WAAW,CA6CxE"}
@@ -0,0 +1,54 @@
1
+ import { CogentaError } from '@cogenta/core';
2
+ const DEFAULT_BASE_URL = 'https://slack.com/api';
3
+ function defaultSleep(ms) {
4
+ return new Promise((resolve) => setTimeout(resolve, ms));
5
+ }
6
+ /**
7
+ * Slack's rate-limit signal is an HTTP `Retry-After` header (seconds) on a
8
+ * 429, not a JSON body field like Telegram's `retry_after` — same "wait the
9
+ * value the platform actually gave us" principle either way (`## Pièges
10
+ * connus`: "Prévoir la file, le backoff et le regroupement dès le premier
11
+ * adaptateur").
12
+ */
13
+ export function createSlackClient(config) {
14
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
15
+ const fetchImpl = config.fetchImpl ?? fetch;
16
+ const sleepImpl = config.sleepImpl ?? defaultSleep;
17
+ async function call(method, body) {
18
+ for (;;) {
19
+ const response = await fetchImpl(`${baseUrl}/${method}`, {
20
+ method: 'POST',
21
+ headers: {
22
+ 'content-type': 'application/json; charset=utf-8',
23
+ authorization: `Bearer ${config.botToken}`,
24
+ },
25
+ body: JSON.stringify(body),
26
+ });
27
+ if (response.status === 429) {
28
+ const retryAfterSeconds = Number(response.headers.get('retry-after') ?? '1');
29
+ await sleepImpl((Number.isFinite(retryAfterSeconds) ? retryAfterSeconds : 1) * 1000);
30
+ continue;
31
+ }
32
+ const json = (await response.json());
33
+ if (!json.ok) {
34
+ throw new CogentaError({
35
+ code: 'CHANNEL_SLACK_API_ERROR',
36
+ message: `Slack API call "${method}" failed: ${json.error}`,
37
+ hint: 'Check the bot token, scopes, and that the channel this call references still exists.',
38
+ details: { method, error: json.error },
39
+ });
40
+ }
41
+ return json;
42
+ }
43
+ }
44
+ return {
45
+ async postMessage(params) {
46
+ const result = await call('chat.postMessage', params);
47
+ return { channel: result.channel, ts: result.ts };
48
+ },
49
+ async updateMessage(params) {
50
+ await call('chat.update', params);
51
+ },
52
+ };
53
+ }
54
+ //# sourceMappingURL=client.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.js","sourceRoot":"","sources":["../../../src/providers/slack/client.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAA;AA0D5C,MAAM,gBAAgB,GAAG,uBAAuB,CAAA;AAEhD,SAAS,YAAY,CAAC,EAAU;IAC9B,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,CAAA;AAC1D,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,iBAAiB,CAAC,MAAyB;IACzD,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,IAAI,gBAAgB,CAAA;IAClD,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,KAAK,CAAA;IAC3C,MAAM,SAAS,GAAG,MAAM,CAAC,SAAS,IAAI,YAAY,CAAA;IAElD,KAAK,UAAU,IAAI,CAAC,MAAc,EAAE,IAAa;QAC/C,SAAS,CAAC;YACR,MAAM,QAAQ,GAAG,MAAM,SAAS,CAAC,GAAG,OAAO,IAAI,MAAM,EAAE,EAAE;gBACvD,MAAM,EAAE,MAAM;gBACd,OAAO,EAAE;oBACP,cAAc,EAAE,iCAAiC;oBACjD,aAAa,EAAE,UAAU,MAAM,CAAC,QAAQ,EAAE;iBAC3C;gBACD,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,IAAI,CAAC;aAC3B,CAAC,CAAA;YAEF,IAAI,QAAQ,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;gBAC5B,MAAM,iBAAiB,GAAG,MAAM,CAAC,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,CAAA;gBAC5E,MAAM,SAAS,CAAC,CAAC,MAAM,CAAC,QAAQ,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,iBAAiB,CAAC,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,CAAA;gBACpF,SAAQ;YACV,CAAC;YAED,MAAM,IAAI,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAqB,CAAA;YACxD,IAAI,CAAC,IAAI,CAAC,EAAE,EAAE,CAAC;gBACb,MAAM,IAAI,YAAY,CAAC;oBACrB,IAAI,EAAE,yBAAyB;oBAC/B,OAAO,EAAE,mBAAmB,MAAM,aAAa,IAAI,CAAC,KAAK,EAAE;oBAC3D,IAAI,EAAE,sFAAsF;oBAC5F,OAAO,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE;iBACvC,CAAC,CAAA;YACJ,CAAC;YACD,OAAO,IAAI,CAAA;QACb,CAAC;IACH,CAAC;IAED,OAAO;QACL,KAAK,CAAC,WAAW,CAAC,MAAM;YACtB,MAAM,MAAM,GAAG,MAAM,IAAI,CAAC,kBAAkB,EAAE,MAAM,CAAC,CAAA;YACrD,OAAO,EAAE,OAAO,EAAE,MAAM,CAAC,OAAO,EAAE,EAAE,EAAE,MAAM,CAAC,EAAE,EAAE,CAAA;QACnD,CAAC;QAED,KAAK,CAAC,aAAa,CAAC,MAAM;YACxB,MAAM,IAAI,CAAC,aAAa,EAAE,MAAM,CAAC,CAAA;QACnC,CAAC;KACF,CAAA;AACH,CAAC"}
@@ -0,0 +1,23 @@
1
+ import type { InboundHandler } from '../../adapter.js';
2
+ import type { CommandRouter } from '../../inbound/router.js';
3
+ import type { ChannelLinkStore } from '../../linking/store.js';
4
+ import type { SlackClient } from './client.js';
5
+ import type { SlackSocketEnvelope } from './socket.js';
6
+ export interface SlackInboundDeps {
7
+ readonly client: SlackClient;
8
+ readonly linkStore: ChannelLinkStore;
9
+ readonly router: CommandRouter;
10
+ /** The handler registered via the adapter's `onInbound()`, if any. */
11
+ getExternalHandler(): InboundHandler | undefined;
12
+ }
13
+ /**
14
+ * The real routing/authorization wiring for one inbound Slack envelope —
15
+ * extracted from the adapter, same shape as
16
+ * `providers/telegram/inbound.ts`. Handles both a typed message
17
+ * (`events_api`) and a button press (`interactive`'s `block_actions`, whose
18
+ * `action_id`/`value` is itself a command string rendered by `render.ts`)
19
+ * through the exact same `CommandRouter.route()`, never a second, parallel
20
+ * authorization path.
21
+ */
22
+ export declare function createSlackInboundHandler(deps: SlackInboundDeps): (envelope: SlackSocketEnvelope) => Promise<void>;
23
+ //# sourceMappingURL=inbound.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/providers/slack/inbound.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAmB,cAAc,EAAE,MAAM,kBAAkB,CAAA;AACvE,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAC9C,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAA;AAsBtD,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,MAAM,EAAE,WAAW,CAAA;IAC5B,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAA;IACpC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,sEAAsE;IACtE,kBAAkB,IAAI,cAAc,GAAG,SAAS,CAAA;CACjD;AAED;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,gBAAgB,GACrB,CAAC,QAAQ,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,CAqElD"}
@@ -0,0 +1,76 @@
1
+ const CHANNEL_NAME = 'slack';
2
+ /**
3
+ * The real routing/authorization wiring for one inbound Slack envelope —
4
+ * extracted from the adapter, same shape as
5
+ * `providers/telegram/inbound.ts`. Handles both a typed message
6
+ * (`events_api`) and a button press (`interactive`'s `block_actions`, whose
7
+ * `action_id`/`value` is itself a command string rendered by `render.ts`)
8
+ * through the exact same `CommandRouter.route()`, never a second, parallel
9
+ * authorization path.
10
+ */
11
+ export function createSlackInboundHandler(deps) {
12
+ async function resolveIdentity(channelUserId) {
13
+ return deps.linkStore.resolveIdentity(CHANNEL_NAME, channelUserId);
14
+ }
15
+ async function reply(channelId, text) {
16
+ await deps.client.postMessage({ channel: channelId, text });
17
+ }
18
+ async function handleText(channelId, channelUserId, text) {
19
+ let identity = await resolveIdentity(channelUserId);
20
+ if (identity.linkedUserId === null) {
21
+ // An unlinked identity gets exactly one thing to try: the message AS
22
+ // a linking code. Any other failure stays silent — "Une identité de
23
+ // canal non liée à un compte est ignorée, sans réponse."
24
+ try {
25
+ await deps.linkStore.verifyCode(text.trim(), CHANNEL_NAME, channelUserId);
26
+ identity = await resolveIdentity(channelUserId);
27
+ await reply(channelId, ':white_check_mark: Compte lié. Vous pouvez maintenant utiliser les commandes.');
28
+ }
29
+ catch {
30
+ // Wrong/expired/nonexistent code from an unlinked identity: silence.
31
+ }
32
+ const handler = deps.getExternalHandler();
33
+ if (handler !== undefined)
34
+ await handler({ text, identity });
35
+ return;
36
+ }
37
+ const handler = deps.getExternalHandler();
38
+ if (handler !== undefined)
39
+ await handler({ text, identity });
40
+ const result = await deps.router.route(text, identity);
41
+ if (!result.shouldReply)
42
+ return;
43
+ if (result.kind === 'forbidden') {
44
+ await reply(channelId, ":no_entry: Vous n'avez pas la permission d'exécuter cette commande.");
45
+ }
46
+ else if (result.kind === 'unrecognized') {
47
+ await reply(channelId, `Commande inconnue : ${result.commandName}`);
48
+ }
49
+ }
50
+ return async function handleEnvelope(envelope) {
51
+ if (envelope.type === 'events_api') {
52
+ const payload = envelope.payload;
53
+ const event = payload?.event;
54
+ if (event?.type === 'message' &&
55
+ event.bot_id === undefined &&
56
+ event.text !== undefined &&
57
+ event.user !== undefined &&
58
+ event.channel !== undefined) {
59
+ await handleText(event.channel, event.user, event.text);
60
+ }
61
+ return;
62
+ }
63
+ if (envelope.type === 'interactive') {
64
+ const payload = envelope.payload;
65
+ if (payload?.type !== 'block_actions')
66
+ return;
67
+ const channelId = payload.channel?.id;
68
+ const channelUserId = payload.user.id;
69
+ const action = payload.actions[0];
70
+ if (channelId === undefined || action?.action_id === undefined)
71
+ return;
72
+ await handleText(channelId, channelUserId, action.value ?? action.action_id);
73
+ }
74
+ };
75
+ }
76
+ //# sourceMappingURL=inbound.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"inbound.js","sourceRoot":"","sources":["../../../src/providers/slack/inbound.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { ChannelMessage } from '../../adapter.js';
2
+ import type { SlackBlock } from './client.js';
3
+ export interface RenderedSlackMessage {
4
+ readonly text: string;
5
+ readonly blocks: readonly SlackBlock[];
6
+ }
7
+ export declare function renderSlackMessage(message: ChannelMessage): RenderedSlackMessage;
8
+ //# sourceMappingURL=render.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.d.ts","sourceRoot":"","sources":["../../../src/providers/slack/render.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAiB,cAAc,EAAmB,MAAM,kBAAkB,CAAA;AACtF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAA;AA8B7C,MAAM,WAAW,oBAAoB;IACnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,MAAM,EAAE,SAAS,UAAU,EAAE,CAAA;CACvC;AAED,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,cAAc,GAAG,oBAAoB,CA8ChF"}
@@ -0,0 +1,72 @@
1
+ /**
2
+ * "On n'écrit pas de Markdown Telegram dans le code métier" applies just as
3
+ * much to Slack's own Block Kit format — everything platform-specific lives
4
+ * in this module, `ChannelMessage` never knows Block Kit exists.
5
+ */
6
+ const SEVERITY_PREFIX = {
7
+ info: ':information_source:',
8
+ warning: ':warning:',
9
+ critical: ':red_circle:',
10
+ };
11
+ function renderActionsBlock(actions) {
12
+ if (actions === undefined || actions.length === 0)
13
+ return undefined;
14
+ return {
15
+ type: 'actions',
16
+ elements: actions.map((action) => ({
17
+ type: 'button',
18
+ text: { type: 'plain_text', text: action.label },
19
+ // `value` is the command text a button press routes as (mirrors
20
+ // Telegram's `callback_data`) — the same `CommandRouter.route()`,
21
+ // never a second, parallel authorization path.
22
+ value: action.id,
23
+ action_id: action.id,
24
+ })),
25
+ };
26
+ }
27
+ export function renderSlackMessage(message) {
28
+ if (message.level === 'notification') {
29
+ return { text: message.text, blocks: [] };
30
+ }
31
+ if (message.level === 'alert') {
32
+ const prefix = SEVERITY_PREFIX[message.severity];
33
+ const blocks = [
34
+ { type: 'section', text: { type: 'mrkdwn', text: `${prefix} *${message.title}*` } },
35
+ { type: 'section', text: { type: 'mrkdwn', text: message.context } },
36
+ { type: 'section', text: { type: 'mrkdwn', text: `➡️ ${message.expectedAction}` } },
37
+ {
38
+ type: 'section',
39
+ text: { type: 'mrkdwn', text: `<${message.adminUrl}|Voir dans l'admin>` },
40
+ },
41
+ ];
42
+ const actionsBlock = renderActionsBlock(message.actions);
43
+ if (actionsBlock !== undefined)
44
+ blocks.push(actionsBlock);
45
+ return { text: `${prefix} ${message.title}`, blocks };
46
+ }
47
+ // 'report'
48
+ const blocks = [
49
+ { type: 'header', text: { type: 'plain_text', text: message.title } },
50
+ ];
51
+ if (message.keyFigures.length > 0) {
52
+ blocks.push({
53
+ type: 'section',
54
+ text: {
55
+ type: 'mrkdwn',
56
+ text: message.keyFigures.map((figure) => `*${figure.value}* ${figure.label}`).join(' '),
57
+ },
58
+ });
59
+ }
60
+ for (const section of message.sections) {
61
+ const text = section.heading === undefined ? section.body : `*${section.heading}*\n${section.body}`;
62
+ blocks.push({ type: 'section', text: { type: 'mrkdwn', text } });
63
+ }
64
+ if (message.moreUrl !== undefined) {
65
+ blocks.push({
66
+ type: 'section',
67
+ text: { type: 'mrkdwn', text: `<${message.moreUrl}|Voir le détail>` },
68
+ });
69
+ }
70
+ return { text: message.title, blocks };
71
+ }
72
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../../src/providers/slack/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Socket Mode, not a webhook — Slack's own equivalent of Telegram's
3
+ * long-polling choice (`providers/telegram/adapter.ts`): a persistent
4
+ * outbound WebSocket connection to Slack, no public HTTPS endpoint or
5
+ * request-signature verification to half-build, works unchanged wherever
6
+ * `cogenta serve` already runs. Node's built-in `WebSocket` global (stable
7
+ * since Node 22, this package's minimum) means no dependency is needed for
8
+ * the transport itself.
9
+ */
10
+ export interface SlackSocketEnvelope {
11
+ readonly type: string;
12
+ readonly envelope_id?: string;
13
+ readonly payload?: unknown;
14
+ readonly reason?: string;
15
+ }
16
+ export interface SlackSocketClientConfig {
17
+ readonly appToken: string;
18
+ readonly baseUrl?: string;
19
+ readonly fetchImpl?: typeof fetch;
20
+ /** Defaults to the global `WebSocket` (Node's built-in implementation). */
21
+ readonly webSocketImpl?: typeof WebSocket;
22
+ }
23
+ export interface SlackSocketClient {
24
+ /** Opens the connection and delivers every envelope Slack sends; acks each one automatically once `onEnvelope` resolves. Resolves once the initial connection is established. */
25
+ connect(onEnvelope: (envelope: SlackSocketEnvelope) => Promise<void>): Promise<void>;
26
+ disconnect(): void;
27
+ }
28
+ /**
29
+ * A `disconnect` envelope (`reason: 'refresh_requested' | 'warning' | ...`)
30
+ * means Slack is about to close this socket — reconnect once, exactly like
31
+ * Telegram's poll loop keeps calling `getUpdates` while `polling` is true.
32
+ */
33
+ export declare function createSlackSocketClient(config: SlackSocketClientConfig): SlackSocketClient;
34
+ //# sourceMappingURL=socket.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket.d.ts","sourceRoot":"","sources":["../../../src/providers/slack/socket.ts"],"names":[],"mappings":"AAEA;;;;;;;;GAQG;AAEH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;IAC7B,QAAQ,CAAC,OAAO,CAAC,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CACzB;AAED,MAAM,WAAW,uBAAuB;IACtC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,SAAS,CAAC,EAAE,OAAO,KAAK,CAAA;IACjC,2EAA2E;IAC3E,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,SAAS,CAAA;CAC1C;AAED,MAAM,WAAW,iBAAiB;IAChC,iLAAiL;IACjL,OAAO,CAAC,UAAU,EAAE,CAAC,QAAQ,EAAE,mBAAmB,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IACpF,UAAU,IAAI,IAAI,CAAA;CACnB;AAgCD;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,uBAAuB,GAAG,iBAAiB,CAsD1F"}
@@ -0,0 +1,70 @@
1
+ import { CogentaError } from '@cogenta/core';
2
+ const DEFAULT_BASE_URL = 'https://slack.com/api';
3
+ async function openConnection(fetchImpl, baseUrl, appToken) {
4
+ const response = await fetchImpl(`${baseUrl}/apps.connections.open`, {
5
+ method: 'POST',
6
+ headers: { authorization: `Bearer ${appToken}` },
7
+ });
8
+ const json = (await response.json());
9
+ if (!json.ok) {
10
+ throw new CogentaError({
11
+ code: 'CHANNEL_SLACK_API_ERROR',
12
+ message: `Slack Socket Mode connection failed: ${json.error}`,
13
+ hint: 'Check the app-level token (xapp-...) has the connections:write scope.',
14
+ details: { error: json.error },
15
+ });
16
+ }
17
+ return json.url;
18
+ }
19
+ /**
20
+ * A `disconnect` envelope (`reason: 'refresh_requested' | 'warning' | ...`)
21
+ * means Slack is about to close this socket — reconnect once, exactly like
22
+ * Telegram's poll loop keeps calling `getUpdates` while `polling` is true.
23
+ */
24
+ export function createSlackSocketClient(config) {
25
+ const baseUrl = config.baseUrl ?? DEFAULT_BASE_URL;
26
+ const fetchImpl = config.fetchImpl ?? fetch;
27
+ const WebSocketImpl = config.webSocketImpl ?? WebSocket;
28
+ let socket;
29
+ let connected = false;
30
+ function ack(envelopeId) {
31
+ if (envelopeId === undefined || socket === undefined)
32
+ return;
33
+ socket.send(JSON.stringify({ envelope_id: envelopeId }));
34
+ }
35
+ async function openSocket(onEnvelope) {
36
+ const url = await openConnection(fetchImpl, baseUrl, config.appToken);
37
+ const ws = new WebSocketImpl(url);
38
+ socket = ws;
39
+ await new Promise((resolve, reject) => {
40
+ ws.addEventListener('open', () => resolve());
41
+ ws.addEventListener('error', () => reject(new Error('Slack Socket Mode connection failed to open.')));
42
+ });
43
+ ws.addEventListener('message', (event) => {
44
+ const raw = typeof event.data === 'string' ? event.data : String(event.data);
45
+ const envelope = JSON.parse(raw);
46
+ void (async () => {
47
+ if (envelope.type === 'disconnect') {
48
+ ws.close();
49
+ if (connected)
50
+ await openSocket(onEnvelope);
51
+ return;
52
+ }
53
+ await onEnvelope(envelope);
54
+ ack(envelope.envelope_id);
55
+ })();
56
+ });
57
+ }
58
+ return {
59
+ async connect(onEnvelope) {
60
+ connected = true;
61
+ await openSocket(onEnvelope);
62
+ },
63
+ disconnect() {
64
+ connected = false;
65
+ socket?.close();
66
+ socket = undefined;
67
+ },
68
+ };
69
+ }
70
+ //# sourceMappingURL=socket.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"socket.js","sourceRoot":"","sources":["../../../src/providers/slack/socket.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,35 @@
1
+ import type { ChannelAdapter, ChannelMessage, InboundHandler, MessageId } from '../../adapter.js';
2
+ import type { CommandRouter } from '../../inbound/router.js';
3
+ import type { ChannelLinkStore } from '../../linking/store.js';
4
+ import { type TelegramClientConfig } from './client.js';
5
+ export interface TelegramLinkProof {
6
+ readonly code: string;
7
+ readonly channelUserId: string;
8
+ }
9
+ export interface TelegramAdapterOptions extends TelegramClientConfig {
10
+ readonly linkStore: ChannelLinkStore;
11
+ readonly router: CommandRouter;
12
+ readonly pollTimeoutSeconds?: number;
13
+ }
14
+ /**
15
+ * A live channel adapter beyond the generic interface: `start()`/`stop()`
16
+ * manage the long-polling loop, which has no counterpart in `ChannelAdapter`
17
+ * (a webhook-based provider would have no need for it) — platform-specific
18
+ * lifecycle concerns live on the concrete adapter, not the shared interface.
19
+ */
20
+ export interface TelegramAdapter extends ChannelAdapter {
21
+ update(id: MessageId, message: ChannelMessage): Promise<void>;
22
+ onInbound(handler: InboundHandler): void;
23
+ start(): void;
24
+ stop(): Promise<void>;
25
+ }
26
+ /**
27
+ * "Telegram en premier, complet." Long-polling (`getUpdates`), not a
28
+ * webhook: a webhook needs a real public HTTPS endpoint and Telegram's own
29
+ * `X-Telegram-Bot-Api-Secret-Token` signature verification, and no plane of
30
+ * this project is deployed publicly yet (L9 task 12's scoping). Polling
31
+ * works unchanged wherever `cogenta serve` already runs — locally, on a
32
+ * VPS, in CI — with no exposure and no signing infrastructure to half-build.
33
+ */
34
+ export declare function createTelegramAdapter(options: TelegramAdapterOptions): TelegramAdapter;
35
+ //# sourceMappingURL=adapter.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../../../src/providers/telegram/adapter.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,cAAc,EAEd,cAAc,EAEd,cAAc,EACd,SAAS,EACV,MAAM,kBAAkB,CAAA;AACzB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,yBAAyB,CAAA;AAC5D,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAC9D,OAAO,EAA6C,KAAK,oBAAoB,EAAE,MAAM,aAAa,CAAA;AAOlG,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;IACrB,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAA;CAC/B;AAWD,MAAM,WAAW,sBAAuB,SAAQ,oBAAoB;IAClE,QAAQ,CAAC,SAAS,EAAE,gBAAgB,CAAA;IACpC,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAA;IAC9B,QAAQ,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAA;CACrC;AAED;;;;;GAKG;AACH,MAAM,WAAW,eAAgB,SAAQ,cAAc;IAGrD,MAAM,CAAC,EAAE,EAAE,SAAS,EAAE,OAAO,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;IAC7D,SAAS,CAAC,OAAO,EAAE,cAAc,GAAG,IAAI,CAAA;IACxC,KAAK,IAAI,IAAI,CAAA;IACb,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAA;CACtB;AAED;;;;;;;GAOG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,sBAAsB,GAAG,eAAe,CAgGtF"}
@@ -0,0 +1,109 @@
1
+ import { CogentaError } from '@cogenta/core';
2
+ import { createTelegramClient } from './client.js';
3
+ import { createTelegramInboundHandler } from './inbound.js';
4
+ import { renderTelegramMessage } from './render.js';
5
+ const CHANNEL_NAME = 'telegram';
6
+ const DEFAULT_POLL_TIMEOUT_SECONDS = 30;
7
+ function isTelegramLinkProof(proof) {
8
+ return (typeof proof === 'object' &&
9
+ proof !== null &&
10
+ typeof proof.code === 'string' &&
11
+ typeof proof.channelUserId === 'string');
12
+ }
13
+ /**
14
+ * "Telegram en premier, complet." Long-polling (`getUpdates`), not a
15
+ * webhook: a webhook needs a real public HTTPS endpoint and Telegram's own
16
+ * `X-Telegram-Bot-Api-Secret-Token` signature verification, and no plane of
17
+ * this project is deployed publicly yet (L9 task 12's scoping). Polling
18
+ * works unchanged wherever `cogenta serve` already runs — locally, on a
19
+ * VPS, in CI — with no exposure and no signing infrastructure to half-build.
20
+ */
21
+ export function createTelegramAdapter(options) {
22
+ const client = createTelegramClient(options);
23
+ const pollTimeoutSeconds = options.pollTimeoutSeconds ?? DEFAULT_POLL_TIMEOUT_SECONDS;
24
+ let inboundHandler;
25
+ let polling = false;
26
+ let pollLoop;
27
+ let updateOffset = 0;
28
+ const handleUpdate = createTelegramInboundHandler({
29
+ client,
30
+ linkStore: options.linkStore,
31
+ router: options.router,
32
+ getExternalHandler: () => inboundHandler,
33
+ });
34
+ async function pollOnce() {
35
+ const updates = await client.getUpdates(updateOffset, pollTimeoutSeconds);
36
+ for (const update of updates) {
37
+ updateOffset = update.update_id + 1;
38
+ await handleUpdate(update);
39
+ }
40
+ }
41
+ return {
42
+ name: CHANNEL_NAME,
43
+ capabilities: {
44
+ richText: true,
45
+ buttons: true,
46
+ threads: false,
47
+ attachments: false,
48
+ inbound: true,
49
+ },
50
+ async send(target, message) {
51
+ const rendered = renderTelegramMessage(message);
52
+ const sent = await client.sendMessage({
53
+ chat_id: target.id,
54
+ text: rendered.text,
55
+ parse_mode: 'MarkdownV2',
56
+ ...(rendered.replyMarkup === undefined ? {} : { reply_markup: rendered.replyMarkup }),
57
+ });
58
+ return `${sent.chat.id}:${sent.message_id}`;
59
+ },
60
+ async update(id, message) {
61
+ const [chatId, messageIdPart] = id.split(':');
62
+ if (chatId === undefined || messageIdPart === undefined) {
63
+ throw new CogentaError({
64
+ code: 'CHANNEL_TELEGRAM_API_ERROR',
65
+ message: `"${id}" is not a message id this adapter produced.`,
66
+ hint: "Only pass a MessageId returned by this adapter's own send() to update().",
67
+ });
68
+ }
69
+ const rendered = renderTelegramMessage(message);
70
+ await client.editMessageText({
71
+ chat_id: chatId,
72
+ message_id: Number(messageIdPart),
73
+ text: rendered.text,
74
+ parse_mode: 'MarkdownV2',
75
+ ...(rendered.replyMarkup === undefined ? {} : { reply_markup: rendered.replyMarkup }),
76
+ });
77
+ },
78
+ onInbound(handler) {
79
+ inboundHandler = handler;
80
+ },
81
+ async verifyIdentity(proof) {
82
+ if (!isTelegramLinkProof(proof)) {
83
+ throw new CogentaError({
84
+ code: 'CHANNEL_TELEGRAM_API_ERROR',
85
+ message: 'Telegram identity proof must be { code, channelUserId }.',
86
+ hint: 'Pass the linking code submitted in the channel and the Telegram user id it came from.',
87
+ });
88
+ }
89
+ await options.linkStore.verifyCode(proof.code, CHANNEL_NAME, proof.channelUserId);
90
+ return options.linkStore.resolveIdentity(CHANNEL_NAME, proof.channelUserId);
91
+ },
92
+ start() {
93
+ if (polling)
94
+ return;
95
+ polling = true;
96
+ pollLoop = (async () => {
97
+ while (polling) {
98
+ await pollOnce();
99
+ }
100
+ })();
101
+ },
102
+ async stop() {
103
+ polling = false;
104
+ await pollLoop;
105
+ pollLoop = undefined;
106
+ },
107
+ };
108
+ }
109
+ //# sourceMappingURL=adapter.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"adapter.js","sourceRoot":"","sources":["../../../src/providers/telegram/adapter.ts"],"names":[],"mappings":"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"}