@moxxy/plugin-telegram 0.27.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 (123) hide show
  1. package/LICENSE +21 -0
  2. package/dist/approval.d.ts +37 -0
  3. package/dist/approval.d.ts.map +1 -0
  4. package/dist/approval.js +75 -0
  5. package/dist/approval.js.map +1 -0
  6. package/dist/channel/approval-prompt.d.ts +20 -0
  7. package/dist/channel/approval-prompt.d.ts.map +1 -0
  8. package/dist/channel/approval-prompt.js +34 -0
  9. package/dist/channel/approval-prompt.js.map +1 -0
  10. package/dist/channel/callback-handler.d.ts +32 -0
  11. package/dist/channel/callback-handler.d.ts.map +1 -0
  12. package/dist/channel/callback-handler.js +212 -0
  13. package/dist/channel/callback-handler.js.map +1 -0
  14. package/dist/channel/frame-pump.d.ts +58 -0
  15. package/dist/channel/frame-pump.d.ts.map +1 -0
  16. package/dist/channel/frame-pump.js +159 -0
  17. package/dist/channel/frame-pump.js.map +1 -0
  18. package/dist/channel/html.d.ts +24 -0
  19. package/dist/channel/html.d.ts.map +1 -0
  20. package/dist/channel/html.js +62 -0
  21. package/dist/channel/html.js.map +1 -0
  22. package/dist/channel/pairing-handler.d.ts +66 -0
  23. package/dist/channel/pairing-handler.d.ts.map +1 -0
  24. package/dist/channel/pairing-handler.js +147 -0
  25. package/dist/channel/pairing-handler.js.map +1 -0
  26. package/dist/channel/permission-prompt.d.ts +21 -0
  27. package/dist/channel/permission-prompt.d.ts.map +1 -0
  28. package/dist/channel/permission-prompt.js +36 -0
  29. package/dist/channel/permission-prompt.js.map +1 -0
  30. package/dist/channel/slash-handler.d.ts +47 -0
  31. package/dist/channel/slash-handler.d.ts.map +1 -0
  32. package/dist/channel/slash-handler.js +214 -0
  33. package/dist/channel/slash-handler.js.map +1 -0
  34. package/dist/channel/text-handler.d.ts +43 -0
  35. package/dist/channel/text-handler.d.ts.map +1 -0
  36. package/dist/channel/text-handler.js +116 -0
  37. package/dist/channel/text-handler.js.map +1 -0
  38. package/dist/channel/turn-runner.d.ts +40 -0
  39. package/dist/channel/turn-runner.d.ts.map +1 -0
  40. package/dist/channel/turn-runner.js +67 -0
  41. package/dist/channel/turn-runner.js.map +1 -0
  42. package/dist/channel/typing-indicator.d.ts +14 -0
  43. package/dist/channel/typing-indicator.d.ts.map +1 -0
  44. package/dist/channel/typing-indicator.js +29 -0
  45. package/dist/channel/typing-indicator.js.map +1 -0
  46. package/dist/channel/voice-handler.d.ts +42 -0
  47. package/dist/channel/voice-handler.d.ts.map +1 -0
  48. package/dist/channel/voice-handler.js +138 -0
  49. package/dist/channel/voice-handler.js.map +1 -0
  50. package/dist/channel.d.ts +118 -0
  51. package/dist/channel.d.ts.map +1 -0
  52. package/dist/channel.js +478 -0
  53. package/dist/channel.js.map +1 -0
  54. package/dist/channel.test-d.d.ts +2 -0
  55. package/dist/channel.test-d.d.ts.map +1 -0
  56. package/dist/channel.test-d.js +5 -0
  57. package/dist/channel.test-d.js.map +1 -0
  58. package/dist/format.d.ts +38 -0
  59. package/dist/format.d.ts.map +1 -0
  60. package/dist/format.js +237 -0
  61. package/dist/format.js.map +1 -0
  62. package/dist/index.d.ts +24 -0
  63. package/dist/index.d.ts.map +1 -0
  64. package/dist/index.js +304 -0
  65. package/dist/index.js.map +1 -0
  66. package/dist/keys.d.ts +37 -0
  67. package/dist/keys.d.ts.map +1 -0
  68. package/dist/keys.js +42 -0
  69. package/dist/keys.js.map +1 -0
  70. package/dist/pair-flow.d.ts +18 -0
  71. package/dist/pair-flow.d.ts.map +1 -0
  72. package/dist/pair-flow.js +119 -0
  73. package/dist/pair-flow.js.map +1 -0
  74. package/dist/pairing.d.ts +90 -0
  75. package/dist/pairing.d.ts.map +1 -0
  76. package/dist/pairing.js +97 -0
  77. package/dist/pairing.js.map +1 -0
  78. package/dist/permission.d.ts +25 -0
  79. package/dist/permission.d.ts.map +1 -0
  80. package/dist/permission.js +49 -0
  81. package/dist/permission.js.map +1 -0
  82. package/dist/render.d.ts +68 -0
  83. package/dist/render.d.ts.map +1 -0
  84. package/dist/render.js +439 -0
  85. package/dist/render.js.map +1 -0
  86. package/dist/setup-wizard.d.ts +21 -0
  87. package/dist/setup-wizard.d.ts.map +1 -0
  88. package/dist/setup-wizard.js +140 -0
  89. package/dist/setup-wizard.js.map +1 -0
  90. package/package.json +91 -0
  91. package/src/approval.test.ts +103 -0
  92. package/src/approval.ts +86 -0
  93. package/src/channel/approval-prompt.ts +46 -0
  94. package/src/channel/callback-handler.test.ts +188 -0
  95. package/src/channel/callback-handler.ts +256 -0
  96. package/src/channel/frame-pump.ts +176 -0
  97. package/src/channel/html.test.ts +40 -0
  98. package/src/channel/html.ts +60 -0
  99. package/src/channel/pairing-handler.ts +181 -0
  100. package/src/channel/permission-prompt.ts +51 -0
  101. package/src/channel/slash-handler.ts +259 -0
  102. package/src/channel/text-handler.test.ts +222 -0
  103. package/src/channel/text-handler.ts +174 -0
  104. package/src/channel/turn-runner.ts +102 -0
  105. package/src/channel/typing-indicator.ts +31 -0
  106. package/src/channel/voice-handler.test.ts +328 -0
  107. package/src/channel/voice-handler.ts +198 -0
  108. package/src/channel/voice-reply.test.ts +223 -0
  109. package/src/channel.test-d.ts +7 -0
  110. package/src/channel.ts +564 -0
  111. package/src/format.test.ts +159 -0
  112. package/src/format.ts +257 -0
  113. package/src/index.ts +347 -0
  114. package/src/keys.ts +52 -0
  115. package/src/pair-flow.ts +131 -0
  116. package/src/pairing.test.ts +113 -0
  117. package/src/pairing.ts +184 -0
  118. package/src/permission.test.ts +56 -0
  119. package/src/permission.ts +61 -0
  120. package/src/render.test.ts +350 -0
  121. package/src/render.ts +522 -0
  122. package/src/setup-wizard.ts +178 -0
  123. package/src/subcommands.test.ts +153 -0
package/dist/render.js ADDED
@@ -0,0 +1,439 @@
1
+ import { fileDiffSummary, fileDiffVerb, isFileDiffDisplay, } from '@moxxy/sdk';
2
+ const TELEGRAM_MESSAGE_LIMIT = 4000; // 4096 minus a safety margin
3
+ /**
4
+ * Minimum activity-block line count before the final frame folds it into an
5
+ * expandable box. Short traces (a call or two) stay inline; busy turns collapse
6
+ * so the answer isn't buried under the tool log.
7
+ */
8
+ const COLLAPSE_ACTIVITY_LINES = 4;
9
+ export class TurnRenderer {
10
+ chunks = [];
11
+ /** In-flight + completed tool calls keyed by callId, in arrival order. */
12
+ tools = new Map();
13
+ skillBanner = null;
14
+ notices = [];
15
+ finalAssistant = null;
16
+ errorLine = null;
17
+ /** Pre-formatted HTML diff blocks, one per file-diff tool result, in arrival order. */
18
+ diffBlocks = [];
19
+ lastFrame = '';
20
+ accept(event) {
21
+ switch (event.type) {
22
+ case 'assistant_chunk':
23
+ this.chunks.push(event.delta);
24
+ break;
25
+ case 'assistant_message':
26
+ this.finalAssistant = event.content;
27
+ this.chunks = [];
28
+ break;
29
+ case 'tool_call_requested': {
30
+ const callId = String(event.callId);
31
+ this.tools.set(callId, {
32
+ id: callId,
33
+ name: event.name,
34
+ inputPreview: previewArgs(event.input),
35
+ status: { kind: 'pending', startedAt: Date.now() },
36
+ });
37
+ break;
38
+ }
39
+ case 'tool_call_denied': {
40
+ const entry = this.tools.get(String(event.callId));
41
+ if (entry)
42
+ entry.status = { kind: 'denied', reason: event.reason };
43
+ break;
44
+ }
45
+ case 'tool_result': {
46
+ const entry = this.tools.get(String(event.callId));
47
+ if (!entry)
48
+ break;
49
+ const started = entry.status.kind === 'pending' ? entry.status.startedAt : Date.now();
50
+ const ms = Date.now() - started;
51
+ if (event.ok) {
52
+ entry.status = { kind: 'ok', ms };
53
+ // Write/Edit tools return { forModel, display: FileDiffDisplay }.
54
+ // Render the structured diff as a fenced ```diff block so
55
+ // Telegram highlights +/- lines, plus a summary line.
56
+ const display = event.output?.display;
57
+ if (isFileDiffDisplay(display)) {
58
+ this.diffBlocks.push(renderFileDiff(display));
59
+ }
60
+ }
61
+ else {
62
+ entry.status = {
63
+ kind: 'err',
64
+ ms,
65
+ message: `${event.error?.kind ?? 'error'}: ${event.error?.message ?? ''}`,
66
+ };
67
+ }
68
+ break;
69
+ }
70
+ case 'skill_invoked':
71
+ // skill_invoked fires when a skill scope opens; its tool calls
72
+ // arrive immediately after as tool_call_requested. Show a
73
+ // banner so the user knows the bot is now in a skill scope.
74
+ this.skillBanner = { name: event.name, toolCount: 0 };
75
+ break;
76
+ case 'skill_created':
77
+ this.notices.push(`💡 created skill: <b>${escape(event.name)}</b>`);
78
+ break;
79
+ case 'plugin_event':
80
+ if (event.pluginId === '@moxxy/subagents' && event.subtype === 'subagent_started') {
81
+ const payload = (event.payload ?? {});
82
+ const label = typeof payload.label === 'string' ? payload.label : 'agent';
83
+ this.notices.push(`🤖 spawned agent <b>${escape(label)}</b>`);
84
+ }
85
+ break;
86
+ case 'error':
87
+ this.errorLine = `❗ <b>${escape(event.kind)}</b>: ${escape(event.message)}`;
88
+ break;
89
+ default:
90
+ break;
91
+ }
92
+ const frame = this.snapshot();
93
+ const key = `${frame.activityHtml}\u0000${frame.body}\u0000${frame.diffHtml}\u0000${frame.errorHtml}`;
94
+ const hasUpdate = key !== this.lastFrame;
95
+ this.lastFrame = key;
96
+ return { ...frame, hasUpdate };
97
+ }
98
+ /**
99
+ * Compose the current frame. `collapse` (set on the final flush) folds a
100
+ * long tool-activity trace into an expandable box so the finished message
101
+ * leads with the answer; during streaming it's left false so the activity
102
+ * stays fully visible while work is in flight.
103
+ */
104
+ snapshot(opts = {}) {
105
+ const activityHtml = this.renderActivity(opts.collapse ?? false) ?? '';
106
+ const body = this.finalAssistant ?? this.chunks.join('');
107
+ const diffHtml = this.diffBlocks.join('\n\n');
108
+ const errorHtml = this.errorLine ?? '';
109
+ return { activityHtml, body, diffHtml, errorHtml, hasUpdate: false };
110
+ }
111
+ /**
112
+ * Format the tool/skill activity block. Returns null when there's
113
+ * nothing to show — keeps frames lean for chat-only turns.
114
+ *
115
+ * The block is rendered with HTML tags (`<b>`, `<code>`, `<i>`) so
116
+ * it survives `markdownToTelegramHtml` intact (the function escapes
117
+ * `<` `>` `&` in text segments but leaves existing tags alone via
118
+ * the fence/inline placeholder dance). Use `__inert__` markers so
119
+ * already-formed HTML doesn't get re-escaped.
120
+ *
121
+ * When `collapse` is set (final frame) and the trace ran long, fold it
122
+ * into a `<blockquote expandable>` topped by a one-line summary — the
123
+ * tool noise becomes a tap-to-open box and the assistant's answer leads.
124
+ */
125
+ renderActivity(collapse) {
126
+ const lines = [];
127
+ if (this.skillBanner) {
128
+ lines.push(`💡 <b>${escape(this.skillBanner.name)}</b>`);
129
+ }
130
+ // Show last N tool entries to keep the message compact when a turn
131
+ // makes many calls. Most-recent at the bottom (matches chronology).
132
+ const tools = [...this.tools.values()];
133
+ const visible = tools.slice(-10);
134
+ const hidden = tools.length - visible.length;
135
+ if (hidden > 0) {
136
+ lines.push(`<i>… ${hidden} earlier tool call${hidden === 1 ? '' : 's'} hidden</i>`);
137
+ }
138
+ for (const t of visible) {
139
+ lines.push(`• <code>${escape(t.name)}</code>(${escape(t.inputPreview)}) ${statusBadge(t.status)}`);
140
+ }
141
+ for (const n of this.notices.slice(-5))
142
+ lines.push(n);
143
+ if (lines.length === 0)
144
+ return null;
145
+ if (collapse && lines.length >= COLLAPSE_ACTIVITY_LINES) {
146
+ const summary = activitySummary(tools.length);
147
+ const inner = summary ? `${summary}\n${lines.join('\n')}` : lines.join('\n');
148
+ return `<blockquote expandable>${inner}</blockquote>`;
149
+ }
150
+ return `<blockquote>${lines.join('\n')}</blockquote>`;
151
+ }
152
+ reset() {
153
+ this.chunks = [];
154
+ this.tools.clear();
155
+ this.skillBanner = null;
156
+ this.notices = [];
157
+ this.finalAssistant = null;
158
+ this.errorLine = null;
159
+ this.diffBlocks = [];
160
+ this.lastFrame = '';
161
+ }
162
+ }
163
+ /**
164
+ * One-line summary that heads a collapsed activity box, so the box reads as
165
+ * "🔧 8 steps ▸" before it's opened. Empty when the turn made no tool calls
166
+ * (a skill-only / notice-only trace already names itself in its first line).
167
+ */
168
+ function activitySummary(toolCount) {
169
+ if (toolCount <= 0)
170
+ return '';
171
+ return `🔧 <b>${toolCount} step${toolCount === 1 ? '' : 's'}</b>`;
172
+ }
173
+ function statusBadge(status) {
174
+ // Pair the glyph with a text word ('done'/'error') so the pass/fail
175
+ // distinction survives screen-reader announcement / glyph-stripping —
176
+ // the ✓/✗ alone are read inconsistently (or as verbose emoji names).
177
+ switch (status.kind) {
178
+ case 'pending':
179
+ return '<i>running…</i>';
180
+ case 'ok':
181
+ return `✓ done <i>${fmtElapsed(status.ms)}</i>`;
182
+ case 'err':
183
+ return `✗ error: <i>${escape(status.message).slice(0, 80)}</i>`;
184
+ case 'denied':
185
+ return `✗ <i>denied: ${escape(status.reason).slice(0, 80)}</i>`;
186
+ }
187
+ }
188
+ function fmtElapsed(ms) {
189
+ if (ms < 1000)
190
+ return `${ms}ms`;
191
+ if (ms < 60_000)
192
+ return `${(ms / 1000).toFixed(1)}s`;
193
+ return `${Math.floor(ms / 60_000)}m ${Math.floor((ms % 60_000) / 1000)}s`;
194
+ }
195
+ function escape(s) {
196
+ return s.replace(/[&<>]/g, (c) => (c === '&' ? '&amp;' : c === '<' ? '&lt;' : '&gt;'));
197
+ }
198
+ /**
199
+ * Render one file-diff tool result as a Telegram HTML block: a bold
200
+ * summary line followed by a fenced `diff`-language code block. Telegram
201
+ * highlights `+`/`-` prefixed lines for the `diff` language, which is the
202
+ * closest it can get to red/green backgrounds.
203
+ *
204
+ * The fence body is HTML-escaped exactly like `format.ts` does for
205
+ * ```fenced blocks (only `< > &` matter inside `<pre><code>`), so the
206
+ * diff text survives parse_mode=HTML intact. When `hunks` is empty (a
207
+ * huge diff that got truncated to just counts), we emit only the summary
208
+ * line — no empty code block.
209
+ */
210
+ function renderFileDiff(d) {
211
+ const header = `<b>${escape(fileDiffVerb(d))} ${escape(d.path)}</b> — ${escape(fileDiffSummary(d))}`;
212
+ const diffText = unifiedDiffText(d);
213
+ if (!diffText)
214
+ return header;
215
+ return (header +
216
+ '\n' +
217
+ `<pre><code class="language-diff">${escape(diffText)}</code></pre>`);
218
+ }
219
+ /**
220
+ * Build a classic unified-diff body from a file-diff display: a
221
+ * `@@ -oldStart,oldLines +newStart,newLines @@` header per hunk, then each
222
+ * line prefixed with ` `/`+`/`-`. Hunks are separated by a blank line.
223
+ * Returns '' when there are no hunks.
224
+ */
225
+ function unifiedDiffText(d) {
226
+ const blocks = [];
227
+ for (const hunk of d.hunks) {
228
+ const lines = [
229
+ `@@ -${hunk.oldStart},${hunk.oldLines} +${hunk.newStart},${hunk.newLines} @@`,
230
+ ];
231
+ for (const line of hunk.lines) {
232
+ const marker = line.kind === 'add' ? '+' : line.kind === 'del' ? '-' : ' ';
233
+ lines.push(`${marker}${line.text}`);
234
+ }
235
+ blocks.push(lines.join('\n'));
236
+ }
237
+ return blocks.join('\n\n');
238
+ }
239
+ function truncJson(value, max = 60) {
240
+ const s = typeof value === 'string' ? value : JSON.stringify(value) ?? '';
241
+ if (s.length <= max)
242
+ return s;
243
+ return s.slice(0, max - 1) + '…';
244
+ }
245
+ function previewArgs(input) {
246
+ if (input == null || (typeof input === 'object' && Object.keys(input).length === 0)) {
247
+ return '';
248
+ }
249
+ if (typeof input === 'string')
250
+ return truncJson(input);
251
+ try {
252
+ const obj = input;
253
+ const keys = Object.keys(obj).slice(0, 3);
254
+ const pairs = keys.map((k) => {
255
+ const v = obj[k];
256
+ return `${k}=${truncJson(v, 20)}`;
257
+ });
258
+ const tail = Object.keys(obj).length > keys.length ? ', …' : '';
259
+ return pairs.join(', ') + tail;
260
+ }
261
+ catch {
262
+ return truncJson(input);
263
+ }
264
+ }
265
+ /**
266
+ * Precomputed cut-boundary index for one `html` part. Replaces the old
267
+ * `tagStackAt(html, idx)` which re-scanned from offset 0 on every probe — that
268
+ * made `splitForTelegram` O(n²) per part because it calls it inside a
269
+ * walk-back/walk-forward loop. This scans the part exactly once, recording for
270
+ * every index whether it is inside a `<...>` tag or `&...;` entity and the
271
+ * open-tag stack as of the last completed tag, so `at(idx)` is O(stackDepth).
272
+ */
273
+ class TagBoundaryIndex {
274
+ // unsafe[i] === 1 when index i falls inside a tag or entity (unsafe to cut).
275
+ unsafe;
276
+ // Checkpoints of the open-tag stack, one per completed tag, ordered by `pos`
277
+ // (the offset just AFTER the tag's `>`). stackAt(idx) = the latest checkpoint
278
+ // whose pos <= idx. The stack only changes at a tag close-bracket, so this is
279
+ // exact between checkpoints.
280
+ checkPos = [0];
281
+ checkStack;
282
+ constructor(html, seed) {
283
+ const n = html.length;
284
+ this.unsafe = new Uint8Array(n);
285
+ const stack = seed.slice();
286
+ this.checkStack = [stack.slice()];
287
+ let i = 0;
288
+ while (i < n) {
289
+ const ch = html[i];
290
+ if (ch === '<') {
291
+ const end = html.indexOf('>', i);
292
+ if (end === -1) {
293
+ // Unterminated tag straddling the end — every offset AFTER the `<` is
294
+ // inside it (cutting before the `<`, at i, is still safe). Mark + stop.
295
+ for (let k = i + 1; k < n; k++)
296
+ this.unsafe[k] = 1;
297
+ break;
298
+ }
299
+ // Offsets strictly inside `<...>` (i.e. i+1..end) are unsafe; the `<`
300
+ // itself at i is a safe boundary (cutting before the tag is fine).
301
+ for (let k = i + 1; k <= end; k++)
302
+ this.unsafe[k] = 1;
303
+ const raw = html.slice(i, end + 1);
304
+ // Allow a hyphen in the tag name so Telegram's own elements
305
+ // (`<tg-spoiler>`, `<tg-emoji>`) are recognised — otherwise the name
306
+ // would truncate at `tg` and the reopened/closed tag across a split
307
+ // would be the bogus `</tg>`.
308
+ const m = /^<\s*(\/?)\s*([a-zA-Z][a-zA-Z0-9-]*)/.exec(raw);
309
+ if (m) {
310
+ const closing = m[1] === '/';
311
+ const name = m[2].toLowerCase();
312
+ if (closing) {
313
+ for (let s = stack.length - 1; s >= 0; s--) {
314
+ if (stack[s].name === name) {
315
+ stack.splice(s, 1);
316
+ break;
317
+ }
318
+ }
319
+ }
320
+ else if (!raw.endsWith('/>')) {
321
+ stack.push({ name, open: raw });
322
+ }
323
+ }
324
+ // Checkpoint the new stack at the offset just after the tag.
325
+ this.checkPos.push(end + 1);
326
+ this.checkStack.push(stack.slice());
327
+ i = end + 1;
328
+ continue;
329
+ }
330
+ if (ch === '&') {
331
+ const semi = html.indexOf(';', i);
332
+ if (semi !== -1 && semi - i <= 12 && /^&[a-zA-Z0-9#]+;$/.test(html.slice(i, semi + 1))) {
333
+ // Offsets strictly inside `&...;` (i+1..semi) are unsafe; cutting
334
+ // before the `&` is fine.
335
+ for (let k = i + 1; k <= semi; k++)
336
+ this.unsafe[k] = 1;
337
+ i = semi + 1;
338
+ continue;
339
+ }
340
+ }
341
+ i += 1;
342
+ }
343
+ }
344
+ /** Boundary info at `idx`: the open-tag stack there + whether it's unsafe. */
345
+ at(idx) {
346
+ const inside = idx > 0 && idx < this.unsafe.length && this.unsafe[idx] === 1;
347
+ // The stack only changes at completed tags; find the latest checkpoint at or
348
+ // before idx via binary search over the (sorted) checkpoint positions.
349
+ let lo = 0;
350
+ let hi = this.checkPos.length - 1;
351
+ while (lo < hi) {
352
+ const mid = (lo + hi + 1) >> 1;
353
+ if (this.checkPos[mid] <= idx)
354
+ lo = mid;
355
+ else
356
+ hi = mid - 1;
357
+ }
358
+ // Returns the checkpoint stack by reference — it is an immutable snapshot;
359
+ // the only consumer that re-roots it (`new TagBoundaryIndex(.., carry)`)
360
+ // copies via `seed.slice()` before mutating, so no probe can corrupt it.
361
+ return {
362
+ stack: this.checkStack[lo],
363
+ insideTag: inside,
364
+ };
365
+ }
366
+ }
367
+ /**
368
+ * Split composed Telegram HTML into <=`limit`-char parts that are each
369
+ * independently valid `parse_mode=HTML`. Cuts are chosen so they never land
370
+ * inside a `<...>` tag or an `&...;` entity; if a block-level tag (e.g. a
371
+ * `<pre><code class="language-diff">` fence or a `<blockquote>`) is open at the
372
+ * boundary it is closed in the head part and reopened in the tail. For plain
373
+ * text with no open tags this reduces to the old newline-preferring behaviour
374
+ * and `parts.join('')` reconstructs the input exactly.
375
+ */
376
+ export function splitForTelegram(text, limit = TELEGRAM_MESSAGE_LIMIT) {
377
+ if (text.length <= limit)
378
+ return text ? [text] : [];
379
+ const out = [];
380
+ // Tags carried over from the previous part's tail (reopened at its head).
381
+ let carry = [];
382
+ let remaining = text;
383
+ while (remaining.length > limit) {
384
+ // The reopened tags from the previous cut eat into this part's budget.
385
+ const carryLen = carry.reduce((n, t) => n + t.open.length, 0);
386
+ const budget = limit - carryLen;
387
+ // Index this part's tag/entity boundaries ONCE; every probe below is then an
388
+ // O(stackDepth) lookup instead of an O(idx) re-scan from offset 0.
389
+ const boundaries = new TagBoundaryIndex(remaining, carry);
390
+ // Closing tags appended to the head also consume budget; reserve a margin
391
+ // and search downward for a boundary that's safe (outside tag/entity).
392
+ let idx = Math.min(budget, remaining.length);
393
+ // Prefer a newline at/under the budget so we cut on a natural boundary.
394
+ const nl = remaining.lastIndexOf('\n', idx);
395
+ if (nl > 0)
396
+ idx = nl;
397
+ let info = boundaries.at(idx);
398
+ // If the chosen boundary is inside a tag or entity, walk back to a safe one.
399
+ while (info.insideTag && idx > 1) {
400
+ idx -= 1;
401
+ info = boundaries.at(idx);
402
+ }
403
+ // No safe boundary at/below budget (e.g. budget smaller than a single tag):
404
+ // never cut mid-tag — scan FORWARD to the next safe point even if it pushes
405
+ // this part over the soft cap. Validity beats the size margin.
406
+ if (info.insideTag) {
407
+ idx = Math.min(budget, remaining.length);
408
+ while (idx < remaining.length) {
409
+ const probe = boundaries.at(idx);
410
+ if (!probe.insideTag) {
411
+ info = probe;
412
+ break;
413
+ }
414
+ idx += 1;
415
+ }
416
+ if (idx >= remaining.length)
417
+ info = boundaries.at(remaining.length);
418
+ }
419
+ // Guard against a zero-width cut (would loop forever): fall back to the
420
+ // remaining length so the loop terminates.
421
+ if (idx <= 0) {
422
+ idx = remaining.length;
423
+ info = boundaries.at(idx);
424
+ }
425
+ const closeTags = info.stack
426
+ .slice()
427
+ .reverse()
428
+ .map((t) => `</${t.name}>`)
429
+ .join('');
430
+ const head = carry.map((t) => t.open).join('') + remaining.slice(0, idx) + closeTags;
431
+ out.push(head);
432
+ carry = info.stack;
433
+ remaining = remaining.slice(idx);
434
+ }
435
+ if (remaining)
436
+ out.push(carry.map((t) => t.open).join('') + remaining);
437
+ return out;
438
+ }
439
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../src/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { ChannelSubcommandContext } from '@moxxy/sdk';
2
+ /**
3
+ * Interactive Telegram setup menu.
4
+ *
5
+ * Invoked as the channel's `interactiveCommand` when the user runs
6
+ * `moxxy telegram` (or `moxxy channels telegram`) with no subcommand in a TTY.
7
+ * Headless invocations (or when a runner is already up) bypass it and start the
8
+ * bot directly, preserving the cron / systemd usage path.
9
+ *
10
+ * Menu offers actions appropriate to the current state:
11
+ * - no token -> "Set bot token" + "Quit"
12
+ * - token, not paired -> "Pair this terminal" + "Change token" + "Quit"
13
+ * - token + paired -> "Start bot" + "Unpair" + "Change token" + "Quit"
14
+ *
15
+ * Pairing is driven by the wizard end-to-end via the host-issued QR flow: the
16
+ * wizard opens a pair window, prints a `t.me/<bot>?start=<code>` QR, and the user
17
+ * scans it (or opens the link) and taps START to pair — the same mechanism the
18
+ * desktop Channels panel uses.
19
+ */
20
+ export declare function runTelegramWizard(ctx: ChannelSubcommandContext): Promise<number>;
21
+ //# sourceMappingURL=setup-wizard.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-wizard.d.ts","sourceRoot":"","sources":["../src/setup-wizard.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAyB3D;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAsB,iBAAiB,CAAC,GAAG,EAAE,wBAAwB,GAAG,OAAO,CAAC,MAAM,CAAC,CAoCtF"}
@@ -0,0 +1,140 @@
1
+ import { cancel, intro, isCancel, log, note, outro, password, select, } from '@clack/prompts';
2
+ import { TELEGRAM_AUTHORIZED_CHAT_KEY, TELEGRAM_TOKEN_KEY, TELEGRAM_TOKEN_RE, parseChatId, } from './keys.js';
3
+ import { runPairFlow } from './pair-flow.js';
4
+ // Tiny zero-dep ANSI helpers (bold + dim) so this wizard stays inside the
5
+ // plugin without depending on the CLI's colors module.
6
+ const ANSI = process.stdout.isTTY && !process.env.NO_COLOR;
7
+ const bold = (s) => (ANSI ? `\x1b[1m${s}\x1b[22m` : s);
8
+ const dim = (s) => (ANSI ? `\x1b[2m${s}\x1b[22m` : s);
9
+ /**
10
+ * Interactive Telegram setup menu.
11
+ *
12
+ * Invoked as the channel's `interactiveCommand` when the user runs
13
+ * `moxxy telegram` (or `moxxy channels telegram`) with no subcommand in a TTY.
14
+ * Headless invocations (or when a runner is already up) bypass it and start the
15
+ * bot directly, preserving the cron / systemd usage path.
16
+ *
17
+ * Menu offers actions appropriate to the current state:
18
+ * - no token -> "Set bot token" + "Quit"
19
+ * - token, not paired -> "Pair this terminal" + "Change token" + "Quit"
20
+ * - token + paired -> "Start bot" + "Unpair" + "Change token" + "Quit"
21
+ *
22
+ * Pairing is driven by the wizard end-to-end via the host-issued QR flow: the
23
+ * wizard opens a pair window, prints a `t.me/<bot>?start=<code>` QR, and the user
24
+ * scans it (or opens the link) and taps START to pair — the same mechanism the
25
+ * desktop Channels panel uses.
26
+ */
27
+ export async function runTelegramWizard(ctx) {
28
+ const vault = ctx.deps.vault;
29
+ intro(bold('moxxy telegram setup'));
30
+ while (true) {
31
+ const state = await readState(vault);
32
+ printStatus(state);
33
+ const action = await pickAction(state);
34
+ if (action === null) {
35
+ cancel('cancelled.');
36
+ return 0;
37
+ }
38
+ if (action === 'quit') {
39
+ outro(dim('done.'));
40
+ return 0;
41
+ }
42
+ if (action === 'set-token') {
43
+ await actionSetToken(vault);
44
+ continue;
45
+ }
46
+ if (action === 'pair') {
47
+ return await runPairFlow(ctx);
48
+ }
49
+ if (action === 'unpair') {
50
+ await vault.delete(TELEGRAM_AUTHORIZED_CHAT_KEY);
51
+ log.success('Unpaired. The next /start from any chat will begin a fresh pairing window.');
52
+ continue;
53
+ }
54
+ if (action === 'start') {
55
+ log.info('Starting the bot. Press Ctrl+C to stop.');
56
+ outro(dim('handing off to bot...'));
57
+ return ctx.startChannel();
58
+ }
59
+ }
60
+ }
61
+ async function readState(vault) {
62
+ // env beats vault for token (matches the channel's own isAvailable
63
+ // precedence) so the wizard reflects what the bot would actually see
64
+ // at start time.
65
+ const envToken = process.env.MOXXY_TELEGRAM_TOKEN;
66
+ const vaultToken = envToken ?? (await vault.get(TELEGRAM_TOKEN_KEY));
67
+ const authorized = await vault.get(TELEGRAM_AUTHORIZED_CHAT_KEY);
68
+ return {
69
+ hasToken: !!vaultToken,
70
+ tokenPreview: vaultToken ? maskToken(vaultToken) : null,
71
+ authorizedChatId: parseChatId(authorized),
72
+ };
73
+ }
74
+ function maskToken(token) {
75
+ const id = token.split(':')[0] ?? '';
76
+ return id.length > 4 ? `${id.slice(0, 3)}...${id.slice(-3)}` : id;
77
+ }
78
+ function printStatus(state) {
79
+ const lines = [];
80
+ lines.push(`Token ${state.hasToken ? bold(state.tokenPreview ?? 'set') : dim('not set')}`);
81
+ lines.push(`Paired chat ${state.authorizedChatId != null ? bold(String(state.authorizedChatId)) : dim('none')}`);
82
+ note(lines.join('\n'), 'status');
83
+ }
84
+ async function pickAction(state) {
85
+ const options = [];
86
+ if (state.hasToken && state.authorizedChatId != null) {
87
+ options.push({
88
+ value: 'start',
89
+ label: 'Start the bot',
90
+ hint: 'runs forever - Ctrl+C to stop',
91
+ });
92
+ options.push({
93
+ value: 'unpair',
94
+ label: 'Unpair this chat',
95
+ hint: 'next /start begins a fresh pairing window',
96
+ });
97
+ }
98
+ else if (state.hasToken) {
99
+ options.push({
100
+ value: 'pair',
101
+ label: 'Pair a Telegram chat',
102
+ hint: 'scan a QR (or open the link) and tap START in Telegram',
103
+ });
104
+ }
105
+ options.push({
106
+ value: 'set-token',
107
+ label: state.hasToken ? 'Change the bot token' : 'Set the bot token',
108
+ hint: state.hasToken ? undefined : 'get one from @BotFather on Telegram',
109
+ });
110
+ options.push({ value: 'quit', label: 'Quit' });
111
+ const choice = await select({ message: 'What do you want to do?', options });
112
+ if (isCancel(choice))
113
+ return null;
114
+ return choice;
115
+ }
116
+ async function actionSetToken(vault) {
117
+ note('Open https://t.me/BotFather, run /newbot (or /token for an existing bot), copy the\n' +
118
+ 'token it returns (looks like 1234567890:ABCdef...), and paste it below. It goes\n' +
119
+ "straight into the moxxy vault under '" +
120
+ TELEGRAM_TOKEN_KEY +
121
+ "' - no env var needed.", 'get a bot token');
122
+ const token = await password({
123
+ message: 'Paste the Telegram bot token',
124
+ mask: '•',
125
+ validate: (v) => {
126
+ if (!v || v.trim().length === 0)
127
+ return 'required';
128
+ if (!TELEGRAM_TOKEN_RE.test(v.trim())) {
129
+ return 'doesn\'t look like a Telegram token - expected "<digits>:<22+ url-safe chars>"';
130
+ }
131
+ return undefined;
132
+ },
133
+ });
134
+ if (isCancel(token))
135
+ return false;
136
+ await vault.set(TELEGRAM_TOKEN_KEY, String(token).trim(), ['telegram']);
137
+ log.success('Token stored in vault.');
138
+ return true;
139
+ }
140
+ //# sourceMappingURL=setup-wizard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"setup-wizard.js","sourceRoot":"","sources":["../src/setup-wizard.ts"],"names":[],"mappings":"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"}