cli-jaw 2.17.9 → 2.17.10
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/dist/src/core/config.js +34 -3
- package/dist/src/core/config.js.map +1 -1
- package/dist/src/core/settings-merge.js +40 -0
- package/dist/src/core/settings-merge.js.map +1 -1
- package/dist/src/discord/bot.js +140 -26
- package/dist/src/discord/bot.js.map +1 -1
- package/dist/src/discord/reactions.js +92 -0
- package/dist/src/discord/reactions.js.map +1 -0
- package/dist/src/messaging/ack-reaction.js +192 -0
- package/dist/src/messaging/ack-reaction.js.map +1 -0
- package/dist/src/messaging/channel-capabilities.js +9 -3
- package/dist/src/messaging/channel-capabilities.js.map +1 -1
- package/dist/src/messaging/channel-validate.js +12 -0
- package/dist/src/messaging/channel-validate.js.map +1 -1
- package/dist/src/messaging/queue-notice.js +164 -0
- package/dist/src/messaging/queue-notice.js.map +1 -0
- package/dist/src/slack/api.js +93 -0
- package/dist/src/slack/api.js.map +1 -1
- package/dist/src/slack/auto-join.js +330 -0
- package/dist/src/slack/auto-join.js.map +1 -0
- package/dist/src/slack/bot.js +237 -21
- package/dist/src/slack/bot.js.map +1 -1
- package/dist/src/slack/manifest.js +12 -0
- package/dist/src/slack/manifest.js.map +1 -1
- package/dist/src/slack/send-only-client.js +14 -2
- package/dist/src/slack/send-only-client.js.map +1 -1
- package/dist/src/telegram/bot.js +146 -64
- package/dist/src/telegram/bot.js.map +1 -1
- package/dist/src/telegram/ipv4-fetch.js +74 -0
- package/dist/src/telegram/ipv4-fetch.js.map +1 -0
- package/dist/src/telegram/reactions.js +126 -0
- package/dist/src/telegram/reactions.js.map +1 -0
- package/package.json +1 -1
- package/public/locales/en.json +1 -0
- package/public/locales/ja.json +1 -0
- package/public/locales/ko.json +1 -0
- package/public/locales/zh.json +1 -0
- package/scripts/check-redaction-sinks.mjs +5 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
// ─── Telegram Message Reactions (Bot API 7.0+) ───────
|
|
2
|
+
// Telegram does NOT accept arbitrary emoji here. ReactionTypeEmoji has a fixed
|
|
3
|
+
// allowlist, so a settings typo becomes a runtime API error unless it is checked
|
|
4
|
+
// before the call.
|
|
5
|
+
//
|
|
6
|
+
// Two consequences that look like bugs if you do not know them:
|
|
7
|
+
// 1. ✅ and ❌ are NOT on the list. Success/failure have to use 👍/👎, which is
|
|
8
|
+
// also what hermes-agent settled on — not a stylistic choice.
|
|
9
|
+
// 2. A bot's previous reaction is REPLACED atomically, so there is no remove
|
|
10
|
+
// step. Clearing first would show an empty state and cost an extra request.
|
|
11
|
+
/** ReactionTypeEmoji's complete allowlist, core.telegram.org/bots/api (2026-08-21). */
|
|
12
|
+
export const TELEGRAM_REACTION_EMOJI = new Set([
|
|
13
|
+
'\u2764', '\u{1F44D}', '\u{1F44E}', '\u{1F525}', '\u{1F970}', '\u{1F44F}',
|
|
14
|
+
'\u{1F601}', '\u{1F914}', '\u{1F92F}', '\u{1F631}', '\u{1F92C}', '\u{1F622}',
|
|
15
|
+
'\u{1F389}', '\u{1F929}', '\u{1F92E}', '\u{1F4A9}', '\u{1F64F}', '\u{1F44C}',
|
|
16
|
+
'\u{1F54A}', '\u{1F921}', '\u{1F971}', '\u{1F974}', '\u{1F60D}', '\u{1F433}',
|
|
17
|
+
'\u2764\u200D\u{1F525}', '\u{1F31A}', '\u{1F32D}', '\u{1F4AF}', '\u{1F923}',
|
|
18
|
+
'\u26A1', '\u{1F34C}', '\u{1F3C6}', '\u{1F494}', '\u{1F928}', '\u{1F610}',
|
|
19
|
+
'\u{1F353}', '\u{1F37E}', '\u{1F48B}', '\u{1F595}', '\u{1F608}', '\u{1F634}',
|
|
20
|
+
'\u{1F62D}', '\u{1F913}', '\u{1F47B}', '\u{1F468}\u200D\u{1F4BB}', '\u{1F440}',
|
|
21
|
+
'\u{1F383}', '\u{1F648}', '\u{1F607}', '\u{1F628}', '\u{1F91D}', '\u270D',
|
|
22
|
+
'\u{1F917}', '\u{1FAE1}', '\u{1F385}', '\u{1F384}', '\u2603', '\u{1F485}',
|
|
23
|
+
'\u{1F92A}', '\u{1F5FF}', '\u{1F192}', '\u{1F498}', '\u{1F649}', '\u{1F984}',
|
|
24
|
+
'\u{1F618}', '\u{1F48A}', '\u{1F64A}', '\u{1F60E}', '\u{1F47E}',
|
|
25
|
+
'\u{1F937}\u200D\u2642', '\u{1F937}', '\u{1F937}\u200D\u2640', '\u{1F621}',
|
|
26
|
+
]);
|
|
27
|
+
export function isTelegramReactionEmoji(emoji) {
|
|
28
|
+
return TELEGRAM_REACTION_EMOJI.has(emoji);
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Coerce an emoji into something Telegram will accept, or null to skip.
|
|
32
|
+
*
|
|
33
|
+
* Falls back rather than refusing: a bad config value should cost the reaction's
|
|
34
|
+
* expressiveness, not the acknowledgement itself. Returning null would silently
|
|
35
|
+
* turn the feature off for that user.
|
|
36
|
+
*/
|
|
37
|
+
export function coerceTelegramReaction(emoji, fallback = '\u{1F440}') {
|
|
38
|
+
if (isTelegramReactionEmoji(emoji))
|
|
39
|
+
return emoji;
|
|
40
|
+
return isTelegramReactionEmoji(fallback) ? fallback : null;
|
|
41
|
+
}
|
|
42
|
+
/** A reaction is decoration on top of the answer; it must never be the reason a
|
|
43
|
+
* shutdown hangs. Tight enough that remove+apply stays inside a drain deadline. */
|
|
44
|
+
export const TELEGRAM_REACTION_TIMEOUT_MS = 2500;
|
|
45
|
+
/** Same reasoning for the notice: a stuck cleanup must not hold shutdown open. */
|
|
46
|
+
export const TELEGRAM_NOTICE_TIMEOUT_MS = 5000;
|
|
47
|
+
/**
|
|
48
|
+
* Set (or clear) this bot's reaction on a message.
|
|
49
|
+
*
|
|
50
|
+
* Non-premium bots may set ONE reaction per message, so the array is length 0 or
|
|
51
|
+
* 1 and never more. An empty array clears. grammY throws on API failure, which is
|
|
52
|
+
* the reject-on-vendor-failure contract AckTransport requires.
|
|
53
|
+
*/
|
|
54
|
+
export async function setTelegramReaction(api, chatId, messageId, emoji, options = {}) {
|
|
55
|
+
const timeoutMs = options.timeoutMs ?? TELEGRAM_REACTION_TIMEOUT_MS;
|
|
56
|
+
// Both bounds matter and they mean different things: the caller's signal is a
|
|
57
|
+
// lifecycle cancellation (shutdown), the timeout is protection against a
|
|
58
|
+
// vendor call that simply never answers.
|
|
59
|
+
const signal = options.signal
|
|
60
|
+
? AbortSignal.any([options.signal, AbortSignal.timeout(timeoutMs)])
|
|
61
|
+
: AbortSignal.timeout(timeoutMs);
|
|
62
|
+
await api.raw.setMessageReaction({
|
|
63
|
+
chat_id: chatId,
|
|
64
|
+
message_id: messageId,
|
|
65
|
+
reaction: emoji ? [{ type: 'emoji', emoji }] : [],
|
|
66
|
+
}, signal);
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* The ACK transport for a Telegram message.
|
|
70
|
+
*
|
|
71
|
+
* Exported so production and tests build the SAME seam. A test that defines its
|
|
72
|
+
* own adapter proves only its own adapter: it would stay green if the wiring
|
|
73
|
+
* later picked remove-then-add or dropped the allowlist coercion.
|
|
74
|
+
*
|
|
75
|
+
* `replace` is a vendor fact, not a preference — Telegram swaps a bot's reaction
|
|
76
|
+
* atomically, so clearing first would show an empty state and cost a request.
|
|
77
|
+
*/
|
|
78
|
+
export function createTelegramAckTransport(api, chatId, messageId, options = {}) {
|
|
79
|
+
return {
|
|
80
|
+
mode: 'replace',
|
|
81
|
+
apply: async (emoji) => {
|
|
82
|
+
await setTelegramReaction(api, chatId, messageId, emoji, options);
|
|
83
|
+
},
|
|
84
|
+
remove: async () => {
|
|
85
|
+
// An empty reaction list is how Telegram clears; there is no
|
|
86
|
+
// dedicated remove call.
|
|
87
|
+
await setTelegramReaction(api, chatId, messageId, null, options);
|
|
88
|
+
},
|
|
89
|
+
coerce: (emoji) => coerceTelegramReaction(emoji),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* The queue-notice transport for a posted Telegram message.
|
|
94
|
+
*
|
|
95
|
+
* Exported for the same reason as the ACK transport: production and tests must
|
|
96
|
+
* drive the SAME binding, or a test proves only its own fixture.
|
|
97
|
+
*
|
|
98
|
+
* Both calls forward the signal grammY takes positionally. deleteMessage has a
|
|
99
|
+
* 48-hour limit and needs permission in groups; both failures are harmless here,
|
|
100
|
+
* since the worst case is the stale notice this whole unit exists to remove.
|
|
101
|
+
*/
|
|
102
|
+
export function createTelegramNoticeTransport(api, chatId, messageId, timeoutMs = TELEGRAM_NOTICE_TIMEOUT_MS) {
|
|
103
|
+
// A per-call timeout is composed even when the caller supplies nothing.
|
|
104
|
+
// QueueNotice pins whichever signal the FIRST close receives, and ordinary
|
|
105
|
+
// delivery and the 5-minute timer both close without one — so relying on the
|
|
106
|
+
// shutdown drain to supply it would leave the common path on grammY's
|
|
107
|
+
// 500-second default.
|
|
108
|
+
//
|
|
109
|
+
// The duration is a parameter purely so a test can prove the timeout FIRES
|
|
110
|
+
// without spending five real seconds waiting for it. A wall-clock wait that
|
|
111
|
+
// long is also what made the assertion flaky on CI, where the runner tore
|
|
112
|
+
// the event loop down before the timer landed.
|
|
113
|
+
const bounded = (signal) => {
|
|
114
|
+
const timeout = AbortSignal.timeout(timeoutMs);
|
|
115
|
+
return signal ? AbortSignal.any([signal, timeout]) : timeout;
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
delete: async (signal) => {
|
|
119
|
+
await api.deleteMessage(chatId, messageId, bounded(signal));
|
|
120
|
+
},
|
|
121
|
+
edit: async (text, signal) => {
|
|
122
|
+
await api.editMessageText(chatId, messageId, text, undefined, bounded(signal));
|
|
123
|
+
},
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
//# sourceMappingURL=reactions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"reactions.js","sourceRoot":"","sources":["../../../src/telegram/reactions.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cli-jaw",
|
|
3
|
-
"version": "2.17.
|
|
3
|
+
"version": "2.17.10",
|
|
4
4
|
"description": "Personal AI assistant powered by Pi, Antigravity, AI-E, Claude, Claude E, Codex, Codex App, Cursor, Grok, Kiro, OpenCode, and Copilot — Web, Terminal, Telegram, and Discord interfaces with 107 built-in skills",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"keywords": [
|
package/public/locales/en.json
CHANGED
|
@@ -156,6 +156,7 @@
|
|
|
156
156
|
"cmd.runtimeSelectionInstanceWide": "The CLI and model are shared by every session on this instance. Change them from the instance web settings.",
|
|
157
157
|
"tg.promptUnsupported": "(Not supported on Telegram)",
|
|
158
158
|
"tg.queued": "Added to queue (#{count})",
|
|
159
|
+
"tg.queueExpired": "The queued turn timed out",
|
|
159
160
|
"tg.imageCaption": "[📷 Image] {caption}",
|
|
160
161
|
"tg.imageFail": "Image processing failed: {msg}",
|
|
161
162
|
"tg.fileFail": "File processing failed: {msg}",
|
package/public/locales/ja.json
CHANGED
|
@@ -154,6 +154,7 @@
|
|
|
154
154
|
"cmd.runtimeSelectionInstanceWide": "CLI とモデルの選択はこのインスタンスの全セッションで共有されます。インスタンスのウェブ設定から変更してください。",
|
|
155
155
|
"tg.promptUnsupported": "(Telegram では未対応)",
|
|
156
156
|
"tg.queued": "キューに追加しました(#{count})",
|
|
157
|
+
"tg.queueExpired": "キューの待機時間が超過しました",
|
|
157
158
|
"tg.imageCaption": "[📷 画像] {caption}",
|
|
158
159
|
"tg.imageFail": "画像処理に失敗しました: {msg}",
|
|
159
160
|
"tg.fileFail": "ファイル処理に失敗しました: {msg}",
|
package/public/locales/ko.json
CHANGED
|
@@ -156,6 +156,7 @@
|
|
|
156
156
|
"cmd.runtimeSelectionInstanceWide": "CLI와 모델 선택은 이 인스턴스의 모든 세션이 함께 씁니다. 인스턴스 웹 설정에서 바꿔주세요.",
|
|
157
157
|
"tg.promptUnsupported": "(Telegram에서 미지원)",
|
|
158
158
|
"tg.queued": "대기열에 추가됨 ({count}번째)",
|
|
159
|
+
"tg.queueExpired": "대기 시간이 초과되었습니다",
|
|
159
160
|
"tg.imageCaption": "[📷 이미지] {caption}",
|
|
160
161
|
"tg.imageFail": "이미지 처리 실패: {msg}",
|
|
161
162
|
"tg.fileFail": "파일 처리 실패: {msg}",
|
package/public/locales/zh.json
CHANGED
|
@@ -154,6 +154,7 @@
|
|
|
154
154
|
"cmd.runtimeSelectionInstanceWide": "CLI 与模型选择由本实例的所有会话共用。请在实例网页设置中修改。",
|
|
155
155
|
"tg.promptUnsupported": "(Telegram 不支持)",
|
|
156
156
|
"tg.queued": "已加入队列(第 {count} 个)",
|
|
157
|
+
"tg.queueExpired": "排队等待已超时",
|
|
157
158
|
"tg.imageCaption": "[📷 图片] {caption}",
|
|
158
159
|
"tg.imageFail": "图片处理失败:{msg}",
|
|
159
160
|
"tg.fileFail": "文件处理失败:{msg}",
|
|
@@ -86,6 +86,11 @@ const ALLOWLIST = [
|
|
|
86
86
|
{ contains: "persist failed:", why: "a Node.js Error.message from fs write, not a credential" },
|
|
87
87
|
{ contains: 'await ctx.reply(syncText)', why: 'syncText is masked where it is built' },
|
|
88
88
|
{ contains: 'Chat ID: <code>', why: 'a chat id the user just asked for' },
|
|
89
|
+
// Queue-notice lifecycle (#412). Both carry text this repo built, not text
|
|
90
|
+
// that passed through a credential-bearing surface.
|
|
91
|
+
{ contains: "t('tg.queued'", why: 'a locale string plus a pending count' },
|
|
92
|
+
{ contains: 'const posted = await msg.reply(', why: 'the queue notice: a locale string plus a pending count' },
|
|
93
|
+
{ contains: 'api.editMessageText(chatId, messageId, text', why: 'text is the notice expiry constant supplied at createQueueNotice' },
|
|
89
94
|
];
|
|
90
95
|
|
|
91
96
|
/**
|