stitchkit 0.96.0 → 0.96.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/entrypoints/telegram.js +15 -2
- package/dist/telegram/operator-channel.d.ts +6 -0
- package/dist/telegram/operator-channel.d.ts.map +1 -1
- package/llms/telegram.txt +5 -2
- package/llms-full.txt +5 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,18 @@ additive**; the first breaking change landed in 0.10.0. Grep the file for
|
|
|
15
15
|
|
|
16
16
|
## [Unreleased]
|
|
17
17
|
|
|
18
|
+
## [0.96.1] — 2026-09-24
|
|
19
|
+
|
|
20
|
+
### Fixed
|
|
21
|
+
|
|
22
|
+
- `stitchkit/telegram` — **the operator channel no longer posts a bot token.**
|
|
23
|
+
Bots post their errors to the operators' chat, and an error about a local
|
|
24
|
+
Bot API file carries `<root>/<token>/…` in its path; the journal masked it,
|
|
25
|
+
the chat did not. `createTelegramOperatorChannel` now masks a bot token in
|
|
26
|
+
every message before it is queued — so a dropped message is reported masked
|
|
27
|
+
too — plus any `sensitivePatterns` given, and `telegramOperatorSender` masks
|
|
28
|
+
its own exact token whatever its shape.
|
|
29
|
+
|
|
18
30
|
## [0.96.0] — 2026-09-24
|
|
19
31
|
|
|
20
32
|
### ⚠️ Breaking changes
|
|
@@ -636,6 +636,17 @@ function createTelegramLocalFiles(config) {
|
|
|
636
636
|
}
|
|
637
637
|
// src/telegram/operator-channel.ts
|
|
638
638
|
var TEXT_LIMIT = 4096;
|
|
639
|
+
var MASK = "[redacted]";
|
|
640
|
+
function masked(text, patterns) {
|
|
641
|
+
let output = text;
|
|
642
|
+
for (const pattern of patterns) {
|
|
643
|
+
output = output.replace(new RegExp(pattern.source, `${pattern.flags.replace(/[gy]/g, "")}g`), MASK);
|
|
644
|
+
}
|
|
645
|
+
return output;
|
|
646
|
+
}
|
|
647
|
+
function literal(value) {
|
|
648
|
+
return new RegExp(value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"));
|
|
649
|
+
}
|
|
639
650
|
function timer(milliseconds, signal) {
|
|
640
651
|
return new Promise((resolve2, reject) => {
|
|
641
652
|
if (signal.aborted)
|
|
@@ -660,6 +671,7 @@ function createTelegramOperatorChannel(config) {
|
|
|
660
671
|
const minIntervalMs = config.minIntervalMs ?? 3000;
|
|
661
672
|
const maxAttempts = config.maxAttempts ?? 3;
|
|
662
673
|
const sleep = config.sleep ?? timer;
|
|
674
|
+
const patterns = [TELEGRAM_BOT_TOKEN_PATTERN, ...config.sensitivePatterns ?? []];
|
|
663
675
|
const queue = [];
|
|
664
676
|
const idle = new Set;
|
|
665
677
|
let lifetime = new AbortController;
|
|
@@ -740,7 +752,7 @@ function createTelegramOperatorChannel(config) {
|
|
|
740
752
|
try {
|
|
741
753
|
const item = {
|
|
742
754
|
...topic !== undefined && { topic },
|
|
743
|
-
text,
|
|
755
|
+
text: masked(text, patterns),
|
|
744
756
|
attempts: 0
|
|
745
757
|
};
|
|
746
758
|
if (closed) {
|
|
@@ -786,12 +798,13 @@ function createTelegramOperatorChannel(config) {
|
|
|
786
798
|
};
|
|
787
799
|
}
|
|
788
800
|
function telegramOperatorSender(config) {
|
|
801
|
+
const token = literal(config.token);
|
|
789
802
|
return (message, signal) => callTelegramBotApi({
|
|
790
803
|
token: config.token,
|
|
791
804
|
method: "sendMessage",
|
|
792
805
|
params: {
|
|
793
806
|
chat_id: message.chatId,
|
|
794
|
-
text: message.text,
|
|
807
|
+
text: masked(message.text, [token]),
|
|
795
808
|
...message.threadId !== undefined && { message_thread_id: message.threadId },
|
|
796
809
|
...config.parseMode && { parse_mode: config.parseMode },
|
|
797
810
|
link_preview_options: { is_disabled: true }
|
|
@@ -48,6 +48,12 @@ export interface TelegramOperatorChannelConfig<TTopic extends string> {
|
|
|
48
48
|
readonly onDropped?: (drop: TelegramOperatorDrop<TTopic>) => void;
|
|
49
49
|
/** Default: a timer. Rejects when `signal` aborts. */
|
|
50
50
|
readonly sleep?: (milliseconds: number, signal: AbortSignal) => Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* More secrets to mask in every message before it leaves. A bot token is
|
|
53
|
+
* always masked: bots post their errors here, and an error about a local Bot
|
|
54
|
+
* API file carries `<root>/<token>/…` in its path.
|
|
55
|
+
*/
|
|
56
|
+
readonly sensitivePatterns?: readonly RegExp[];
|
|
51
57
|
}
|
|
52
58
|
export interface TelegramOperatorChannel<TTopic extends string> {
|
|
53
59
|
/** Queue one message. Returns at once and never throws. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"operator-channel.d.ts","sourceRoot":"","sources":["../../src/telegram/operator-channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAGH,OAAO,EAA+B,KAAK,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAEvF,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG,MAAM,CAAC;
|
|
1
|
+
{"version":3,"file":"operator-channel.d.ts","sourceRoot":"","sources":["../../src/telegram/operator-channel.ts"],"names":[],"mappings":"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"}
|
package/llms/telegram.txt
CHANGED
|
@@ -77,7 +77,10 @@ const operators = createTelegramOperatorChannel<'users' | 'payments' | 'errors'>
|
|
|
77
77
|
operators.post(`New user ${user.id}`, 'users')
|
|
78
78
|
```
|
|
79
79
|
|
|
80
|
-
|
|
80
|
+
Every message is masked before it leaves: a bot token always — bots post their
|
|
81
|
+
errors here, and an error about a local Bot API file carries the token in its
|
|
82
|
+
path — plus any `sensitivePatterns` the bot adds; `telegramOperatorSender` also
|
|
83
|
+
masks its own exact token. What to post stays the bot's. On the way down, drain it within the grace period
|
|
81
84
|
and close it: `drain: (context) => operators.drain(context.signal)`,
|
|
82
85
|
`close: () => operators.close()` in a resource.
|
|
83
86
|
|
|
@@ -176,7 +179,7 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
|
|
|
176
179
|
| `TelegramBroadcastConfig` / `TelegramBroadcastSend` / `TelegramBroadcastReport` / `TelegramBroadcastRunOutcome` / `TelegramBroadcastOutcome` / `TelegramBroadcastRecipient` | _type_ | `name`, `directory`, `recipients()`, `send({ recipient, attempt })`, `maxAttempts`, `signal`, `onProgress`; counts `delivered` / `unreachable` / `failed` / `uncertain` / `pending` and `finished` / `stopped` / `halted` / `dry-run` |
|
|
177
180
|
| `telegramBroadcastSender` / `TelegramBroadcastSenderConfig` / `TelegramBroadcastMessage` | function / _type_ | the standard `send`: `sendMessage` with a text or `copyMessage` of a prepared message, through the Bot API |
|
|
178
181
|
| `createTelegramOperatorChannel` | function | the operator chat, apart from the journal: `post(text, topic?)` returns at once and never throws; paced sends into forum topics, 429 honoured, oldest dropped on overflow, `drain(signal)` and `close()` for the way down |
|
|
179
|
-
| `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped
|
|
182
|
+
| `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped`, `sensitivePatterns` (a bot token is always masked); a drop is `overflow` / `refused` / `attempts` / `closed` with its classification |
|
|
180
183
|
| `telegramOperatorSender` / `TelegramOperatorSenderConfig` | function / _type_ | the standard `send`: `sendMessage` into the chat and topic, plain text unless `parseMode` |
|
|
181
184
|
| `createTelegramLocalFiles` | function | files the local Bot API server wrote under `<root>/<token>/`: `resolve(file_path)` and `remove(file_path)` only inside the bot's directory, judged on real paths so a link is judged by where it leads; `check()` for readiness. Recommended root variable: `BOT_API_FILES_ROOT` |
|
|
182
185
|
| `TelegramLocalFilesConfig` / `TelegramLocalFiles` / `TelegramLocalFilesCheck` / `TelegramLocalFileError` / `TelegramLocalFileRefusal` | _type_ / class | `root`, `token`; refusals `root-not-absolute` / `bot-directory-unavailable` / `outside-bot-directory` / `missing` / `not-a-file`, never carrying the path or the token |
|
package/llms-full.txt
CHANGED
|
@@ -12829,7 +12829,10 @@ const operators = createTelegramOperatorChannel<'users' | 'payments' | 'errors'>
|
|
|
12829
12829
|
operators.post(`New user ${user.id}`, 'users')
|
|
12830
12830
|
```
|
|
12831
12831
|
|
|
12832
|
-
|
|
12832
|
+
Every message is masked before it leaves: a bot token always — bots post their
|
|
12833
|
+
errors here, and an error about a local Bot API file carries the token in its
|
|
12834
|
+
path — plus any `sensitivePatterns` the bot adds; `telegramOperatorSender` also
|
|
12835
|
+
masks its own exact token. What to post stays the bot's. On the way down, drain it within the grace period
|
|
12833
12836
|
and close it: `drain: (context) => operators.drain(context.signal)`,
|
|
12834
12837
|
`close: () => operators.close()` in a resource.
|
|
12835
12838
|
|
|
@@ -18540,7 +18543,7 @@ injected grammY bot. → ADR 0143, ADR 0201 — [guide](../guide/telegram.md)
|
|
|
18540
18543
|
| `TelegramBroadcastConfig` / `TelegramBroadcastSend` / `TelegramBroadcastReport` / `TelegramBroadcastRunOutcome` / `TelegramBroadcastOutcome` / `TelegramBroadcastRecipient` | _type_ | `name`, `directory`, `recipients()`, `send({ recipient, attempt })`, `maxAttempts`, `signal`, `onProgress`; counts `delivered` / `unreachable` / `failed` / `uncertain` / `pending` and `finished` / `stopped` / `halted` / `dry-run` |
|
|
18541
18544
|
| `telegramBroadcastSender` / `TelegramBroadcastSenderConfig` / `TelegramBroadcastMessage` | function / _type_ | the standard `send`: `sendMessage` with a text or `copyMessage` of a prepared message, through the Bot API |
|
|
18542
18545
|
| `createTelegramOperatorChannel` | function | the operator chat, apart from the journal: `post(text, topic?)` returns at once and never throws; paced sends into forum topics, 429 honoured, oldest dropped on overflow, `drain(signal)` and `close()` for the way down |
|
|
18543
|
-
| `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped
|
|
18546
|
+
| `TelegramOperatorChannelConfig` / `TelegramOperatorChannel` / `TelegramOperatorMessage` / `TelegramOperatorDrop` / `TelegramChatId` | _type_ | `chatId`, `topics`, `send`, `maxQueued`, `minIntervalMs`, `maxAttempts`, `onDropped`, `sensitivePatterns` (a bot token is always masked); a drop is `overflow` / `refused` / `attempts` / `closed` with its classification |
|
|
18544
18547
|
| `telegramOperatorSender` / `TelegramOperatorSenderConfig` | function / _type_ | the standard `send`: `sendMessage` into the chat and topic, plain text unless `parseMode` |
|
|
18545
18548
|
| `createTelegramLocalFiles` | function | files the local Bot API server wrote under `<root>/<token>/`: `resolve(file_path)` and `remove(file_path)` only inside the bot's directory, judged on real paths so a link is judged by where it leads; `check()` for readiness. Recommended root variable: `BOT_API_FILES_ROOT` |
|
|
18546
18549
|
| `TelegramLocalFilesConfig` / `TelegramLocalFiles` / `TelegramLocalFilesCheck` / `TelegramLocalFileError` / `TelegramLocalFileRefusal` | _type_ / class | `root`, `token`; refusals `root-not-absolute` / `bot-directory-unavailable` / `outside-bot-directory` / `missing` / `not-a-file`, never carrying the path or the token |
|
package/llms.txt
CHANGED
|
@@ -39,7 +39,7 @@ Load the slice for each entrypoint your code imports — `import … from 'stitc
|
|
|
39
39
|
- **stitchkit/testing**: [llms/testing.txt](llms/testing.txt) 30.8 KB
|
|
40
40
|
- **stitchkit/agent-runtime/sandbox**: [llms/agent-runtime-sandbox.txt](llms/agent-runtime-sandbox.txt) 8.1 KB
|
|
41
41
|
- **stitchkit/files**: [llms/files.txt](llms/files.txt) 2.9 KB
|
|
42
|
-
- **stitchkit/telegram**: [llms/telegram.txt](llms/telegram.txt)
|
|
42
|
+
- **stitchkit/telegram**: [llms/telegram.txt](llms/telegram.txt) 12.0 KB
|
|
43
43
|
- **stitchkit/tracking**: [llms/tracking.txt](llms/tracking.txt) 16.8 KB
|
|
44
44
|
- **stitchkit/release**: [llms/release.txt](llms/release.txt) 8.9 KB
|
|
45
45
|
- **stitchkit/tracking/server**: [llms/tracking-server.txt](llms/tracking-server.txt) 2.5 KB
|
package/package.json
CHANGED