zardbot-telegram 1.0.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.
- package/.env.example +116 -0
- package/LICENSE +21 -0
- package/README.md +250 -0
- package/dist/agent/manager.js +88 -0
- package/dist/agent/types.js +26 -0
- package/dist/app/start-bot-app.js +49 -0
- package/dist/bot/commands/abort.js +121 -0
- package/dist/bot/commands/commands.js +480 -0
- package/dist/bot/commands/definitions.js +27 -0
- package/dist/bot/commands/help.js +10 -0
- package/dist/bot/commands/models.js +38 -0
- package/dist/bot/commands/new.js +70 -0
- package/dist/bot/commands/opencode-start.js +101 -0
- package/dist/bot/commands/opencode-stop.js +44 -0
- package/dist/bot/commands/projects.js +223 -0
- package/dist/bot/commands/rename.js +139 -0
- package/dist/bot/commands/sessions.js +351 -0
- package/dist/bot/commands/start.js +43 -0
- package/dist/bot/commands/status.js +95 -0
- package/dist/bot/commands/task.js +399 -0
- package/dist/bot/commands/tasklist.js +220 -0
- package/dist/bot/commands/voice.js +145 -0
- package/dist/bot/handlers/agent.js +118 -0
- package/dist/bot/handlers/context.js +100 -0
- package/dist/bot/handlers/document.js +65 -0
- package/dist/bot/handlers/inline-menu.js +119 -0
- package/dist/bot/handlers/model.js +143 -0
- package/dist/bot/handlers/permission.js +235 -0
- package/dist/bot/handlers/prompt.js +240 -0
- package/dist/bot/handlers/question.js +390 -0
- package/dist/bot/handlers/tts.js +89 -0
- package/dist/bot/handlers/variant.js +138 -0
- package/dist/bot/handlers/voice.js +173 -0
- package/dist/bot/index.js +977 -0
- package/dist/bot/message-patterns.js +4 -0
- package/dist/bot/middleware/auth.js +30 -0
- package/dist/bot/middleware/interaction-guard.js +95 -0
- package/dist/bot/middleware/unknown-command.js +22 -0
- package/dist/bot/streaming/response-streamer.js +286 -0
- package/dist/bot/streaming/tool-call-streamer.js +285 -0
- package/dist/bot/utils/busy-guard.js +15 -0
- package/dist/bot/utils/commands.js +21 -0
- package/dist/bot/utils/file-download.js +91 -0
- package/dist/bot/utils/finalize-assistant-response.js +52 -0
- package/dist/bot/utils/keyboard.js +69 -0
- package/dist/bot/utils/send-with-markdown-fallback.js +165 -0
- package/dist/bot/utils/telegram-text.js +28 -0
- package/dist/bot/utils/thinking-message.js +8 -0
- package/dist/cli/args.js +98 -0
- package/dist/cli.js +80 -0
- package/dist/config.js +97 -0
- package/dist/i18n/de.js +357 -0
- package/dist/i18n/en.js +357 -0
- package/dist/i18n/es.js +357 -0
- package/dist/i18n/fr.js +357 -0
- package/dist/i18n/index.js +109 -0
- package/dist/i18n/ru.js +357 -0
- package/dist/i18n/zh.js +357 -0
- package/dist/index.js +26 -0
- package/dist/interaction/busy.js +8 -0
- package/dist/interaction/cleanup.js +32 -0
- package/dist/interaction/guard.js +140 -0
- package/dist/interaction/manager.js +106 -0
- package/dist/interaction/types.js +1 -0
- package/dist/keyboard/manager.js +172 -0
- package/dist/keyboard/types.js +1 -0
- package/dist/model/capabilities.js +62 -0
- package/dist/model/context-limit.js +57 -0
- package/dist/model/manager.js +259 -0
- package/dist/model/types.js +24 -0
- package/dist/opencode/client.js +13 -0
- package/dist/opencode/events.js +140 -0
- package/dist/permission/manager.js +100 -0
- package/dist/permission/types.js +1 -0
- package/dist/pinned/format.js +29 -0
- package/dist/pinned/manager.js +682 -0
- package/dist/pinned/types.js +1 -0
- package/dist/process/manager.js +273 -0
- package/dist/process/types.js +1 -0
- package/dist/project/manager.js +88 -0
- package/dist/question/manager.js +176 -0
- package/dist/question/types.js +1 -0
- package/dist/rename/manager.js +53 -0
- package/dist/runtime/bootstrap.js +350 -0
- package/dist/runtime/mode.js +74 -0
- package/dist/runtime/paths.js +37 -0
- package/dist/scheduled-task/creation-manager.js +113 -0
- package/dist/scheduled-task/display.js +239 -0
- package/dist/scheduled-task/executor.js +87 -0
- package/dist/scheduled-task/foreground-state.js +32 -0
- package/dist/scheduled-task/next-run.js +207 -0
- package/dist/scheduled-task/runtime.js +368 -0
- package/dist/scheduled-task/schedule-parser.js +169 -0
- package/dist/scheduled-task/store.js +65 -0
- package/dist/scheduled-task/types.js +19 -0
- package/dist/session/cache-manager.js +455 -0
- package/dist/session/manager.js +10 -0
- package/dist/settings/manager.js +158 -0
- package/dist/stt/client.js +97 -0
- package/dist/summary/aggregator.js +1136 -0
- package/dist/summary/formatter.js +491 -0
- package/dist/summary/subagent-formatter.js +63 -0
- package/dist/summary/tool-message-batcher.js +90 -0
- package/dist/tts/client.js +130 -0
- package/dist/utils/error-format.js +29 -0
- package/dist/utils/logger.js +127 -0
- package/dist/utils/safe-background-task.js +33 -0
- package/dist/utils/telegram-rate-limit-retry.js +93 -0
- package/dist/variant/manager.js +103 -0
- package/dist/variant/types.js +1 -0
- package/package.json +79 -0
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { logger } from "../../utils/logger.js";
|
|
2
|
+
const MARKDOWN_PARSE_ERROR_MARKERS = [
|
|
3
|
+
"can't parse entities",
|
|
4
|
+
"can't parse entity",
|
|
5
|
+
"can't find end of the entity",
|
|
6
|
+
"entity beginning",
|
|
7
|
+
"bad request: can't parse",
|
|
8
|
+
];
|
|
9
|
+
const MARKDOWN_V2_RESERVED_CHARS = new Set([
|
|
10
|
+
"_",
|
|
11
|
+
"*",
|
|
12
|
+
"[",
|
|
13
|
+
"]",
|
|
14
|
+
"(",
|
|
15
|
+
")",
|
|
16
|
+
"~",
|
|
17
|
+
"`",
|
|
18
|
+
">",
|
|
19
|
+
"#",
|
|
20
|
+
"+",
|
|
21
|
+
"-",
|
|
22
|
+
"=",
|
|
23
|
+
"|",
|
|
24
|
+
"{",
|
|
25
|
+
"}",
|
|
26
|
+
".",
|
|
27
|
+
"!",
|
|
28
|
+
"\\",
|
|
29
|
+
]);
|
|
30
|
+
const MARKDOWN_V2_ESCAPED_CHAR = /\\([_\*\[\]\(\)~`>#+\-=|{}.!\\])/g;
|
|
31
|
+
function escapeTelegramMarkdownV2(text) {
|
|
32
|
+
let result = "";
|
|
33
|
+
let trailingBackslashes = 0;
|
|
34
|
+
for (const char of text) {
|
|
35
|
+
if (char === "\\") {
|
|
36
|
+
result += char;
|
|
37
|
+
trailingBackslashes += 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
const isEscaped = trailingBackslashes % 2 === 1;
|
|
41
|
+
trailingBackslashes = 0;
|
|
42
|
+
if (MARKDOWN_V2_RESERVED_CHARS.has(char) && !isEscaped) {
|
|
43
|
+
result += `\\${char}`;
|
|
44
|
+
continue;
|
|
45
|
+
}
|
|
46
|
+
result += char;
|
|
47
|
+
}
|
|
48
|
+
return result;
|
|
49
|
+
}
|
|
50
|
+
function unescapeTelegramMarkdownV2(text) {
|
|
51
|
+
return text.replace(MARKDOWN_V2_ESCAPED_CHAR, "$1");
|
|
52
|
+
}
|
|
53
|
+
function stripMarkdownFormattingOptions(options) {
|
|
54
|
+
if (!options) {
|
|
55
|
+
return options;
|
|
56
|
+
}
|
|
57
|
+
const rawOptions = {
|
|
58
|
+
...options,
|
|
59
|
+
};
|
|
60
|
+
delete rawOptions.parse_mode;
|
|
61
|
+
delete rawOptions.entities;
|
|
62
|
+
return rawOptions;
|
|
63
|
+
}
|
|
64
|
+
function getErrorText(error) {
|
|
65
|
+
const parts = [];
|
|
66
|
+
if (error instanceof Error) {
|
|
67
|
+
parts.push(error.message);
|
|
68
|
+
}
|
|
69
|
+
if (typeof error === "object" && error !== null) {
|
|
70
|
+
const description = Reflect.get(error, "description");
|
|
71
|
+
if (typeof description === "string") {
|
|
72
|
+
parts.push(description);
|
|
73
|
+
}
|
|
74
|
+
const message = Reflect.get(error, "message");
|
|
75
|
+
if (typeof message === "string") {
|
|
76
|
+
parts.push(message);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (typeof error === "string") {
|
|
80
|
+
parts.push(error);
|
|
81
|
+
}
|
|
82
|
+
if (parts.length === 0) {
|
|
83
|
+
return "";
|
|
84
|
+
}
|
|
85
|
+
return parts.join("\n").toLowerCase();
|
|
86
|
+
}
|
|
87
|
+
export function isTelegramMarkdownParseError(error) {
|
|
88
|
+
const errorText = getErrorText(error);
|
|
89
|
+
if (!errorText) {
|
|
90
|
+
return false;
|
|
91
|
+
}
|
|
92
|
+
return MARKDOWN_PARSE_ERROR_MARKERS.some((marker) => errorText.includes(marker));
|
|
93
|
+
}
|
|
94
|
+
export async function sendMessageWithMarkdownFallback({ api, chatId, text, rawFallbackText, options, parseMode, }) {
|
|
95
|
+
if (!parseMode) {
|
|
96
|
+
return api.sendMessage(chatId, text, options);
|
|
97
|
+
}
|
|
98
|
+
const markdownOptions = {
|
|
99
|
+
...(options || {}),
|
|
100
|
+
parse_mode: parseMode,
|
|
101
|
+
};
|
|
102
|
+
const fallbackText = rawFallbackText ?? (parseMode === "MarkdownV2" ? unescapeTelegramMarkdownV2(text) : text);
|
|
103
|
+
try {
|
|
104
|
+
return await api.sendMessage(chatId, text, markdownOptions);
|
|
105
|
+
}
|
|
106
|
+
catch (error) {
|
|
107
|
+
if (!isTelegramMarkdownParseError(error)) {
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
if (parseMode === "MarkdownV2") {
|
|
111
|
+
const escapedText = escapeTelegramMarkdownV2(text);
|
|
112
|
+
if (escapedText !== text) {
|
|
113
|
+
logger.warn("[Bot] Markdown parse failed, retrying assistant message with escaped MarkdownV2", error);
|
|
114
|
+
try {
|
|
115
|
+
return await api.sendMessage(chatId, escapedText, markdownOptions);
|
|
116
|
+
}
|
|
117
|
+
catch (escapedError) {
|
|
118
|
+
if (!isTelegramMarkdownParseError(escapedError)) {
|
|
119
|
+
throw escapedError;
|
|
120
|
+
}
|
|
121
|
+
logger.warn("[Bot] Escaped Markdown parse failed, retrying assistant message in raw mode", escapedError);
|
|
122
|
+
return api.sendMessage(chatId, fallbackText, stripMarkdownFormattingOptions(options));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
logger.warn("[Bot] Markdown parse failed, retrying assistant message in raw mode", error);
|
|
127
|
+
return api.sendMessage(chatId, fallbackText, stripMarkdownFormattingOptions(options));
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export async function editMessageWithMarkdownFallback({ api, chatId, messageId, text, rawFallbackText, options, parseMode, }) {
|
|
131
|
+
if (!parseMode) {
|
|
132
|
+
return api.editMessageText(chatId, messageId, text, options);
|
|
133
|
+
}
|
|
134
|
+
const markdownOptions = {
|
|
135
|
+
...(options || {}),
|
|
136
|
+
parse_mode: parseMode,
|
|
137
|
+
};
|
|
138
|
+
const fallbackText = rawFallbackText ?? (parseMode === "MarkdownV2" ? unescapeTelegramMarkdownV2(text) : text);
|
|
139
|
+
try {
|
|
140
|
+
return await api.editMessageText(chatId, messageId, text, markdownOptions);
|
|
141
|
+
}
|
|
142
|
+
catch (error) {
|
|
143
|
+
if (!isTelegramMarkdownParseError(error)) {
|
|
144
|
+
throw error;
|
|
145
|
+
}
|
|
146
|
+
if (parseMode === "MarkdownV2") {
|
|
147
|
+
const escapedText = escapeTelegramMarkdownV2(text);
|
|
148
|
+
if (escapedText !== text) {
|
|
149
|
+
logger.warn("[Bot] Markdown parse failed, retrying edited message with escaped MarkdownV2", error);
|
|
150
|
+
try {
|
|
151
|
+
return await api.editMessageText(chatId, messageId, escapedText, markdownOptions);
|
|
152
|
+
}
|
|
153
|
+
catch (escapedError) {
|
|
154
|
+
if (!isTelegramMarkdownParseError(escapedError)) {
|
|
155
|
+
throw escapedError;
|
|
156
|
+
}
|
|
157
|
+
logger.warn("[Bot] Escaped Markdown parse failed, retrying edited message in raw mode", escapedError);
|
|
158
|
+
return api.editMessageText(chatId, messageId, fallbackText, stripMarkdownFormattingOptions(options));
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
logger.warn("[Bot] Markdown parse failed, retrying edited message in raw mode", error);
|
|
163
|
+
return api.editMessageText(chatId, messageId, fallbackText, stripMarkdownFormattingOptions(options));
|
|
164
|
+
}
|
|
165
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { editMessageWithMarkdownFallback, sendMessageWithMarkdownFallback, } from "./send-with-markdown-fallback.js";
|
|
2
|
+
function resolveParseMode(format) {
|
|
3
|
+
if (format === "markdown_v2") {
|
|
4
|
+
return "MarkdownV2";
|
|
5
|
+
}
|
|
6
|
+
return undefined;
|
|
7
|
+
}
|
|
8
|
+
export async function sendBotText({ api, chatId, text, rawFallbackText, options, format = "raw", }) {
|
|
9
|
+
await sendMessageWithMarkdownFallback({
|
|
10
|
+
api,
|
|
11
|
+
chatId,
|
|
12
|
+
text,
|
|
13
|
+
rawFallbackText,
|
|
14
|
+
options,
|
|
15
|
+
parseMode: resolveParseMode(format),
|
|
16
|
+
});
|
|
17
|
+
}
|
|
18
|
+
export async function editBotText({ api, chatId, messageId, text, rawFallbackText, options, format = "raw", }) {
|
|
19
|
+
await editMessageWithMarkdownFallback({
|
|
20
|
+
api,
|
|
21
|
+
chatId,
|
|
22
|
+
messageId,
|
|
23
|
+
text,
|
|
24
|
+
rawFallbackText,
|
|
25
|
+
options,
|
|
26
|
+
parseMode: resolveParseMode(format),
|
|
27
|
+
});
|
|
28
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { t } from "../../i18n/index.js";
|
|
2
|
+
export function deliverThinkingMessage(sessionId, batcher, options) {
|
|
3
|
+
if (options.hideThinkingMessages) {
|
|
4
|
+
return;
|
|
5
|
+
}
|
|
6
|
+
const message = t("bot.thinking");
|
|
7
|
+
batcher.sendTextNow(sessionId, message, "thinking_started");
|
|
8
|
+
}
|
package/dist/cli/args.js
ADDED
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { t } from "../i18n/index.js";
|
|
2
|
+
const SUPPORTED_COMMANDS = ["start", "status", "stop", "config"];
|
|
3
|
+
function isCliCommand(value) {
|
|
4
|
+
return SUPPORTED_COMMANDS.includes(value);
|
|
5
|
+
}
|
|
6
|
+
function normalizeMode(value) {
|
|
7
|
+
if (value === "installed") {
|
|
8
|
+
return "installed";
|
|
9
|
+
}
|
|
10
|
+
if (value === "sources") {
|
|
11
|
+
return "sources";
|
|
12
|
+
}
|
|
13
|
+
return null;
|
|
14
|
+
}
|
|
15
|
+
export function parseCliArgs(argv) {
|
|
16
|
+
const args = [...argv];
|
|
17
|
+
let command = "start";
|
|
18
|
+
let mode;
|
|
19
|
+
let showHelp = false;
|
|
20
|
+
let currentIndex = 0;
|
|
21
|
+
const firstArg = args[0];
|
|
22
|
+
if (firstArg && !firstArg.startsWith("-")) {
|
|
23
|
+
if (!isCliCommand(firstArg)) {
|
|
24
|
+
return {
|
|
25
|
+
command,
|
|
26
|
+
showHelp: true,
|
|
27
|
+
error: t("cli.args.unknown_command", { value: firstArg }),
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
command = firstArg;
|
|
31
|
+
currentIndex = 1;
|
|
32
|
+
}
|
|
33
|
+
while (currentIndex < args.length) {
|
|
34
|
+
const token = args[currentIndex];
|
|
35
|
+
if (token === "--help" || token === "-h") {
|
|
36
|
+
showHelp = true;
|
|
37
|
+
currentIndex += 1;
|
|
38
|
+
continue;
|
|
39
|
+
}
|
|
40
|
+
if (token === "--mode") {
|
|
41
|
+
const modeValue = args[currentIndex + 1];
|
|
42
|
+
if (!modeValue || modeValue.startsWith("-")) {
|
|
43
|
+
return {
|
|
44
|
+
command,
|
|
45
|
+
mode,
|
|
46
|
+
showHelp: true,
|
|
47
|
+
error: t("cli.args.mode_requires_value"),
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
const parsedMode = normalizeMode(modeValue);
|
|
51
|
+
if (!parsedMode) {
|
|
52
|
+
return {
|
|
53
|
+
command,
|
|
54
|
+
mode,
|
|
55
|
+
showHelp: true,
|
|
56
|
+
error: t("cli.args.invalid_mode", { value: modeValue }),
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
mode = parsedMode;
|
|
60
|
+
currentIndex += 2;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (token.startsWith("--mode=")) {
|
|
64
|
+
const modeValue = token.slice("--mode=".length);
|
|
65
|
+
const parsedMode = normalizeMode(modeValue);
|
|
66
|
+
if (!parsedMode) {
|
|
67
|
+
return {
|
|
68
|
+
command,
|
|
69
|
+
mode,
|
|
70
|
+
showHelp: true,
|
|
71
|
+
error: t("cli.args.invalid_mode", { value: modeValue }),
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
mode = parsedMode;
|
|
75
|
+
currentIndex += 1;
|
|
76
|
+
continue;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
command,
|
|
80
|
+
mode,
|
|
81
|
+
showHelp: true,
|
|
82
|
+
error: t("cli.args.unknown_option", { value: token }),
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
if (command !== "start" && mode) {
|
|
86
|
+
return {
|
|
87
|
+
command,
|
|
88
|
+
mode,
|
|
89
|
+
showHelp: true,
|
|
90
|
+
error: t("cli.args.mode_only_start"),
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return {
|
|
94
|
+
command,
|
|
95
|
+
mode,
|
|
96
|
+
showHelp,
|
|
97
|
+
};
|
|
98
|
+
}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { parseCliArgs } from "./cli/args.js";
|
|
3
|
+
import { resolveRuntimeMode, setRuntimeMode } from "./runtime/mode.js";
|
|
4
|
+
import { t } from "./i18n/index.js";
|
|
5
|
+
const EXIT_SUCCESS = 0;
|
|
6
|
+
const EXIT_RUNTIME_ERROR = 1;
|
|
7
|
+
const EXIT_INVALID_ARGS = 2;
|
|
8
|
+
function writeStdout(message) {
|
|
9
|
+
process.stdout.write(`${message}\n`);
|
|
10
|
+
}
|
|
11
|
+
function writeStderr(message) {
|
|
12
|
+
process.stderr.write(`${message}\n`);
|
|
13
|
+
}
|
|
14
|
+
function printUsage() {
|
|
15
|
+
writeStdout(t("cli.usage"));
|
|
16
|
+
}
|
|
17
|
+
function getPlaceholderMessage(command) {
|
|
18
|
+
if (command === "status") {
|
|
19
|
+
return t("cli.placeholder.status");
|
|
20
|
+
}
|
|
21
|
+
if (command === "stop") {
|
|
22
|
+
return t("cli.placeholder.stop");
|
|
23
|
+
}
|
|
24
|
+
return t("cli.placeholder.unavailable");
|
|
25
|
+
}
|
|
26
|
+
async function runStartCommand(mode) {
|
|
27
|
+
const modeResult = resolveRuntimeMode({
|
|
28
|
+
defaultMode: "installed",
|
|
29
|
+
explicitMode: mode,
|
|
30
|
+
});
|
|
31
|
+
if (modeResult.error) {
|
|
32
|
+
throw new Error(modeResult.error);
|
|
33
|
+
}
|
|
34
|
+
setRuntimeMode(modeResult.mode);
|
|
35
|
+
const { ensureRuntimeConfigForStart } = await import("./runtime/bootstrap.js");
|
|
36
|
+
await ensureRuntimeConfigForStart();
|
|
37
|
+
const { startBotApp } = await import("./app/start-bot-app.js");
|
|
38
|
+
await startBotApp();
|
|
39
|
+
return EXIT_SUCCESS;
|
|
40
|
+
}
|
|
41
|
+
async function runConfigCommand() {
|
|
42
|
+
setRuntimeMode("installed");
|
|
43
|
+
const { runConfigWizardCommand } = await import("./runtime/bootstrap.js");
|
|
44
|
+
await runConfigWizardCommand();
|
|
45
|
+
return EXIT_SUCCESS;
|
|
46
|
+
}
|
|
47
|
+
async function runPlaceholderCommand(command) {
|
|
48
|
+
writeStdout(getPlaceholderMessage(command));
|
|
49
|
+
return EXIT_SUCCESS;
|
|
50
|
+
}
|
|
51
|
+
async function runCli(argv) {
|
|
52
|
+
const parsedArgs = parseCliArgs(argv);
|
|
53
|
+
if (parsedArgs.error) {
|
|
54
|
+
writeStderr(parsedArgs.error);
|
|
55
|
+
}
|
|
56
|
+
if (parsedArgs.showHelp) {
|
|
57
|
+
printUsage();
|
|
58
|
+
return parsedArgs.error ? EXIT_INVALID_ARGS : EXIT_SUCCESS;
|
|
59
|
+
}
|
|
60
|
+
if (parsedArgs.command === "start") {
|
|
61
|
+
return runStartCommand(parsedArgs.mode);
|
|
62
|
+
}
|
|
63
|
+
if (parsedArgs.command === "config") {
|
|
64
|
+
return runConfigCommand();
|
|
65
|
+
}
|
|
66
|
+
return runPlaceholderCommand(parsedArgs.command);
|
|
67
|
+
}
|
|
68
|
+
void runCli(process.argv.slice(2))
|
|
69
|
+
.then((exitCode) => {
|
|
70
|
+
process.exitCode = exitCode;
|
|
71
|
+
})
|
|
72
|
+
.catch((error) => {
|
|
73
|
+
if (error instanceof Error) {
|
|
74
|
+
writeStderr(t("cli.error.prefix", { message: error.message }));
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
writeStderr(t("cli.error.prefix", { message: String(error) }));
|
|
78
|
+
}
|
|
79
|
+
process.exitCode = EXIT_RUNTIME_ERROR;
|
|
80
|
+
});
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import dotenv from "dotenv";
|
|
2
|
+
import { getRuntimePaths } from "./runtime/paths.js";
|
|
3
|
+
import { normalizeLocale } from "./i18n/index.js";
|
|
4
|
+
const runtimePaths = getRuntimePaths();
|
|
5
|
+
dotenv.config({ path: runtimePaths.envFilePath, quiet: true });
|
|
6
|
+
function getEnvVar(key, required = true) {
|
|
7
|
+
const value = process.env[key];
|
|
8
|
+
if (required && !value) {
|
|
9
|
+
throw new Error(`Missing required environment variable: ${key} (expected in ${runtimePaths.envFilePath})`);
|
|
10
|
+
}
|
|
11
|
+
return value || "";
|
|
12
|
+
}
|
|
13
|
+
function getOptionalPositiveIntEnvVar(key, defaultValue) {
|
|
14
|
+
const value = getEnvVar(key, false);
|
|
15
|
+
if (!value) {
|
|
16
|
+
return defaultValue;
|
|
17
|
+
}
|
|
18
|
+
const parsedValue = Number.parseInt(value, 10);
|
|
19
|
+
if (Number.isNaN(parsedValue) || parsedValue <= 0) {
|
|
20
|
+
return defaultValue;
|
|
21
|
+
}
|
|
22
|
+
return parsedValue;
|
|
23
|
+
}
|
|
24
|
+
function getOptionalLocaleEnvVar(key, defaultValue) {
|
|
25
|
+
const value = getEnvVar(key, false);
|
|
26
|
+
return normalizeLocale(value, defaultValue);
|
|
27
|
+
}
|
|
28
|
+
function getOptionalBooleanEnvVar(key, defaultValue) {
|
|
29
|
+
const value = getEnvVar(key, false);
|
|
30
|
+
if (!value) {
|
|
31
|
+
return defaultValue;
|
|
32
|
+
}
|
|
33
|
+
const normalized = value.trim().toLowerCase();
|
|
34
|
+
if (["1", "true", "yes", "on"].includes(normalized)) {
|
|
35
|
+
return true;
|
|
36
|
+
}
|
|
37
|
+
if (["0", "false", "no", "off"].includes(normalized)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
return defaultValue;
|
|
41
|
+
}
|
|
42
|
+
function getOptionalMessageFormatModeEnvVar(key, defaultValue) {
|
|
43
|
+
const value = getEnvVar(key, false);
|
|
44
|
+
if (!value) {
|
|
45
|
+
return defaultValue;
|
|
46
|
+
}
|
|
47
|
+
const normalized = value.trim().toLowerCase();
|
|
48
|
+
if (normalized === "raw" || normalized === "markdown") {
|
|
49
|
+
return normalized;
|
|
50
|
+
}
|
|
51
|
+
return defaultValue;
|
|
52
|
+
}
|
|
53
|
+
export const config = {
|
|
54
|
+
telegram: {
|
|
55
|
+
token: getEnvVar("TELEGRAM_BOT_TOKEN"),
|
|
56
|
+
allowedUserId: parseInt(getEnvVar("TELEGRAM_ALLOWED_USER_ID"), 10),
|
|
57
|
+
proxyUrl: getEnvVar("TELEGRAM_PROXY_URL", false),
|
|
58
|
+
},
|
|
59
|
+
opencode: {
|
|
60
|
+
apiUrl: getEnvVar("OPENCODE_API_URL", false) || "http://localhost:4096",
|
|
61
|
+
username: getEnvVar("OPENCODE_SERVER_USERNAME", false) || "opencode",
|
|
62
|
+
password: getEnvVar("OPENCODE_SERVER_PASSWORD", false),
|
|
63
|
+
model: {
|
|
64
|
+
provider: getEnvVar("OPENCODE_MODEL_PROVIDER", true), // Required
|
|
65
|
+
modelId: getEnvVar("OPENCODE_MODEL_ID", true), // Required
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
server: {
|
|
69
|
+
logLevel: getEnvVar("LOG_LEVEL", false) || "info",
|
|
70
|
+
},
|
|
71
|
+
bot: {
|
|
72
|
+
sessionsListLimit: getOptionalPositiveIntEnvVar("SESSIONS_LIST_LIMIT", 10),
|
|
73
|
+
projectsListLimit: getOptionalPositiveIntEnvVar("PROJECTS_LIST_LIMIT", 10),
|
|
74
|
+
commandsListLimit: getOptionalPositiveIntEnvVar("COMMANDS_LIST_LIMIT", 10),
|
|
75
|
+
taskLimit: getOptionalPositiveIntEnvVar("TASK_LIMIT", 10),
|
|
76
|
+
responseStreamThrottleMs: getOptionalPositiveIntEnvVar("RESPONSE_STREAM_THROTTLE_MS", 500),
|
|
77
|
+
locale: getOptionalLocaleEnvVar("BOT_LOCALE", "en"),
|
|
78
|
+
hideThinkingMessages: getOptionalBooleanEnvVar("HIDE_THINKING_MESSAGES", false),
|
|
79
|
+
hideToolCallMessages: getOptionalBooleanEnvVar("HIDE_TOOL_CALL_MESSAGES", false),
|
|
80
|
+
messageFormatMode: getOptionalMessageFormatModeEnvVar("MESSAGE_FORMAT_MODE", "markdown"),
|
|
81
|
+
},
|
|
82
|
+
files: {
|
|
83
|
+
maxFileSizeKb: parseInt(getEnvVar("CODE_FILE_MAX_SIZE_KB", false) || "100", 10),
|
|
84
|
+
},
|
|
85
|
+
stt: {
|
|
86
|
+
apiUrl: getEnvVar("STT_API_URL", false),
|
|
87
|
+
apiKey: getEnvVar("STT_API_KEY", false),
|
|
88
|
+
model: getEnvVar("STT_MODEL", false) || "whisper-large-v3-turbo",
|
|
89
|
+
language: getEnvVar("STT_LANGUAGE", false),
|
|
90
|
+
},
|
|
91
|
+
tts: {
|
|
92
|
+
apiUrl: getEnvVar("TTS_API_URL", false),
|
|
93
|
+
voice: getEnvVar("TTS_DEFAULT_VOICE", false) || "david-attenborough-original",
|
|
94
|
+
model: getEnvVar("TTS_MODEL", false) || "tts-1",
|
|
95
|
+
speed: parseFloat(getEnvVar("TTS_SPEED", false) || "1.0"),
|
|
96
|
+
},
|
|
97
|
+
};
|