@krischoichoi/channel-telegram 0.6.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 (82) hide show
  1. package/README.md +129 -0
  2. package/lib/adapter.d.ts +94 -0
  3. package/lib/adapter.d.ts.map +1 -0
  4. package/lib/adapter.js +417 -0
  5. package/lib/adapter.js.map +1 -0
  6. package/lib/api-error.d.ts +74 -0
  7. package/lib/api-error.d.ts.map +1 -0
  8. package/lib/api-error.js +103 -0
  9. package/lib/api-error.js.map +1 -0
  10. package/lib/config.d.ts +115 -0
  11. package/lib/config.d.ts.map +1 -0
  12. package/lib/config.js +67 -0
  13. package/lib/config.js.map +1 -0
  14. package/lib/definition.d.ts +59 -0
  15. package/lib/definition.d.ts.map +1 -0
  16. package/lib/definition.js +158 -0
  17. package/lib/definition.js.map +1 -0
  18. package/lib/inbound.d.ts +61 -0
  19. package/lib/inbound.d.ts.map +1 -0
  20. package/lib/inbound.js +125 -0
  21. package/lib/inbound.js.map +1 -0
  22. package/lib/index.d.ts +52 -0
  23. package/lib/index.d.ts.map +1 -0
  24. package/lib/index.js +114 -0
  25. package/lib/index.js.map +1 -0
  26. package/lib/manifest.d.ts +16 -0
  27. package/lib/manifest.d.ts.map +1 -0
  28. package/lib/manifest.js +26 -0
  29. package/lib/manifest.js.map +1 -0
  30. package/lib/mapper.d.ts +51 -0
  31. package/lib/mapper.d.ts.map +1 -0
  32. package/lib/mapper.js +314 -0
  33. package/lib/mapper.js.map +1 -0
  34. package/lib/media-hydrator.d.ts +46 -0
  35. package/lib/media-hydrator.d.ts.map +1 -0
  36. package/lib/media-hydrator.js +85 -0
  37. package/lib/media-hydrator.js.map +1 -0
  38. package/lib/outbound.d.ts +42 -0
  39. package/lib/outbound.d.ts.map +1 -0
  40. package/lib/outbound.js +215 -0
  41. package/lib/outbound.js.map +1 -0
  42. package/lib/render/html.d.ts +13 -0
  43. package/lib/render/html.d.ts.map +1 -0
  44. package/lib/render/html.js +110 -0
  45. package/lib/render/html.js.map +1 -0
  46. package/lib/render/index.d.ts +64 -0
  47. package/lib/render/index.d.ts.map +1 -0
  48. package/lib/render/index.js +76 -0
  49. package/lib/render/index.js.map +1 -0
  50. package/lib/render/markdown.d.ts +38 -0
  51. package/lib/render/markdown.d.ts.map +1 -0
  52. package/lib/render/markdown.js +187 -0
  53. package/lib/render/markdown.js.map +1 -0
  54. package/lib/render/plain.d.ts +43 -0
  55. package/lib/render/plain.d.ts.map +1 -0
  56. package/lib/render/plain.js +97 -0
  57. package/lib/render/plain.js.map +1 -0
  58. package/lib/render/segment.d.ts +39 -0
  59. package/lib/render/segment.d.ts.map +1 -0
  60. package/lib/render/segment.js +117 -0
  61. package/lib/render/segment.js.map +1 -0
  62. package/lib/rich-message.d.ts +75 -0
  63. package/lib/rich-message.d.ts.map +1 -0
  64. package/lib/rich-message.js +11 -0
  65. package/lib/rich-message.js.map +1 -0
  66. package/lib/rich-streaming-reply.d.ts +69 -0
  67. package/lib/rich-streaming-reply.d.ts.map +1 -0
  68. package/lib/rich-streaming-reply.js +207 -0
  69. package/lib/rich-streaming-reply.js.map +1 -0
  70. package/lib/streaming-reply.d.ts +98 -0
  71. package/lib/streaming-reply.d.ts.map +1 -0
  72. package/lib/streaming-reply.js +274 -0
  73. package/lib/streaming-reply.js.map +1 -0
  74. package/lib/transport.d.ts +38 -0
  75. package/lib/transport.d.ts.map +1 -0
  76. package/lib/transport.js +91 -0
  77. package/lib/transport.js.map +1 -0
  78. package/lib/upstream.d.ts +174 -0
  79. package/lib/upstream.d.ts.map +1 -0
  80. package/lib/upstream.js +452 -0
  81. package/lib/upstream.js.map +1 -0
  82. package/package.json +52 -0
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Structured Telegram Bot API error (execution plan §3.1 / §5.4).
3
+ *
4
+ * Telegram's error envelope carries `error_code`/`description` and, for some
5
+ * failures, a `parameters` object (e.g. `retry_after`, `migrate_to_chat_id`).
6
+ * The previous code collapsed every non-ok `sendMessage` into a bare
7
+ * "invalid response" catch-all, which made it impossible to distinguish a
8
+ * rich-formatting parse failure (→ plain fallback) from a 401/403 (→ never
9
+ * fallback), a 429 (→ rate-limit retry) or a 5xx/network failure (→ retry).
10
+ *
11
+ * `TelegramApiError` preserves the original fields and classifies each error
12
+ * into a stable `TelegramErrorKind`. It is constructed at the envelope/post
13
+ * boundary in `HttpTelegramUpstream`, so downstream code (the renderer and the
14
+ * reply engine) can decide retry / fallback / fail without rebuilding the
15
+ * classification from raw numbers.
16
+ */
17
+ import { ChannelError } from '@krischoichoi/channel-core';
18
+ /**
19
+ * Machine-readable Telegram error class used to drive fallback/retry policy.
20
+ *
21
+ * - `format` — Telegram rejected the formatted payload (bad entity, rich
22
+ * message parse failure). The only kind that may trigger a
23
+ * one-shot plain fallback.
24
+ * - `rate-limit`— HTTP 429; retry after `parameters.retryAfter`.
25
+ * - `auth` — 401 invalid token.
26
+ * - `permission`— 403 the bot lacks permission / was removed; never fallback.
27
+ * - `network` — the HTTP request never got a valid Bot API response (timeout,
28
+ * aborted, transport failure). Not a formatting problem.
29
+ * - `upstream` — 5xx / other genuine Telegram-side server errors. Retry.
30
+ */
31
+ export type TelegramErrorKind = 'format' | 'rate-limit' | 'auth' | 'permission' | 'network' | 'upstream';
32
+ /** Fields Telegram may return in the error `parameters` object. */
33
+ export interface TelegramErrorParameters {
34
+ retryAfter?: number;
35
+ migrateToChatId?: number;
36
+ }
37
+ /**
38
+ * Classify a Telegram `error_code` (plus optional retry/migration parameters)
39
+ * into a stable `TelegramErrorKind`. This is the single source of truth for
40
+ * fallback / retry policy; no caller should infer the kind from raw numbers.
41
+ *
42
+ * Logic (execution plan §20.9):
43
+ * - explicit retry_after -> rate-limit
44
+ * - 401 -> auth
45
+ * - 403 -> permission
46
+ * - 429 -> rate-limit
47
+ * - 5xx -> upstream
48
+ * - description match addenda -> format (only for 400-class non-authed)
49
+ * - other 400 responses -> upstream (Telegram also uses 400 for chat,
50
+ * message, thread, and button errors; those must never trigger fallback)
51
+ * - unknown / missing code -> upstream (not enough evidence to fallback)
52
+ */
53
+ export declare function classifyTelegramError(errorCode: number | undefined, description: string | undefined, parameters?: TelegramErrorParameters): TelegramErrorKind;
54
+ export interface TelegramApiErrorOptions extends ErrorOptions {
55
+ method?: string;
56
+ errorCode?: number;
57
+ description?: string;
58
+ parameters?: TelegramErrorParameters;
59
+ kind?: TelegramErrorKind;
60
+ }
61
+ /**
62
+ * Structured Telegram error. Extends `ChannelError` so existing adapter-level
63
+ * error mapping (which asserts `ChannelError`) keeps working, while preserving
64
+ * the Telegram-specific original fields and the stable classification.
65
+ */
66
+ export declare class TelegramApiError extends ChannelError {
67
+ readonly method: string;
68
+ readonly errorCode?: number;
69
+ readonly description?: string;
70
+ readonly parameters?: TelegramErrorParameters;
71
+ readonly kind: TelegramErrorKind;
72
+ constructor(options?: TelegramApiErrorOptions);
73
+ }
74
+ //# sourceMappingURL=api-error.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-error.d.ts","sourceRoot":"","sources":["../src/api-error.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,OAAO,EAAE,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE1D;;;;;;;;;;;;GAYG;AACH,MAAM,MAAM,iBAAiB,GACzB,QAAQ,GACR,YAAY,GACZ,MAAM,GACN,YAAY,GACZ,SAAS,GACT,UAAU,CAAC;AAEf,mEAAmE;AACnE,MAAM,WAAW,uBAAuB;IACtC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B;AAcD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qBAAqB,CACnC,SAAS,EAAE,MAAM,GAAG,SAAS,EAC7B,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,UAAU,CAAC,EAAE,uBAAuB,GACnC,iBAAiB,CAcnB;AAcD,MAAM,WAAW,uBAAwB,SAAQ,YAAY;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,uBAAuB,CAAC;IACrC,IAAI,CAAC,EAAE,iBAAiB,CAAC;CAC1B;AAED;;;;GAIG;AACH,qBAAa,gBAAiB,SAAQ,YAAY;IAChD,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,UAAU,CAAC,EAAE,uBAAuB,CAAC;IAC9C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;gBAErB,OAAO,GAAE,uBAA4B;CAalD"}
@@ -0,0 +1,103 @@
1
+ /**
2
+ * Structured Telegram Bot API error (execution plan §3.1 / §5.4).
3
+ *
4
+ * Telegram's error envelope carries `error_code`/`description` and, for some
5
+ * failures, a `parameters` object (e.g. `retry_after`, `migrate_to_chat_id`).
6
+ * The previous code collapsed every non-ok `sendMessage` into a bare
7
+ * "invalid response" catch-all, which made it impossible to distinguish a
8
+ * rich-formatting parse failure (→ plain fallback) from a 401/403 (→ never
9
+ * fallback), a 429 (→ rate-limit retry) or a 5xx/network failure (→ retry).
10
+ *
11
+ * `TelegramApiError` preserves the original fields and classifies each error
12
+ * into a stable `TelegramErrorKind`. It is constructed at the envelope/post
13
+ * boundary in `HttpTelegramUpstream`, so downstream code (the renderer and the
14
+ * reply engine) can decide retry / fallback / fail without rebuilding the
15
+ * classification from raw numbers.
16
+ */
17
+ import { ChannelError } from '@krischoichoi/channel-core';
18
+ /** Telegram errors Telegram may legitimately return (by description text). */
19
+ const FORMAT_TEXT_MARKERS = [
20
+ 'can\'t parse entities',
21
+ 'entity is too long',
22
+ 'failed to parse',
23
+ 'rich message',
24
+ 'message entities',
25
+ 'unsupported start tag',
26
+ 'unsupported end tag',
27
+ 'unclosed start tag',
28
+ ];
29
+ /**
30
+ * Classify a Telegram `error_code` (plus optional retry/migration parameters)
31
+ * into a stable `TelegramErrorKind`. This is the single source of truth for
32
+ * fallback / retry policy; no caller should infer the kind from raw numbers.
33
+ *
34
+ * Logic (execution plan §20.9):
35
+ * - explicit retry_after -> rate-limit
36
+ * - 401 -> auth
37
+ * - 403 -> permission
38
+ * - 429 -> rate-limit
39
+ * - 5xx -> upstream
40
+ * - description match addenda -> format (only for 400-class non-authed)
41
+ * - other 400 responses -> upstream (Telegram also uses 400 for chat,
42
+ * message, thread, and button errors; those must never trigger fallback)
43
+ * - unknown / missing code -> upstream (not enough evidence to fallback)
44
+ */
45
+ export function classifyTelegramError(errorCode, description, parameters) {
46
+ if (parameters?.retryAfter !== undefined)
47
+ return 'rate-limit';
48
+ if (errorCode === 401)
49
+ return 'auth';
50
+ if (errorCode === 403)
51
+ return 'permission';
52
+ if (errorCode === 429)
53
+ return 'rate-limit';
54
+ if (errorCode !== undefined && errorCode >= 500)
55
+ return 'upstream';
56
+ if (errorCode === 400 && description) {
57
+ const lower = description.toLowerCase();
58
+ if (FORMAT_TEXT_MARKERS.some((marker) => lower.includes(marker)))
59
+ return 'format';
60
+ return 'upstream';
61
+ }
62
+ if (errorCode === 400)
63
+ return 'upstream';
64
+ // No usable code / unexpected shape — treat as an upstream-level failure.
65
+ return 'upstream';
66
+ }
67
+ /** Map the resolved kind to a stable, readable message fragment. */
68
+ function kindLabel(kind) {
69
+ switch (kind) {
70
+ case 'format': return 'format';
71
+ case 'rate-limit': return 'rate-limit';
72
+ case 'auth': return 'auth';
73
+ case 'permission': return 'permission';
74
+ case 'network': return 'network';
75
+ case 'upstream': return 'upstream';
76
+ }
77
+ }
78
+ /**
79
+ * Structured Telegram error. Extends `ChannelError` so existing adapter-level
80
+ * error mapping (which asserts `ChannelError`) keeps working, while preserving
81
+ * the Telegram-specific original fields and the stable classification.
82
+ */
83
+ export class TelegramApiError extends ChannelError {
84
+ method;
85
+ errorCode;
86
+ description;
87
+ parameters;
88
+ kind;
89
+ constructor(options = {}) {
90
+ const kind = options.kind ?? classifyTelegramError(options.errorCode, options.description, options.parameters);
91
+ const method = options.method ?? 'telegram';
92
+ const detail = options.description ?? 'unknown error';
93
+ const code = kind === 'auth' ? 'CHANNEL_AUTH_FAILED' : 'CHANNEL_ERROR';
94
+ super(code, `telegram ${method} failed (${kindLabel(kind)}): ${detail}`, options);
95
+ this.name = 'TelegramApiError';
96
+ this.method = method;
97
+ this.errorCode = options.errorCode;
98
+ this.description = options.description;
99
+ this.parameters = options.parameters;
100
+ this.kind = kind;
101
+ }
102
+ }
103
+ //# sourceMappingURL=api-error.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"api-error.js","sourceRoot":"","sources":["../src/api-error.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,115 @@
1
+ /**
2
+ * Telegram adapter configuration (Schemastery).
3
+ *
4
+ * Every deployment-tunable parameter is configurable here — no hardcoded
5
+ * deployment constants. The Bot API token is a secret: config carries only its
6
+ * credential reference (`tokenRef`, default `TELEGRAM_BOT_TOKEN`); the real
7
+ * value is resolved through `ctx.credentials` at startup and never lives in
8
+ * profile config, logs, or fixtures.
9
+ *
10
+ * Migration note: legacy configs may still carry a plaintext `token`. The field
11
+ * is deprecated and hidden; the plugin's apply() migrates it into
12
+ * `ctx.credentials` under `tokenRef` exactly once and strips the plaintext.
13
+ */
14
+ import Schema from '@deepseek-ai/schemastery';
15
+ import type { Volatile } from '@deepseek-ai/cordis';
16
+ /** Default credential reference name for the Telegram Bot API token. */
17
+ export declare const TELEGRAM_BOT_TOKEN_REF = "TELEGRAM_BOT_TOKEN";
18
+ export interface TelegramReconnectConfig {
19
+ enabled: boolean;
20
+ baseDelayMs: number;
21
+ maxDelayMs: number;
22
+ maxRetries: number;
23
+ }
24
+ export interface TelegramDedupConfig {
25
+ enabled: boolean;
26
+ windowMs: number;
27
+ }
28
+ export interface TelegramStreamingConfig {
29
+ /**
30
+ * Whether incremental outbound streaming is enabled. When false, the reply
31
+ * router falls back to the buffered send-once strategy.
32
+ */
33
+ enabled: boolean;
34
+ /** Text sent as the initial placeholder while the model is working. */
35
+ placeholder: string;
36
+ }
37
+ /** Telegram's typing action expires after a few seconds, so it is refreshed. */
38
+ export interface TelegramTypingConfig {
39
+ enabled: boolean;
40
+ refreshMs: number;
41
+ }
42
+ /**
43
+ * Outbound formatting policy (plan §5.1). The adapter's minimum supported
44
+ * upstream is Bot API 10.2, so `auto` always selects Rich Markdown.
45
+ */
46
+ export interface TelegramFormattingConfig {
47
+ /**
48
+ * Output renderer:
49
+ * - `auto` -> Rich Markdown
50
+ * - `rich-markdown`-> sendRichMessage / sendRichMessageDraft
51
+ * - `html` -> sendMessage(parse_mode=HTML) via the safe HTML renderer
52
+ * - `markdown-v2` -> expert-compat mode (fully escaped, never raw Agent MD)
53
+ * - `plain` -> no formatting parsed
54
+ */
55
+ mode: 'auto' | 'rich-markdown' | 'html' | 'markdown-v2' | 'plain';
56
+ /**
57
+ * What to fall back to when the selected mode fails with a *format* error.
58
+ * Only `plain` is supported today; 401/403 / 429 / network / 5xx never
59
+ * trigger this fallback (plan §20.9).
60
+ */
61
+ fallback: 'plain';
62
+ }
63
+ export interface TelegramConfig {
64
+ enabled: boolean;
65
+ /** Account id within the telegram channel (defaults to 'main'). */
66
+ accountId: string;
67
+ /** Base URL of the Telegram Bot API. */
68
+ baseUrl: string;
69
+ /**
70
+ * Credential reference name for the Bot API token (resolved via
71
+ * `ctx.credentials`). Defaults to `TELEGRAM_BOT_TOKEN_REF`. Only the
72
+ * reference name lives in config — never the token itself.
73
+ */
74
+ tokenRef: string;
75
+ /**
76
+ * @deprecated Migration-only compatibility field. Legacy plaintext configs
77
+ * still parse; apply() migrates the value into `ctx.credentials` under
78
+ * `tokenRef` and deletes this field. New configs must use `tokenRef`.
79
+ */
80
+ token?: string;
81
+ /** Per-request timeout. */
82
+ timeoutMs: number;
83
+ /** Long-poll hang time for one getUpdates cycle. */
84
+ longPollTimeoutMs: number;
85
+ reconnect: TelegramReconnectConfig;
86
+ dedup: TelegramDedupConfig;
87
+ streaming: TelegramStreamingConfig;
88
+ typing: TelegramTypingConfig;
89
+ /** Outbound formatting / rich rendering policy. */
90
+ formatting: TelegramFormattingConfig;
91
+ /** Hard byte cap for one inbound media download (image / document). */
92
+ maxDownloadBytes: number;
93
+ }
94
+ /**
95
+ * Activation-time config shape (Harness 0.2.0): every volatile field resolves
96
+ * to a `Volatile<T>` live handle instead of a plain value. `apply()` unwraps
97
+ * these once via `resolveVolatileConfig` into a plain `TelegramConfig`.
98
+ */
99
+ export interface TelegramConfigLive {
100
+ enabled: Volatile<boolean>;
101
+ accountId: Volatile<string>;
102
+ baseUrl: Volatile<string>;
103
+ tokenRef: string;
104
+ token?: string;
105
+ timeoutMs: Volatile<number>;
106
+ longPollTimeoutMs: Volatile<number>;
107
+ reconnect: Volatile<TelegramReconnectConfig>;
108
+ dedup: Volatile<TelegramDedupConfig>;
109
+ streaming: Volatile<TelegramStreamingConfig>;
110
+ typing: Volatile<TelegramTypingConfig>;
111
+ formatting: Volatile<TelegramFormattingConfig>;
112
+ maxDownloadBytes: Volatile<number>;
113
+ }
114
+ export declare const Config: Schema<TelegramConfigLive>;
115
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AACH,OAAO,MAAM,MAAM,0BAA0B,CAAC;AAC9C,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AAEpD,wEAAwE;AACxE,eAAO,MAAM,sBAAsB,uBAAuB,CAAC;AAE3D,MAAM,WAAW,uBAAuB;IACtC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC;;;OAGG;IACH,OAAO,EAAE,OAAO,CAAC;IACjB,uEAAuE;IACvE,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,gFAAgF;AAChF,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,wBAAwB;IACvC;;;;;;;OAOG;IACH,IAAI,EAAE,MAAM,GAAG,eAAe,GAAG,MAAM,GAAG,aAAa,GAAG,OAAO,CAAC;IAClE;;;;OAIG;IACH,QAAQ,EAAE,OAAO,CAAC;CACnB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,mEAAmE;IACnE,SAAS,EAAE,MAAM,CAAC;IAClB,wCAAwC;IACxC,OAAO,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,QAAQ,EAAE,MAAM,CAAC;IACjB;;;;OAIG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,2BAA2B;IAC3B,SAAS,EAAE,MAAM,CAAC;IAClB,oDAAoD;IACpD,iBAAiB,EAAE,MAAM,CAAC;IAC1B,SAAS,EAAE,uBAAuB,CAAC;IACnC,KAAK,EAAE,mBAAmB,CAAC;IAC3B,SAAS,EAAE,uBAAuB,CAAC;IACnC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,mDAAmD;IACnD,UAAU,EAAE,wBAAwB,CAAC;IACrC,uEAAuE;IACvE,gBAAgB,EAAE,MAAM,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;IAC3B,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC5B,iBAAiB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;IACpC,SAAS,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC7C,KAAK,EAAE,QAAQ,CAAC,mBAAmB,CAAC,CAAC;IACrC,SAAS,EAAE,QAAQ,CAAC,uBAAuB,CAAC,CAAC;IAC7C,MAAM,EAAE,QAAQ,CAAC,oBAAoB,CAAC,CAAC;IACvC,UAAU,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;IAC/C,gBAAgB,EAAE,QAAQ,CAAC,MAAM,CAAC,CAAC;CACpC;AAMD,eAAO,MAAM,MAAM,EA6CF,MAAM,CAAC,kBAAkB,CAAC,CAAC"}
package/lib/config.js ADDED
@@ -0,0 +1,67 @@
1
+ /**
2
+ * Telegram adapter configuration (Schemastery).
3
+ *
4
+ * Every deployment-tunable parameter is configurable here — no hardcoded
5
+ * deployment constants. The Bot API token is a secret: config carries only its
6
+ * credential reference (`tokenRef`, default `TELEGRAM_BOT_TOKEN`); the real
7
+ * value is resolved through `ctx.credentials` at startup and never lives in
8
+ * profile config, logs, or fixtures.
9
+ *
10
+ * Migration note: legacy configs may still carry a plaintext `token`. The field
11
+ * is deprecated and hidden; the plugin's apply() migrates it into
12
+ * `ctx.credentials` under `tokenRef` exactly once and strips the plaintext.
13
+ */
14
+ import Schema from '@deepseek-ai/schemastery';
15
+ /** Default credential reference name for the Telegram Bot API token. */
16
+ export const TELEGRAM_BOT_TOKEN_REF = 'TELEGRAM_BOT_TOKEN';
17
+ // The runtime schema is genuine Schemastery (validation happens at the Loader
18
+ // boundary); the annotation records the activation-time output shape. The cast
19
+ // is required because Schemastery's inferred ObjectT models per-field optionality
20
+ // more precisely than the hand-written `TelegramConfigLive` alias.
21
+ export const Config = Schema.object({
22
+ // Harness 0.2.0 settings model: fields the control plane / settings page may
23
+ // write at runtime are declared `.volatile()` — they surface on the
24
+ // auto-generated settings form, `ctx.settings.update(entryId, patch)` accepts
25
+ // exactly these paths, and at activation they resolve to `Volatile<T>`
26
+ // handles (unwrapped by `resolveVolatileConfig` in definition.ts / apply()).
27
+ enabled: Schema.boolean().default(true).volatile(),
28
+ accountId: Schema.string().default('main').volatile(),
29
+ baseUrl: Schema.string().default('https://api.telegram.org').volatile(),
30
+ // Credential reference name only — never the secret value itself.
31
+ tokenRef: Schema.string().default(TELEGRAM_BOT_TOKEN_REF),
32
+ // DEPRECATED migration-only legacy plaintext field: kept so old configs still
33
+ // parse. apply() migrates its value to credentials once and deletes it.
34
+ token: Schema.string().hidden(),
35
+ timeoutMs: Schema.natural().default(30000).volatile(),
36
+ longPollTimeoutMs: Schema.natural().default(25000).volatile(),
37
+ reconnect: Schema.object({
38
+ enabled: Schema.boolean().default(true),
39
+ baseDelayMs: Schema.natural().default(1000),
40
+ maxDelayMs: Schema.natural().default(30000),
41
+ maxRetries: Schema.natural().default(10),
42
+ }).default({}).volatile(),
43
+ dedup: Schema.object({
44
+ enabled: Schema.boolean().default(true),
45
+ windowMs: Schema.natural().default(5000),
46
+ }).default({}).volatile(),
47
+ streaming: Schema.object({
48
+ enabled: Schema.boolean().default(true),
49
+ placeholder: Schema.string().default('…'),
50
+ }).default({}).volatile(),
51
+ typing: Schema.object({
52
+ enabled: Schema.boolean().default(true),
53
+ refreshMs: Schema.natural().min(1000).default(4000),
54
+ }).default({}).volatile(),
55
+ formatting: Schema.object({
56
+ mode: Schema.union([
57
+ Schema.const('auto'),
58
+ Schema.const('rich-markdown'),
59
+ Schema.const('html'),
60
+ Schema.const('markdown-v2'),
61
+ Schema.const('plain'),
62
+ ]).default('auto'),
63
+ fallback: Schema.const('plain').default('plain'),
64
+ }).default({}).volatile(),
65
+ maxDownloadBytes: Schema.natural().default(20 * 1024 * 1024).volatile(),
66
+ });
67
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Telegram ChannelDefinition — the description of the telegram channel exposed
3
+ * to the Channel Control Plane.
4
+ *
5
+ * The control plane drives setup, configured-state reporting and adapter
6
+ * instantiation purely through this object, never through per-channel
7
+ * conditionals.
8
+ *
9
+ * - `setup.fields` — a single secret field (`token`) backed by the
10
+ * `TELEGRAM_BOT_TOKEN_REF` credential reference. The real
11
+ * token is only ever written through the credentials seam.
12
+ * - `getConfiguredState` — configured when the token credential exists.
13
+ * - `saveConfig` — merges only non-secret keys (behavioural tuning) into an
14
+ * internal mutable snapshot used by createAdapter. Secret
15
+ * fields are rejected upstream by the control plane.
16
+ * - `createAdapter` — resolves `tokenRef` via the injected credentials seam
17
+ * and throws a stable error when it is missing.
18
+ * - `setup.setupUrl` — points at @BotFather, where operators create a bot and
19
+ * obtain the Bot API token.
20
+ *
21
+ * Fully offline-testable: inject a fake credentials seam and a fake transport
22
+ * via `deps` — no network, no host.
23
+ */
24
+ import type { ChannelDefinition } from '@krischoichoi/channel-control';
25
+ import type { TelegramConfig } from './config.js';
26
+ import { type TelegramAdapterDeps } from './adapter.js';
27
+ /**
28
+ * Structural credential seam used by the definition. Mirrors the tiny slice of
29
+ * `ctx.credentials` the control plane needs; injected so the definition stays
30
+ * host-agnostic and offline-testable. Resolution is per call and must not be
31
+ * cached across calls.
32
+ */
33
+ export interface TelegramCredentialSeam {
34
+ resolve(ref: string): Promise<{
35
+ value: string;
36
+ source: string;
37
+ } | undefined>;
38
+ describe(ref: string): Promise<{
39
+ configured: boolean;
40
+ source?: string;
41
+ writable: boolean;
42
+ }>;
43
+ set(ref: string, value: string): Promise<void>;
44
+ }
45
+ export interface CreateTelegramDefinitionOptions {
46
+ config: TelegramConfig;
47
+ deps?: TelegramAdapterDeps;
48
+ /** Injected credentials seam (wraps ctx.credentials in apply()). */
49
+ credentials: TelegramCredentialSeam;
50
+ /** Durable store for the enabled intent (doc §21) when the host provides one. */
51
+ persistEnabled?: (enabled: boolean) => Promise<void>;
52
+ }
53
+ /**
54
+ * Build the telegram ChannelDefinition for the control plane. Returns a fresh
55
+ * object each call (cheap) so a plugin can register it against any control
56
+ * instance.
57
+ */
58
+ export declare function createTelegramDefinition(options: CreateTelegramDefinitionOptions): ChannelDefinition;
59
+ //# sourceMappingURL=definition.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.d.ts","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;GAsBG;AACH,OAAO,KAAK,EACV,iBAAiB,EAGlB,MAAM,+BAA+B,CAAC;AAEvC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAGlD,OAAO,EAAmB,KAAK,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEzE;;;;;GAKG;AACH,MAAM,WAAW,sBAAsB;IACrC,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,GAAG,SAAS,CAAC,CAAC;IAC7E,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC;QAAE,UAAU,EAAE,OAAO,CAAC;QAAC,MAAM,CAAC,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,OAAO,CAAA;KAAE,CAAC,CAAC;IAC5F,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,+BAA+B;IAC9C,MAAM,EAAE,cAAc,CAAC;IACvB,IAAI,CAAC,EAAE,mBAAmB,CAAC;IAC3B,oEAAoE;IACpE,WAAW,EAAE,sBAAsB,CAAC;IACpC,iFAAiF;IACjF,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,OAAO,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CACtD;AAsDD;;;;GAIG;AACH,wBAAgB,wBAAwB,CACtC,OAAO,EAAE,+BAA+B,GACvC,iBAAiB,CAyGnB"}
@@ -0,0 +1,158 @@
1
+ import { ControlError } from '@krischoichoi/channel-control';
2
+ import { resolveVolatileConfig } from '@krischoichoi/channel-core';
3
+ import { Config, TELEGRAM_BOT_TOKEN_REF } from './config.js';
4
+ import { TelegramAdapter } from './adapter.js';
5
+ /** Allowed non-secret nested sub-config keys merged by saveConfig. */
6
+ const NESTED_KEYS = ['reconnect', 'dedup', 'streaming', 'typing', 'formatting'];
7
+ /** Allowed non-secret top-level scalar keys merged by saveConfig. */
8
+ const SCALAR_KEYS = [
9
+ 'accountId',
10
+ 'baseUrl',
11
+ 'timeoutMs',
12
+ 'longPollTimeoutMs',
13
+ 'maxDownloadBytes',
14
+ ];
15
+ /** Deep-copy a TelegramConfig into an independent mutable snapshot. */
16
+ function snapshotOf(config) {
17
+ // Harness 0.2.0: volatile fields arrive as `Volatile<T>` handles; unwrap
18
+ // them once so the snapshot (and everything reading it) sees plain values.
19
+ const plain = resolveVolatileConfig(config);
20
+ return {
21
+ ...plain,
22
+ reconnect: { ...plain.reconnect },
23
+ dedup: { ...plain.dedup },
24
+ streaming: { ...plain.streaming },
25
+ typing: { ...plain.typing },
26
+ formatting: { ...plain.formatting },
27
+ };
28
+ }
29
+ function applyScalarPatch(snapshot, key, value) {
30
+ switch (key) {
31
+ case 'accountId':
32
+ if (typeof value === 'string' && value)
33
+ snapshot.accountId = value;
34
+ return;
35
+ case 'baseUrl':
36
+ if (typeof value === 'string' && value)
37
+ snapshot.baseUrl = value;
38
+ return;
39
+ case 'timeoutMs':
40
+ if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
41
+ snapshot.timeoutMs = Math.floor(value);
42
+ }
43
+ return;
44
+ case 'longPollTimeoutMs':
45
+ if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
46
+ snapshot.longPollTimeoutMs = Math.floor(value);
47
+ }
48
+ return;
49
+ case 'maxDownloadBytes':
50
+ if (typeof value === 'number' && Number.isFinite(value) && value > 0) {
51
+ snapshot.maxDownloadBytes = Math.floor(value);
52
+ }
53
+ return;
54
+ }
55
+ }
56
+ /**
57
+ * Build the telegram ChannelDefinition for the control plane. Returns a fresh
58
+ * object each call (cheap) so a plugin can register it against any control
59
+ * instance.
60
+ */
61
+ export function createTelegramDefinition(options) {
62
+ const credentials = options.credentials;
63
+ const deps = options.deps ?? {};
64
+ // Mutable snapshot: saveConfig merges non-secret patches into this; the same
65
+ // object feeds getConfiguredState + createAdapter so both always agree.
66
+ const state = snapshotOf(options.config);
67
+ const tokenRef = () => state.tokenRef || TELEGRAM_BOT_TOKEN_REF;
68
+ const setup = {
69
+ fields: [
70
+ {
71
+ name: 'token',
72
+ kind: 'secret',
73
+ secret: true,
74
+ configured: false,
75
+ writable: true,
76
+ ref: tokenRef(),
77
+ },
78
+ ],
79
+ authMethods: ['credentials'],
80
+ setupUrl: 'https://t.me/BotFather',
81
+ };
82
+ const configuredState = async () => {
83
+ const described = await credentials.describe(tokenRef());
84
+ return {
85
+ configured: described.configured,
86
+ fields: {
87
+ token: {
88
+ configured: described.configured,
89
+ writable: described.writable,
90
+ source: described.source,
91
+ },
92
+ },
93
+ };
94
+ };
95
+ const saveConfig = async (patch) => {
96
+ for (const key of NESTED_KEYS) {
97
+ const value = patch[key];
98
+ if (value && typeof value === 'object') {
99
+ Object.assign(state[key], value);
100
+ }
101
+ }
102
+ for (const key of SCALAR_KEYS) {
103
+ if (patch[key] !== undefined) {
104
+ applyScalarPatch(state, key, patch[key]);
105
+ }
106
+ }
107
+ if (patch.enabled !== undefined)
108
+ state.enabled = Boolean(patch.enabled);
109
+ // `token` / `tokenRef` are secret fields and are rejected by the control
110
+ // plane before reaching this definition; nothing to persist here.
111
+ };
112
+ const restoreConfig = async (saved) => {
113
+ // Schemastery validates and normalizes the persisted control-plane value.
114
+ const restored = snapshotOf(Config(saved));
115
+ Object.assign(state, restored);
116
+ state.reconnect = restored.reconnect;
117
+ state.dedup = restored.dedup;
118
+ state.streaming = restored.streaming;
119
+ state.typing = restored.typing;
120
+ state.formatting = restored.formatting;
121
+ };
122
+ const createAdapter = async () => {
123
+ const resolved = await credentials.resolve(tokenRef());
124
+ const token = resolved?.value;
125
+ if (!token) {
126
+ throw new ControlError('CONTROL_ERROR', `telegram credential "${tokenRef()}" is not configured`);
127
+ }
128
+ return new TelegramAdapter(state, { ...deps, token });
129
+ };
130
+ return {
131
+ id: 'telegram',
132
+ get enabled() {
133
+ return state.enabled;
134
+ },
135
+ async setEnabled(enabled) {
136
+ state.enabled = enabled;
137
+ await options.persistEnabled?.(enabled);
138
+ },
139
+ setup,
140
+ getConfiguredState: configuredState,
141
+ saveConfig,
142
+ snapshotConfig: () => snapshotOf(state),
143
+ restoreConfig,
144
+ createAdapter,
145
+ autoStart: true,
146
+ // Telegram entities are mapped to a reliable mentionedBot activation fact;
147
+ // new group rules default to requiring an explicit bot mention.
148
+ access: {
149
+ directMessages: true,
150
+ groups: true,
151
+ mentions: true,
152
+ ownerDiscovery: 'claim',
153
+ identityLabels: { user: 'Telegram User ID', group: 'Telegram Chat ID' },
154
+ defaults: { requireMention: true },
155
+ },
156
+ };
157
+ }
158
+ //# sourceMappingURL=definition.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"definition.js","sourceRoot":"","sources":["../src/definition.ts"],"names":[],"mappings":"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"}