@ebowwa/glm-daemon-telegram 1.0.0 → 1.0.2
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/index.d.ts +22 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +344 -0
- package/dist/index.js.map +1 -0
- package/package.json +6 -1
- package/src/index.js +205 -0
- package/src/index.ts +314 -47
- package/CLAUDE.md +0 -106
- package/TICKET.md +0 -55
- package/deploy.sh +0 -26
- package/run.sh +0 -3
- package/tsconfig.json +0 -18
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon Telegram Bot
|
|
3
|
+
*
|
|
4
|
+
* Full-featured Telegram bot with GLM-4.7 AI, git status, Doppler integration
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - GLM-4.7 AI responses via Z.AI API
|
|
8
|
+
* - 429 rate limit handling with exponential backoff
|
|
9
|
+
* - /git command for git status + GitHub auth
|
|
10
|
+
* - /doppler command for Doppler secrets status
|
|
11
|
+
* - Rolling API key support
|
|
12
|
+
*/
|
|
13
|
+
declare class TelegramGLMBot {
|
|
14
|
+
private bot;
|
|
15
|
+
constructor(token: string);
|
|
16
|
+
start(): Promise<void>;
|
|
17
|
+
getGLMResponse(userMessage: string, userName: string): Promise<string>;
|
|
18
|
+
sendTestMessage(chatId: number): Promise<void>;
|
|
19
|
+
stop(): Promise<void>;
|
|
20
|
+
}
|
|
21
|
+
export { TelegramGLMBot };
|
|
22
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAqEH,cAAM,cAAc;IAClB,OAAO,CAAC,GAAG,CAAc;gBAEb,KAAK,EAAE,MAAM;IAInB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8MtB,cAAc,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAiDtE,eAAe,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAkB9C,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;CAI5B;AA8BD,OAAO,EAAE,cAAc,EAAE,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,344 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* GLM Daemon Telegram Bot
|
|
3
|
+
*
|
|
4
|
+
* Full-featured Telegram bot with GLM-4.7 AI, git status, Doppler integration
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - GLM-4.7 AI responses via Z.AI API
|
|
8
|
+
* - 429 rate limit handling with exponential backoff
|
|
9
|
+
* - /git command for git status + GitHub auth
|
|
10
|
+
* - /doppler command for Doppler secrets status
|
|
11
|
+
* - Rolling API key support
|
|
12
|
+
*/
|
|
13
|
+
import TelegramBot from 'node-telegram-bot-api';
|
|
14
|
+
import { execSync } from 'child_process';
|
|
15
|
+
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || '';
|
|
16
|
+
const TELEGRAM_TEST_CHAT_ID = process.env.TELEGRAM_TEST_CHAT_ID || '';
|
|
17
|
+
// API Key Resolution - supports rolling keys
|
|
18
|
+
const getZAIKey = () => {
|
|
19
|
+
const keyValue = process.env.Z_AI_API_KEY || process.env.ANTHROPIC_API_KEYS;
|
|
20
|
+
if (!keyValue)
|
|
21
|
+
return null;
|
|
22
|
+
try {
|
|
23
|
+
const keysArray = JSON.parse(keyValue);
|
|
24
|
+
if (Array.isArray(keysArray) && keysArray.length > 0) {
|
|
25
|
+
return keysArray[0];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch {
|
|
29
|
+
return keyValue;
|
|
30
|
+
}
|
|
31
|
+
return keyValue;
|
|
32
|
+
};
|
|
33
|
+
const ZAI_API_ENDPOINT = 'https://api.z.ai/api/coding/paas/v4/chat/completions';
|
|
34
|
+
// Retry helper with exponential backoff for 429 handling
|
|
35
|
+
const sleep = (ms) => new Promise(resolve => setTimeout(resolve, ms));
|
|
36
|
+
async function fetchWithRetry(url, options, maxRetries = 3, baseDelay = 1000) {
|
|
37
|
+
let lastError = null;
|
|
38
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
39
|
+
try {
|
|
40
|
+
const response = await fetch(url, options);
|
|
41
|
+
if (response.status === 429) {
|
|
42
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
43
|
+
const delay = retryAfter
|
|
44
|
+
? parseInt(retryAfter) * 1000
|
|
45
|
+
: baseDelay * Math.pow(2, attempt);
|
|
46
|
+
console.log(`[429] Rate limited, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
47
|
+
await sleep(delay);
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
if (!response.ok) {
|
|
51
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
52
|
+
}
|
|
53
|
+
return response;
|
|
54
|
+
}
|
|
55
|
+
catch (error) {
|
|
56
|
+
lastError = error;
|
|
57
|
+
if (attempt < maxRetries - 1) {
|
|
58
|
+
const delay = baseDelay * Math.pow(2, attempt);
|
|
59
|
+
console.log(`[Retry] Attempt ${attempt + 1} failed: ${lastError.message}, retrying in ${delay}ms`);
|
|
60
|
+
await sleep(delay);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
throw lastError;
|
|
65
|
+
}
|
|
66
|
+
class TelegramGLMBot {
|
|
67
|
+
bot;
|
|
68
|
+
constructor(token) {
|
|
69
|
+
this.bot = new TelegramBot(token, { polling: true });
|
|
70
|
+
}
|
|
71
|
+
async start() {
|
|
72
|
+
console.log('🤖 GLM Daemon Telegram Bot starting with GLM-4.7...');
|
|
73
|
+
// /start command
|
|
74
|
+
this.bot.onText(/\/start/, async (msg) => {
|
|
75
|
+
const chatId = msg.chat.id;
|
|
76
|
+
await this.bot.sendMessage(chatId, '👋 Hello! I\'m GLM Daemon Telegram Bot.\n\n' +
|
|
77
|
+
'🧠 Powered by GLM-4.7 via Z.AI API\n\n' +
|
|
78
|
+
'Commands:\n' +
|
|
79
|
+
'/start - Show this message\n' +
|
|
80
|
+
'/status - Check API status\n' +
|
|
81
|
+
'/git - Check git status & GitHub auth\n' +
|
|
82
|
+
'/doppler - Check Doppler config\n' +
|
|
83
|
+
'/help - Show help\n\n' +
|
|
84
|
+
'Or just send any message for AI assistance!');
|
|
85
|
+
});
|
|
86
|
+
// /status command
|
|
87
|
+
this.bot.onText(/\/status/, async (msg) => {
|
|
88
|
+
const chatId = msg.chat.id;
|
|
89
|
+
const apiKey = getZAIKey();
|
|
90
|
+
const status = {
|
|
91
|
+
api: !!apiKey,
|
|
92
|
+
key: apiKey ? `${apiKey.slice(0, 10)}...` : 'None',
|
|
93
|
+
endpoint: ZAI_API_ENDPOINT
|
|
94
|
+
};
|
|
95
|
+
await this.bot.sendMessage(chatId, `📊 Bot Status:\n\n` +
|
|
96
|
+
`✅ API: ${status.api ? 'Connected' : 'Disconnected'}\n` +
|
|
97
|
+
`🔑 Key: ${status.key ? 'Configured' : 'Missing'}\n` +
|
|
98
|
+
`📍 Endpoint: ${status.endpoint}`);
|
|
99
|
+
});
|
|
100
|
+
// /help command
|
|
101
|
+
this.bot.onText(/\/help/, async (msg) => {
|
|
102
|
+
const chatId = msg.chat.id;
|
|
103
|
+
await this.bot.sendMessage(chatId, '📚 *GLM Daemon Bot Help*\n\n' +
|
|
104
|
+
'🧠 Powered by GLM-4.7 via Z.AI API\n\n' +
|
|
105
|
+
'*Commands:*\n' +
|
|
106
|
+
'/start - Show welcome message\n' +
|
|
107
|
+
'/status - Check API status\n' +
|
|
108
|
+
'/git - Check git status & GitHub auth\n' +
|
|
109
|
+
'/git auth - Start GitHub auth flow\n' +
|
|
110
|
+
'/doppler - Check Doppler config\n' +
|
|
111
|
+
'/doppler secrets - List secret names\n' +
|
|
112
|
+
'/help - Show this help\n\n' +
|
|
113
|
+
'*Usage:*\n' +
|
|
114
|
+
'Just send any message and I\\\'ll respond with AI assistance!', { parse_mode: 'Markdown' });
|
|
115
|
+
});
|
|
116
|
+
// /git command
|
|
117
|
+
this.bot.onText(/\/git(?:\s+(.+))?/, async (msg, match) => {
|
|
118
|
+
const chatId = msg.chat.id;
|
|
119
|
+
const subCommand = match?.[1]?.trim();
|
|
120
|
+
try {
|
|
121
|
+
if (subCommand === 'auth') {
|
|
122
|
+
let statusMsg = 'GitHub Auth\n\n';
|
|
123
|
+
try {
|
|
124
|
+
const deviceResp = execSync('gh auth login --web --git-protocol https 2>&1 | head -20', {
|
|
125
|
+
encoding: 'utf-8',
|
|
126
|
+
timeout: 8000,
|
|
127
|
+
cwd: '/root'
|
|
128
|
+
});
|
|
129
|
+
const codeMatch = deviceResp.match(/verification code:\s*([A-Z0-9-]+)/i);
|
|
130
|
+
const urlMatch = deviceResp.match(/https:\/\/github\.com\/login\/device[^\s]*/);
|
|
131
|
+
if (codeMatch && urlMatch) {
|
|
132
|
+
statusMsg += `Code: ${codeMatch[1]}\n`;
|
|
133
|
+
statusMsg += `URL: ${urlMatch[0]}\n\n`;
|
|
134
|
+
statusMsg += `Open URL and enter code to authenticate.`;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
statusMsg += deviceResp.slice(0, 500);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
catch (e) {
|
|
141
|
+
statusMsg += `Error: ${e.message}`;
|
|
142
|
+
}
|
|
143
|
+
await this.bot.sendMessage(chatId, statusMsg);
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
let statusMsg = 'Git Status\n\n';
|
|
147
|
+
try {
|
|
148
|
+
const authStatus = execSync('gh auth status 2>&1', { encoding: 'utf-8', cwd: '/root' });
|
|
149
|
+
const loggedIn = authStatus.includes('Logged in');
|
|
150
|
+
const accountMatch = authStatus.match(/account\s+(\w+)/i);
|
|
151
|
+
statusMsg += `GitHub: ${loggedIn ? 'Connected' : 'Not logged in'}\n`;
|
|
152
|
+
if (accountMatch)
|
|
153
|
+
statusMsg += `Account: ${accountMatch[1]}\n`;
|
|
154
|
+
if (!loggedIn)
|
|
155
|
+
statusMsg += `\nUse /git auth to authenticate`;
|
|
156
|
+
}
|
|
157
|
+
catch {
|
|
158
|
+
statusMsg += `GitHub: Not authenticated\n`;
|
|
159
|
+
statusMsg += `Use /git auth to login\n`;
|
|
160
|
+
}
|
|
161
|
+
try {
|
|
162
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD 2>/dev/null', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
163
|
+
const ahead = execSync('git rev-list --count @{upstream}..HEAD 2>/dev/null || echo 0', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
164
|
+
const dirty = execSync('git status --porcelain 2>/dev/null | wc -l', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
165
|
+
statusMsg += `\nRepo:\n`;
|
|
166
|
+
statusMsg += `Branch: ${branch}\n`;
|
|
167
|
+
statusMsg += `Ahead: ${ahead} commits\n`;
|
|
168
|
+
statusMsg += `Modified: ${dirty} files`;
|
|
169
|
+
}
|
|
170
|
+
catch {
|
|
171
|
+
statusMsg += `\nRepo: Not a git repo or error`;
|
|
172
|
+
}
|
|
173
|
+
await this.bot.sendMessage(chatId, statusMsg);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
catch (error) {
|
|
177
|
+
await this.bot.sendMessage(chatId, `Error: ${error.message}`);
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
// /doppler command
|
|
181
|
+
this.bot.onText(/\/doppler(?:\s+(.+))?/, async (msg, match) => {
|
|
182
|
+
const chatId = msg.chat.id;
|
|
183
|
+
const subCommand = match?.[1]?.trim();
|
|
184
|
+
try {
|
|
185
|
+
if (subCommand === 'secrets') {
|
|
186
|
+
let statusMsg = 'Doppler Secrets\n\n';
|
|
187
|
+
try {
|
|
188
|
+
const secrets = execSync('doppler secrets --plain 2>&1 | cut -d\'\\t\' -f1', {
|
|
189
|
+
encoding: 'utf-8',
|
|
190
|
+
cwd: '/root'
|
|
191
|
+
});
|
|
192
|
+
const secretList = secrets.trim().split('\n').filter(s => s.length > 0);
|
|
193
|
+
statusMsg += `Found ${secretList.length} secrets:\n\n`;
|
|
194
|
+
statusMsg += secretList.slice(0, 20).join('\n');
|
|
195
|
+
if (secretList.length > 20) {
|
|
196
|
+
statusMsg += `\n... and ${secretList.length - 20} more`;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
statusMsg += `Error: ${e.message}`;
|
|
201
|
+
}
|
|
202
|
+
await this.bot.sendMessage(chatId, statusMsg);
|
|
203
|
+
}
|
|
204
|
+
else {
|
|
205
|
+
let statusMsg = 'Doppler Status\n\n';
|
|
206
|
+
try {
|
|
207
|
+
const project = execSync('doppler configure get project 2>/dev/null', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
208
|
+
const config = execSync('doppler configure get config 2>/dev/null', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
209
|
+
statusMsg += `Project: ${project || 'Not set'}\n`;
|
|
210
|
+
statusMsg += `Config: ${config || 'Not set'}\n`;
|
|
211
|
+
const tokenCheck = execSync('doppler me 2>&1 | head -5', { encoding: 'utf-8', cwd: '/root' });
|
|
212
|
+
if (tokenCheck.includes('email') || tokenCheck.includes('name')) {
|
|
213
|
+
statusMsg += `Token: Valid\n`;
|
|
214
|
+
}
|
|
215
|
+
else {
|
|
216
|
+
statusMsg += `Token: Not configured\n`;
|
|
217
|
+
}
|
|
218
|
+
statusMsg += `\nCommands:\n`;
|
|
219
|
+
statusMsg += `/doppler secrets - List secret names`;
|
|
220
|
+
}
|
|
221
|
+
catch {
|
|
222
|
+
statusMsg += `Not configured\n\n`;
|
|
223
|
+
statusMsg += `Run: doppler setup`;
|
|
224
|
+
}
|
|
225
|
+
await this.bot.sendMessage(chatId, statusMsg);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
catch (error) {
|
|
229
|
+
await this.bot.sendMessage(chatId, `Error: ${error.message}`);
|
|
230
|
+
}
|
|
231
|
+
});
|
|
232
|
+
// Handle all text messages with GLM-4.7
|
|
233
|
+
this.bot.on('message', async (msg) => {
|
|
234
|
+
if (!msg.text)
|
|
235
|
+
return;
|
|
236
|
+
if (msg.text.startsWith('/'))
|
|
237
|
+
return;
|
|
238
|
+
const chatId = msg.chat.id;
|
|
239
|
+
const userName = msg.from?.first_name || msg.from?.username || 'User';
|
|
240
|
+
console.log(`[Telegram] ${userName}: ${msg.text}`);
|
|
241
|
+
await this.bot.sendChatAction(chatId, 'typing');
|
|
242
|
+
const response = await this.getGLMResponse(msg.text, userName);
|
|
243
|
+
await this.bot.sendMessage(chatId, response);
|
|
244
|
+
});
|
|
245
|
+
// Polling errors
|
|
246
|
+
this.bot.on('polling_error', (error) => {
|
|
247
|
+
console.error('[Telegram] Polling error:', error.message);
|
|
248
|
+
});
|
|
249
|
+
console.log('✅ Telegram bot is running with GLM-4.7!');
|
|
250
|
+
if (TELEGRAM_TEST_CHAT_ID) {
|
|
251
|
+
await this.sendTestMessage(Number(TELEGRAM_TEST_CHAT_ID));
|
|
252
|
+
}
|
|
253
|
+
else {
|
|
254
|
+
console.log('\n💡 Tip: Get your chat ID from @userinfobot and set TELEGRAM_TEST_CHAT_ID');
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
async getGLMResponse(userMessage, userName) {
|
|
258
|
+
const apiKey = getZAIKey();
|
|
259
|
+
if (!apiKey) {
|
|
260
|
+
return '⚠️ Z_AI_API_KEY not configured in Doppler secrets.\n\nPlease set Z_AI_API_KEY to use GLM-4.7.';
|
|
261
|
+
}
|
|
262
|
+
try {
|
|
263
|
+
console.log(`🔄 Calling GLM-4.7 API for: ${userMessage}`);
|
|
264
|
+
const response = await fetchWithRetry(ZAI_API_ENDPOINT, {
|
|
265
|
+
method: 'POST',
|
|
266
|
+
headers: {
|
|
267
|
+
'Content-Type': 'application/json',
|
|
268
|
+
'Authorization': `Bearer ${apiKey}`
|
|
269
|
+
},
|
|
270
|
+
body: JSON.stringify({
|
|
271
|
+
model: 'glm-4.7',
|
|
272
|
+
messages: [
|
|
273
|
+
{
|
|
274
|
+
role: 'system',
|
|
275
|
+
content: 'You are GLM Daemon, an autonomous AI assistant that helps with coding tasks, questions, and feature development. You are running as part of a distributed system with Telegram and Discord channels. Be helpful, concise, and technical.'
|
|
276
|
+
},
|
|
277
|
+
{ role: 'user', content: userMessage }
|
|
278
|
+
],
|
|
279
|
+
temperature: 0.7,
|
|
280
|
+
max_tokens: 2048
|
|
281
|
+
})
|
|
282
|
+
}, 3, 1000);
|
|
283
|
+
const data = await response.json();
|
|
284
|
+
if (data.choices?.[0]?.message?.content) {
|
|
285
|
+
const aiResponse = data.choices[0].message.content;
|
|
286
|
+
console.log(`✅ GLM-4.7 response: ${aiResponse.slice(0, 100)}...`);
|
|
287
|
+
return aiResponse;
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
console.error('❌ Unexpected API response format:', data);
|
|
291
|
+
return '❌ Unexpected response from AI API.';
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
console.error('❌ Error calling GLM-4.7:', error.message);
|
|
296
|
+
if (error.message.includes('429')) {
|
|
297
|
+
return '⚠️ Rate limited by API. Please try again in a moment.';
|
|
298
|
+
}
|
|
299
|
+
return `❌ Error: ${error.message}`;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async sendTestMessage(chatId) {
|
|
303
|
+
const apiKey = getZAIKey();
|
|
304
|
+
await this.bot.sendMessage(chatId, '✅ GLM Daemon Telegram Bot is NOW ONLINE!\n\n' +
|
|
305
|
+
'🎉 Connected from seed-node-prod!\n\n' +
|
|
306
|
+
`🧠 AI: ${apiKey ? 'GLM-4.7 via Z.AI' : 'Not configured'}\n\n` +
|
|
307
|
+
'Commands:\n' +
|
|
308
|
+
'/start - Show welcome message\n' +
|
|
309
|
+
'/status - Check API status\n' +
|
|
310
|
+
'/git - Git & GitHub status\n' +
|
|
311
|
+
'/doppler - Doppler config\n' +
|
|
312
|
+
'/help - Show all commands\n' +
|
|
313
|
+
'Any message - Chat with GLM-4.7 AI');
|
|
314
|
+
console.log(`✅ Test message sent to chat ${chatId}`);
|
|
315
|
+
}
|
|
316
|
+
async stop() {
|
|
317
|
+
console.log('🛑 Stopping Telegram bot...');
|
|
318
|
+
this.bot.stopPolling();
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
// Main entry point
|
|
322
|
+
async function main() {
|
|
323
|
+
if (!TELEGRAM_BOT_TOKEN) {
|
|
324
|
+
console.error('❌ TELEGRAM_BOT_TOKEN not found in Doppler secrets');
|
|
325
|
+
console.error('Make sure to set in your seed/prd Doppler project');
|
|
326
|
+
process.exit(1);
|
|
327
|
+
}
|
|
328
|
+
console.log(`📱 Bot token loaded: ${TELEGRAM_BOT_TOKEN.substring(0, 10)}...`);
|
|
329
|
+
console.log(`🤖 Bot name: @SimulationapiBot`);
|
|
330
|
+
const bot = new TelegramGLMBot(TELEGRAM_BOT_TOKEN);
|
|
331
|
+
await bot.start();
|
|
332
|
+
process.on('SIGINT', async () => {
|
|
333
|
+
await bot.stop();
|
|
334
|
+
process.exit(0);
|
|
335
|
+
});
|
|
336
|
+
process.on('SIGTERM', async () => {
|
|
337
|
+
await bot.stop();
|
|
338
|
+
process.exit(0);
|
|
339
|
+
});
|
|
340
|
+
}
|
|
341
|
+
main().catch(console.error);
|
|
342
|
+
// Export for use as module
|
|
343
|
+
export { TelegramGLMBot };
|
|
344
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,10 +1,15 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ebowwa/glm-daemon-telegram",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Telegram adapter for GLM Daemon with ButlerAgent personality",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
7
7
|
"types": "./dist/index.d.ts",
|
|
8
|
+
"files": [
|
|
9
|
+
"dist",
|
|
10
|
+
"src",
|
|
11
|
+
"README.md"
|
|
12
|
+
],
|
|
8
13
|
"exports": {
|
|
9
14
|
".": {
|
|
10
15
|
"types": "./dist/index.d.ts",
|
package/src/index.js
ADDED
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GLM Daemon Telegram Bot
|
|
4
|
+
*
|
|
5
|
+
* Simple Telegram adapter - sends test message and echoes messages
|
|
6
|
+
*/
|
|
7
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
8
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
9
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
10
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
11
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
12
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
13
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
14
|
+
});
|
|
15
|
+
};
|
|
16
|
+
var __generator = (this && this.__generator) || function (thisArg, body) {
|
|
17
|
+
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
|
|
18
|
+
return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
|
|
19
|
+
function verb(n) { return function (v) { return step([n, v]); }; }
|
|
20
|
+
function step(op) {
|
|
21
|
+
if (f) throw new TypeError("Generator is already executing.");
|
|
22
|
+
while (g && (g = 0, op[0] && (_ = 0)), _) try {
|
|
23
|
+
if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
|
|
24
|
+
if (y = 0, t) op = [op[0] & 2, t.value];
|
|
25
|
+
switch (op[0]) {
|
|
26
|
+
case 0: case 1: t = op; break;
|
|
27
|
+
case 4: _.label++; return { value: op[1], done: false };
|
|
28
|
+
case 5: _.label++; y = op[1]; op = [0]; continue;
|
|
29
|
+
case 7: op = _.ops.pop(); _.trys.pop(); continue;
|
|
30
|
+
default:
|
|
31
|
+
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
|
|
32
|
+
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
|
|
33
|
+
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
|
|
34
|
+
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
|
|
35
|
+
if (t[2]) _.ops.pop();
|
|
36
|
+
_.trys.pop(); continue;
|
|
37
|
+
}
|
|
38
|
+
op = body.call(thisArg, _);
|
|
39
|
+
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
|
|
40
|
+
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
|
+
var node_telegram_bot_api_1 = require("node-telegram-bot-api");
|
|
45
|
+
var TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || '';
|
|
46
|
+
var TELEGRAM_TEST_CHAT_ID = process.env.TELEGRAM_TEST_CHAT_ID || '';
|
|
47
|
+
var TelegramGLMBot = /** @class */ (function () {
|
|
48
|
+
function TelegramGLMBot(token) {
|
|
49
|
+
this.bot = new node_telegram_bot_api_1.TelegramBot(token, { polling: true });
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Start the bot and register handlers
|
|
53
|
+
*/
|
|
54
|
+
TelegramGLMBot.prototype.start = function () {
|
|
55
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
56
|
+
var _this = this;
|
|
57
|
+
return __generator(this, function (_a) {
|
|
58
|
+
console.log('🤖 GLM Daemon Telegram Bot starting...');
|
|
59
|
+
// Handle /start command
|
|
60
|
+
this.bot.onText(/\/start/, function (msg) { return __awaiter(_this, void 0, void 0, function () {
|
|
61
|
+
var chatId;
|
|
62
|
+
return __generator(this, function (_a) {
|
|
63
|
+
switch (_a.label) {
|
|
64
|
+
case 0:
|
|
65
|
+
chatId = msg.chat.id;
|
|
66
|
+
return [4 /*yield*/, this.bot.sendMessage(chatId, '👋 Hello! I\'m the GLM Daemon Telegram Bot.\n\n' +
|
|
67
|
+
'I\'m currently in simple echo mode.\n' +
|
|
68
|
+
'Just send me a message and I\'ll echo it back!\n\n' +
|
|
69
|
+
'Full Butler AI personality coming soon!')];
|
|
70
|
+
case 1:
|
|
71
|
+
_a.sent();
|
|
72
|
+
return [2 /*return*/];
|
|
73
|
+
}
|
|
74
|
+
});
|
|
75
|
+
}); });
|
|
76
|
+
// Handle all text messages
|
|
77
|
+
this.bot.on('message', function (msg) { return __awaiter(_this, void 0, void 0, function () {
|
|
78
|
+
var chatId, userName, response;
|
|
79
|
+
var _a, _b;
|
|
80
|
+
return __generator(this, function (_c) {
|
|
81
|
+
switch (_c.label) {
|
|
82
|
+
case 0:
|
|
83
|
+
// Ignore non-text messages
|
|
84
|
+
if (!msg.text)
|
|
85
|
+
return [2 /*return*/];
|
|
86
|
+
// Ignore commands
|
|
87
|
+
if (msg.text.startsWith('/'))
|
|
88
|
+
return [2 /*return*/];
|
|
89
|
+
chatId = msg.chat.id;
|
|
90
|
+
userName = ((_a = msg.from) === null || _a === void 0 ? void 0 : _a.first_name) || ((_b = msg.from) === null || _b === void 0 ? void 0 : _b.username) || 'User';
|
|
91
|
+
console.log("[Telegram] ".concat(userName, ": ").concat(msg.text));
|
|
92
|
+
response = "\uD83D\uDCE2 You said: \"".concat(msg.text, "\"\n\n\u2705 Bot is working! Full AI responses coming soon.");
|
|
93
|
+
return [4 /*yield*/, this.bot.sendMessage(chatId, response)];
|
|
94
|
+
case 1:
|
|
95
|
+
_c.sent();
|
|
96
|
+
return [2 /*return*/];
|
|
97
|
+
}
|
|
98
|
+
});
|
|
99
|
+
}); });
|
|
100
|
+
// Handle polling errors
|
|
101
|
+
this.bot.on('polling_error', function (error) {
|
|
102
|
+
console.error('[Telegram] Polling error:', error);
|
|
103
|
+
});
|
|
104
|
+
console.log('✅ Telegram bot is running!');
|
|
105
|
+
return [2 /*return*/];
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
};
|
|
109
|
+
/**
|
|
110
|
+
* Send a test message to verify bot is working
|
|
111
|
+
*/
|
|
112
|
+
TelegramGLMBot.prototype.sendTestMessage = function (chatId) {
|
|
113
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
114
|
+
return __generator(this, function (_a) {
|
|
115
|
+
switch (_a.label) {
|
|
116
|
+
case 0: return [4 /*yield*/, this.bot.sendMessage(chatId, '✅ GLM Daemon Telegram Bot is NOW ONLINE!\n\n' +
|
|
117
|
+
'🎉 The bot is working correctly!\n\n' +
|
|
118
|
+
'Commands:\n' +
|
|
119
|
+
'/start - Show welcome message\n' +
|
|
120
|
+
'Any message - Echo test\n\n' +
|
|
121
|
+
'Full AI personality integration coming soon!')];
|
|
122
|
+
case 1:
|
|
123
|
+
_a.sent();
|
|
124
|
+
console.log("\u2705 Test message sent to chat ".concat(chatId));
|
|
125
|
+
return [2 /*return*/];
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
/**
|
|
131
|
+
* Stop the bot
|
|
132
|
+
*/
|
|
133
|
+
TelegramGLMBot.prototype.stop = function () {
|
|
134
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
135
|
+
return __generator(this, function (_a) {
|
|
136
|
+
console.log('🛑 Stopping Telegram bot...');
|
|
137
|
+
this.bot.stopPolling();
|
|
138
|
+
return [2 /*return*/];
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
};
|
|
142
|
+
return TelegramGLMBot;
|
|
143
|
+
}());
|
|
144
|
+
/**
|
|
145
|
+
* Main entry point
|
|
146
|
+
*/
|
|
147
|
+
function main() {
|
|
148
|
+
return __awaiter(this, void 0, void 0, function () {
|
|
149
|
+
var bot;
|
|
150
|
+
var _this = this;
|
|
151
|
+
return __generator(this, function (_a) {
|
|
152
|
+
switch (_a.label) {
|
|
153
|
+
case 0:
|
|
154
|
+
if (!TELEGRAM_BOT_TOKEN) {
|
|
155
|
+
console.error('❌ TELEGRAM_BOT_TOKEN not set');
|
|
156
|
+
console.error('Get your token from @BotFather on Telegram');
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
console.log("\uD83D\uDCF1 Bot token loaded: ".concat(TELEGRAM_BOT_TOKEN.substring(0, 10), "..."));
|
|
160
|
+
bot = new TelegramGLMBot(TELEGRAM_BOT_TOKEN);
|
|
161
|
+
return [4 /*yield*/, bot.start()];
|
|
162
|
+
case 1:
|
|
163
|
+
_a.sent();
|
|
164
|
+
if (!TELEGRAM_TEST_CHAT_ID) return [3 /*break*/, 3];
|
|
165
|
+
return [4 /*yield*/, bot.sendTestMessage(Number(TELEGRAM_TEST_CHAT_ID))];
|
|
166
|
+
case 2:
|
|
167
|
+
_a.sent();
|
|
168
|
+
return [3 /*break*/, 4];
|
|
169
|
+
case 3:
|
|
170
|
+
console.log('');
|
|
171
|
+
console.log('⚠️ No TELEGRAM_TEST_CHAT_ID set.');
|
|
172
|
+
console.log(' Send /start to your bot to test it!');
|
|
173
|
+
console.log(' Or get your chat ID from @userinfobot');
|
|
174
|
+
console.log('');
|
|
175
|
+
_a.label = 4;
|
|
176
|
+
case 4:
|
|
177
|
+
// Handle graceful shutdown
|
|
178
|
+
process.on('SIGINT', function () { return __awaiter(_this, void 0, void 0, function () {
|
|
179
|
+
return __generator(this, function (_a) {
|
|
180
|
+
switch (_a.label) {
|
|
181
|
+
case 0: return [4 /*yield*/, bot.stop()];
|
|
182
|
+
case 1:
|
|
183
|
+
_a.sent();
|
|
184
|
+
process.exit(0);
|
|
185
|
+
return [2 /*return*/];
|
|
186
|
+
}
|
|
187
|
+
});
|
|
188
|
+
}); });
|
|
189
|
+
process.on('SIGTERM', function () { return __awaiter(_this, void 0, void 0, function () {
|
|
190
|
+
return __generator(this, function (_a) {
|
|
191
|
+
switch (_a.label) {
|
|
192
|
+
case 0: return [4 /*yield*/, bot.stop()];
|
|
193
|
+
case 1:
|
|
194
|
+
_a.sent();
|
|
195
|
+
process.exit(0);
|
|
196
|
+
return [2 /*return*/];
|
|
197
|
+
}
|
|
198
|
+
});
|
|
199
|
+
}); });
|
|
200
|
+
return [2 /*return*/];
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
main().catch(console.error);
|
package/src/index.ts
CHANGED
|
@@ -1,14 +1,83 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* GLM Daemon Telegram Bot
|
|
3
3
|
*
|
|
4
|
-
*
|
|
4
|
+
* Full-featured Telegram bot with GLM-4.7 AI, git status, Doppler integration
|
|
5
|
+
*
|
|
6
|
+
* Features:
|
|
7
|
+
* - GLM-4.7 AI responses via Z.AI API
|
|
8
|
+
* - 429 rate limit handling with exponential backoff
|
|
9
|
+
* - /git command for git status + GitHub auth
|
|
10
|
+
* - /doppler command for Doppler secrets status
|
|
11
|
+
* - Rolling API key support
|
|
5
12
|
*/
|
|
6
13
|
|
|
7
14
|
import TelegramBot from 'node-telegram-bot-api';
|
|
15
|
+
import { execSync } from 'child_process';
|
|
8
16
|
|
|
9
17
|
const TELEGRAM_BOT_TOKEN = process.env.TELEGRAM_BOT_TOKEN || '';
|
|
10
18
|
const TELEGRAM_TEST_CHAT_ID = process.env.TELEGRAM_TEST_CHAT_ID || '';
|
|
11
19
|
|
|
20
|
+
// API Key Resolution - supports rolling keys
|
|
21
|
+
const getZAIKey = (): string | null => {
|
|
22
|
+
const keyValue = process.env.Z_AI_API_KEY || process.env.ANTHROPIC_API_KEYS;
|
|
23
|
+
if (!keyValue) return null;
|
|
24
|
+
try {
|
|
25
|
+
const keysArray = JSON.parse(keyValue);
|
|
26
|
+
if (Array.isArray(keysArray) && keysArray.length > 0) {
|
|
27
|
+
return keysArray[0];
|
|
28
|
+
}
|
|
29
|
+
} catch {
|
|
30
|
+
return keyValue;
|
|
31
|
+
}
|
|
32
|
+
return keyValue;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const ZAI_API_ENDPOINT = 'https://api.z.ai/api/coding/paas/v4/chat/completions';
|
|
36
|
+
|
|
37
|
+
// Retry helper with exponential backoff for 429 handling
|
|
38
|
+
const sleep = (ms: number) => new Promise(resolve => setTimeout(resolve, ms));
|
|
39
|
+
|
|
40
|
+
async function fetchWithRetry(
|
|
41
|
+
url: string,
|
|
42
|
+
options: RequestInit,
|
|
43
|
+
maxRetries = 3,
|
|
44
|
+
baseDelay = 1000
|
|
45
|
+
): Promise<Response> {
|
|
46
|
+
let lastError: Error | null = null;
|
|
47
|
+
|
|
48
|
+
for (let attempt = 0; attempt < maxRetries; attempt++) {
|
|
49
|
+
try {
|
|
50
|
+
const response = await fetch(url, options);
|
|
51
|
+
|
|
52
|
+
if (response.status === 429) {
|
|
53
|
+
const retryAfter = response.headers.get('Retry-After');
|
|
54
|
+
const delay = retryAfter
|
|
55
|
+
? parseInt(retryAfter) * 1000
|
|
56
|
+
: baseDelay * Math.pow(2, attempt);
|
|
57
|
+
|
|
58
|
+
console.log(`[429] Rate limited, retrying in ${delay}ms (attempt ${attempt + 1}/${maxRetries})`);
|
|
59
|
+
await sleep(delay);
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
if (!response.ok) {
|
|
64
|
+
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return response;
|
|
68
|
+
} catch (error) {
|
|
69
|
+
lastError = error as Error;
|
|
70
|
+
if (attempt < maxRetries - 1) {
|
|
71
|
+
const delay = baseDelay * Math.pow(2, attempt);
|
|
72
|
+
console.log(`[Retry] Attempt ${attempt + 1} failed: ${lastError.message}, retrying in ${delay}ms`);
|
|
73
|
+
await sleep(delay);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
throw lastError;
|
|
79
|
+
}
|
|
80
|
+
|
|
12
81
|
class TelegramGLMBot {
|
|
13
82
|
private bot: TelegramBot;
|
|
14
83
|
|
|
@@ -16,104 +85,299 @@ class TelegramGLMBot {
|
|
|
16
85
|
this.bot = new TelegramBot(token, { polling: true });
|
|
17
86
|
}
|
|
18
87
|
|
|
19
|
-
/**
|
|
20
|
-
* Start the bot and register handlers
|
|
21
|
-
*/
|
|
22
88
|
async start(): Promise<void> {
|
|
23
|
-
console.log('🤖 GLM Daemon Telegram Bot starting...');
|
|
89
|
+
console.log('🤖 GLM Daemon Telegram Bot starting with GLM-4.7...');
|
|
24
90
|
|
|
25
|
-
//
|
|
91
|
+
// /start command
|
|
26
92
|
this.bot.onText(/\/start/, async (msg: TelegramBot.Message) => {
|
|
27
93
|
const chatId = msg.chat.id;
|
|
28
94
|
await this.bot.sendMessage(
|
|
29
95
|
chatId,
|
|
30
|
-
'👋 Hello! I\'m
|
|
31
|
-
'
|
|
32
|
-
'
|
|
33
|
-
'
|
|
96
|
+
'👋 Hello! I\'m GLM Daemon Telegram Bot.\n\n' +
|
|
97
|
+
'🧠 Powered by GLM-4.7 via Z.AI API\n\n' +
|
|
98
|
+
'Commands:\n' +
|
|
99
|
+
'/start - Show this message\n' +
|
|
100
|
+
'/status - Check API status\n' +
|
|
101
|
+
'/git - Check git status & GitHub auth\n' +
|
|
102
|
+
'/doppler - Check Doppler config\n' +
|
|
103
|
+
'/help - Show help\n\n' +
|
|
104
|
+
'Or just send any message for AI assistance!'
|
|
105
|
+
);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
// /status command
|
|
109
|
+
this.bot.onText(/\/status/, async (msg: TelegramBot.Message) => {
|
|
110
|
+
const chatId = msg.chat.id;
|
|
111
|
+
const apiKey = getZAIKey();
|
|
112
|
+
const status = {
|
|
113
|
+
api: !!apiKey,
|
|
114
|
+
key: apiKey ? `${apiKey.slice(0, 10)}...` : 'None',
|
|
115
|
+
endpoint: ZAI_API_ENDPOINT
|
|
116
|
+
};
|
|
117
|
+
await this.bot.sendMessage(
|
|
118
|
+
chatId,
|
|
119
|
+
`📊 Bot Status:\n\n` +
|
|
120
|
+
`✅ API: ${status.api ? 'Connected' : 'Disconnected'}\n` +
|
|
121
|
+
`🔑 Key: ${status.key ? 'Configured' : 'Missing'}\n` +
|
|
122
|
+
`📍 Endpoint: ${status.endpoint}`
|
|
34
123
|
);
|
|
35
124
|
});
|
|
36
125
|
|
|
37
|
-
//
|
|
126
|
+
// /help command
|
|
127
|
+
this.bot.onText(/\/help/, async (msg: TelegramBot.Message) => {
|
|
128
|
+
const chatId = msg.chat.id;
|
|
129
|
+
await this.bot.sendMessage(
|
|
130
|
+
chatId,
|
|
131
|
+
'📚 *GLM Daemon Bot Help*\n\n' +
|
|
132
|
+
'🧠 Powered by GLM-4.7 via Z.AI API\n\n' +
|
|
133
|
+
'*Commands:*\n' +
|
|
134
|
+
'/start - Show welcome message\n' +
|
|
135
|
+
'/status - Check API status\n' +
|
|
136
|
+
'/git - Check git status & GitHub auth\n' +
|
|
137
|
+
'/git auth - Start GitHub auth flow\n' +
|
|
138
|
+
'/doppler - Check Doppler config\n' +
|
|
139
|
+
'/doppler secrets - List secret names\n' +
|
|
140
|
+
'/help - Show this help\n\n' +
|
|
141
|
+
'*Usage:*\n' +
|
|
142
|
+
'Just send any message and I\\\'ll respond with AI assistance!',
|
|
143
|
+
{ parse_mode: 'Markdown' }
|
|
144
|
+
);
|
|
145
|
+
});
|
|
146
|
+
|
|
147
|
+
// /git command
|
|
148
|
+
this.bot.onText(/\/git(?:\s+(.+))?/, async (msg: TelegramBot.Message, match: RegExpExecArray | null) => {
|
|
149
|
+
const chatId = msg.chat.id;
|
|
150
|
+
const subCommand = match?.[1]?.trim();
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
if (subCommand === 'auth') {
|
|
154
|
+
let statusMsg = 'GitHub Auth\n\n';
|
|
155
|
+
try {
|
|
156
|
+
const deviceResp = execSync('gh auth login --web --git-protocol https 2>&1 | head -20', {
|
|
157
|
+
encoding: 'utf-8',
|
|
158
|
+
timeout: 8000,
|
|
159
|
+
cwd: '/root'
|
|
160
|
+
});
|
|
161
|
+
const codeMatch = deviceResp.match(/verification code:\s*([A-Z0-9-]+)/i);
|
|
162
|
+
const urlMatch = deviceResp.match(/https:\/\/github\.com\/login\/device[^\s]*/);
|
|
163
|
+
|
|
164
|
+
if (codeMatch && urlMatch) {
|
|
165
|
+
statusMsg += `Code: ${codeMatch[1]}\n`;
|
|
166
|
+
statusMsg += `URL: ${urlMatch[0]}\n\n`;
|
|
167
|
+
statusMsg += `Open URL and enter code to authenticate.`;
|
|
168
|
+
} else {
|
|
169
|
+
statusMsg += deviceResp.slice(0, 500);
|
|
170
|
+
}
|
|
171
|
+
} catch (e) {
|
|
172
|
+
statusMsg += `Error: ${(e as Error).message}`;
|
|
173
|
+
}
|
|
174
|
+
await this.bot.sendMessage(chatId, statusMsg);
|
|
175
|
+
} else {
|
|
176
|
+
let statusMsg = 'Git Status\n\n';
|
|
177
|
+
|
|
178
|
+
try {
|
|
179
|
+
const authStatus = execSync('gh auth status 2>&1', { encoding: 'utf-8', cwd: '/root' });
|
|
180
|
+
const loggedIn = authStatus.includes('Logged in');
|
|
181
|
+
const accountMatch = authStatus.match(/account\s+(\w+)/i);
|
|
182
|
+
|
|
183
|
+
statusMsg += `GitHub: ${loggedIn ? 'Connected' : 'Not logged in'}\n`;
|
|
184
|
+
if (accountMatch) statusMsg += `Account: ${accountMatch[1]}\n`;
|
|
185
|
+
if (!loggedIn) statusMsg += `\nUse /git auth to authenticate`;
|
|
186
|
+
} catch {
|
|
187
|
+
statusMsg += `GitHub: Not authenticated\n`;
|
|
188
|
+
statusMsg += `Use /git auth to login\n`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
try {
|
|
192
|
+
const branch = execSync('git rev-parse --abbrev-ref HEAD 2>/dev/null', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
193
|
+
const ahead = execSync('git rev-list --count @{upstream}..HEAD 2>/dev/null || echo 0', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
194
|
+
const dirty = execSync('git status --porcelain 2>/dev/null | wc -l', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
195
|
+
|
|
196
|
+
statusMsg += `\nRepo:\n`;
|
|
197
|
+
statusMsg += `Branch: ${branch}\n`;
|
|
198
|
+
statusMsg += `Ahead: ${ahead} commits\n`;
|
|
199
|
+
statusMsg += `Modified: ${dirty} files`;
|
|
200
|
+
} catch {
|
|
201
|
+
statusMsg += `\nRepo: Not a git repo or error`;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
await this.bot.sendMessage(chatId, statusMsg);
|
|
205
|
+
}
|
|
206
|
+
} catch (error) {
|
|
207
|
+
await this.bot.sendMessage(chatId, `Error: ${(error as Error).message}`);
|
|
208
|
+
}
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
// /doppler command
|
|
212
|
+
this.bot.onText(/\/doppler(?:\s+(.+))?/, async (msg: TelegramBot.Message, match: RegExpExecArray | null) => {
|
|
213
|
+
const chatId = msg.chat.id;
|
|
214
|
+
const subCommand = match?.[1]?.trim();
|
|
215
|
+
|
|
216
|
+
try {
|
|
217
|
+
if (subCommand === 'secrets') {
|
|
218
|
+
let statusMsg = 'Doppler Secrets\n\n';
|
|
219
|
+
try {
|
|
220
|
+
const secrets = execSync('doppler secrets --plain 2>&1 | cut -d\'\\t\' -f1', {
|
|
221
|
+
encoding: 'utf-8',
|
|
222
|
+
cwd: '/root'
|
|
223
|
+
});
|
|
224
|
+
const secretList = secrets.trim().split('\n').filter(s => s.length > 0);
|
|
225
|
+
statusMsg += `Found ${secretList.length} secrets:\n\n`;
|
|
226
|
+
statusMsg += secretList.slice(0, 20).join('\n');
|
|
227
|
+
if (secretList.length > 20) {
|
|
228
|
+
statusMsg += `\n... and ${secretList.length - 20} more`;
|
|
229
|
+
}
|
|
230
|
+
} catch (e) {
|
|
231
|
+
statusMsg += `Error: ${(e as Error).message}`;
|
|
232
|
+
}
|
|
233
|
+
await this.bot.sendMessage(chatId, statusMsg);
|
|
234
|
+
} else {
|
|
235
|
+
let statusMsg = 'Doppler Status\n\n';
|
|
236
|
+
|
|
237
|
+
try {
|
|
238
|
+
const project = execSync('doppler configure get project 2>/dev/null', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
239
|
+
const config = execSync('doppler configure get config 2>/dev/null', { encoding: 'utf-8', cwd: '/root' }).trim();
|
|
240
|
+
|
|
241
|
+
statusMsg += `Project: ${project || 'Not set'}\n`;
|
|
242
|
+
statusMsg += `Config: ${config || 'Not set'}\n`;
|
|
243
|
+
|
|
244
|
+
const tokenCheck = execSync('doppler me 2>&1 | head -5', { encoding: 'utf-8', cwd: '/root' });
|
|
245
|
+
if (tokenCheck.includes('email') || tokenCheck.includes('name')) {
|
|
246
|
+
statusMsg += `Token: Valid\n`;
|
|
247
|
+
} else {
|
|
248
|
+
statusMsg += `Token: Not configured\n`;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
statusMsg += `\nCommands:\n`;
|
|
252
|
+
statusMsg += `/doppler secrets - List secret names`;
|
|
253
|
+
} catch {
|
|
254
|
+
statusMsg += `Not configured\n\n`;
|
|
255
|
+
statusMsg += `Run: doppler setup`;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
await this.bot.sendMessage(chatId, statusMsg);
|
|
259
|
+
}
|
|
260
|
+
} catch (error) {
|
|
261
|
+
await this.bot.sendMessage(chatId, `Error: ${(error as Error).message}`);
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
// Handle all text messages with GLM-4.7
|
|
38
266
|
this.bot.on('message', async (msg: TelegramBot.Message) => {
|
|
39
|
-
// Ignore non-text messages
|
|
40
267
|
if (!msg.text) return;
|
|
41
|
-
|
|
42
|
-
// Ignore commands
|
|
43
268
|
if (msg.text.startsWith('/')) return;
|
|
44
269
|
|
|
45
270
|
const chatId = msg.chat.id;
|
|
46
271
|
const userName = msg.from?.first_name || msg.from?.username || 'User';
|
|
47
272
|
|
|
48
273
|
console.log(`[Telegram] ${userName}: ${msg.text}`);
|
|
274
|
+
await this.bot.sendChatAction(chatId, 'typing');
|
|
49
275
|
|
|
50
|
-
|
|
51
|
-
const response = `📢 You said: "${msg.text}"\n\n✅ Bot is working! Full AI responses coming soon.`;
|
|
52
|
-
|
|
276
|
+
const response = await this.getGLMResponse(msg.text, userName);
|
|
53
277
|
await this.bot.sendMessage(chatId, response);
|
|
54
278
|
});
|
|
55
279
|
|
|
56
|
-
//
|
|
280
|
+
// Polling errors
|
|
57
281
|
this.bot.on('polling_error', (error: Error) => {
|
|
58
|
-
console.error('[Telegram] Polling error:', error);
|
|
282
|
+
console.error('[Telegram] Polling error:', error.message);
|
|
59
283
|
});
|
|
60
284
|
|
|
61
|
-
console.log('✅ Telegram bot is running!');
|
|
285
|
+
console.log('✅ Telegram bot is running with GLM-4.7!');
|
|
286
|
+
|
|
287
|
+
if (TELEGRAM_TEST_CHAT_ID) {
|
|
288
|
+
await this.sendTestMessage(Number(TELEGRAM_TEST_CHAT_ID));
|
|
289
|
+
} else {
|
|
290
|
+
console.log('\n💡 Tip: Get your chat ID from @userinfobot and set TELEGRAM_TEST_CHAT_ID');
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
async getGLMResponse(userMessage: string, userName: string): Promise<string> {
|
|
295
|
+
const apiKey = getZAIKey();
|
|
296
|
+
if (!apiKey) {
|
|
297
|
+
return '⚠️ Z_AI_API_KEY not configured in Doppler secrets.\n\nPlease set Z_AI_API_KEY to use GLM-4.7.';
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
try {
|
|
301
|
+
console.log(`🔄 Calling GLM-4.7 API for: ${userMessage}`);
|
|
302
|
+
|
|
303
|
+
const response = await fetchWithRetry(ZAI_API_ENDPOINT, {
|
|
304
|
+
method: 'POST',
|
|
305
|
+
headers: {
|
|
306
|
+
'Content-Type': 'application/json',
|
|
307
|
+
'Authorization': `Bearer ${apiKey}`
|
|
308
|
+
},
|
|
309
|
+
body: JSON.stringify({
|
|
310
|
+
model: 'glm-4.7',
|
|
311
|
+
messages: [
|
|
312
|
+
{
|
|
313
|
+
role: 'system',
|
|
314
|
+
content: 'You are GLM Daemon, an autonomous AI assistant that helps with coding tasks, questions, and feature development. You are running as part of a distributed system with Telegram and Discord channels. Be helpful, concise, and technical.'
|
|
315
|
+
},
|
|
316
|
+
{ role: 'user', content: userMessage }
|
|
317
|
+
],
|
|
318
|
+
temperature: 0.7,
|
|
319
|
+
max_tokens: 2048
|
|
320
|
+
})
|
|
321
|
+
}, 3, 1000);
|
|
322
|
+
|
|
323
|
+
const data = await response.json() as { choices?: Array<{ message?: { content?: string } }> };
|
|
324
|
+
|
|
325
|
+
if (data.choices?.[0]?.message?.content) {
|
|
326
|
+
const aiResponse = data.choices[0].message.content;
|
|
327
|
+
console.log(`✅ GLM-4.7 response: ${aiResponse.slice(0, 100)}...`);
|
|
328
|
+
return aiResponse;
|
|
329
|
+
} else {
|
|
330
|
+
console.error('❌ Unexpected API response format:', data);
|
|
331
|
+
return '❌ Unexpected response from AI API.';
|
|
332
|
+
}
|
|
333
|
+
} catch (error) {
|
|
334
|
+
console.error('❌ Error calling GLM-4.7:', (error as Error).message);
|
|
335
|
+
|
|
336
|
+
if ((error as Error).message.includes('429')) {
|
|
337
|
+
return '⚠️ Rate limited by API. Please try again in a moment.';
|
|
338
|
+
}
|
|
339
|
+
return `❌ Error: ${(error as Error).message}`;
|
|
340
|
+
}
|
|
62
341
|
}
|
|
63
342
|
|
|
64
|
-
/**
|
|
65
|
-
* Send a test message to verify bot is working
|
|
66
|
-
*/
|
|
67
343
|
async sendTestMessage(chatId: number): Promise<void> {
|
|
344
|
+
const apiKey = getZAIKey();
|
|
68
345
|
await this.bot.sendMessage(
|
|
69
346
|
chatId,
|
|
70
347
|
'✅ GLM Daemon Telegram Bot is NOW ONLINE!\n\n' +
|
|
71
|
-
'🎉
|
|
348
|
+
'🎉 Connected from seed-node-prod!\n\n' +
|
|
349
|
+
`🧠 AI: ${apiKey ? 'GLM-4.7 via Z.AI' : 'Not configured'}\n\n` +
|
|
72
350
|
'Commands:\n' +
|
|
73
351
|
'/start - Show welcome message\n' +
|
|
74
|
-
'
|
|
75
|
-
'
|
|
352
|
+
'/status - Check API status\n' +
|
|
353
|
+
'/git - Git & GitHub status\n' +
|
|
354
|
+
'/doppler - Doppler config\n' +
|
|
355
|
+
'/help - Show all commands\n' +
|
|
356
|
+
'Any message - Chat with GLM-4.7 AI'
|
|
76
357
|
);
|
|
77
358
|
console.log(`✅ Test message sent to chat ${chatId}`);
|
|
78
359
|
}
|
|
79
360
|
|
|
80
|
-
/**
|
|
81
|
-
* Stop the bot
|
|
82
|
-
*/
|
|
83
361
|
async stop(): Promise<void> {
|
|
84
362
|
console.log('🛑 Stopping Telegram bot...');
|
|
85
363
|
this.bot.stopPolling();
|
|
86
364
|
}
|
|
87
365
|
}
|
|
88
366
|
|
|
89
|
-
|
|
90
|
-
* Main entry point
|
|
91
|
-
*/
|
|
367
|
+
// Main entry point
|
|
92
368
|
async function main() {
|
|
93
369
|
if (!TELEGRAM_BOT_TOKEN) {
|
|
94
|
-
console.error('❌ TELEGRAM_BOT_TOKEN not
|
|
95
|
-
console.error('
|
|
370
|
+
console.error('❌ TELEGRAM_BOT_TOKEN not found in Doppler secrets');
|
|
371
|
+
console.error('Make sure to set in your seed/prd Doppler project');
|
|
96
372
|
process.exit(1);
|
|
97
373
|
}
|
|
98
374
|
|
|
99
375
|
console.log(`📱 Bot token loaded: ${TELEGRAM_BOT_TOKEN.substring(0, 10)}...`);
|
|
376
|
+
console.log(`🤖 Bot name: @SimulationapiBot`);
|
|
100
377
|
|
|
101
|
-
// Create and start bot
|
|
102
378
|
const bot = new TelegramGLMBot(TELEGRAM_BOT_TOKEN);
|
|
103
379
|
await bot.start();
|
|
104
380
|
|
|
105
|
-
// Send test message if chat ID is provided
|
|
106
|
-
if (TELEGRAM_TEST_CHAT_ID) {
|
|
107
|
-
await bot.sendTestMessage(Number(TELEGRAM_TEST_CHAT_ID));
|
|
108
|
-
} else {
|
|
109
|
-
console.log('');
|
|
110
|
-
console.log('⚠️ No TELEGRAM_TEST_CHAT_ID set.');
|
|
111
|
-
console.log(' Send /start to your bot to test it!');
|
|
112
|
-
console.log(' Or get your chat ID from @userinfobot');
|
|
113
|
-
console.log('');
|
|
114
|
-
}
|
|
115
|
-
|
|
116
|
-
// Handle graceful shutdown
|
|
117
381
|
process.on('SIGINT', async () => {
|
|
118
382
|
await bot.stop();
|
|
119
383
|
process.exit(0);
|
|
@@ -126,3 +390,6 @@ async function main() {
|
|
|
126
390
|
}
|
|
127
391
|
|
|
128
392
|
main().catch(console.error);
|
|
393
|
+
|
|
394
|
+
// Export for use as module
|
|
395
|
+
export { TelegramGLMBot };
|
package/CLAUDE.md
DELETED
|
@@ -1,106 +0,0 @@
|
|
|
1
|
-
|
|
2
|
-
Default to using Bun instead of Node.js.
|
|
3
|
-
|
|
4
|
-
- Use `bun <file>` instead of `node <file>` or `ts-node <file>`
|
|
5
|
-
- Use `bun test` instead of `jest` or `vitest`
|
|
6
|
-
- Use `bun build <file.html|file.ts|file.css>` instead of `webpack` or `esbuild`
|
|
7
|
-
- Use `bun install` instead of `npm install` or `yarn install` or `pnpm install`
|
|
8
|
-
- Use `bun run <script>` instead of `npm run <script>` or `yarn run <script>` or `pnpm run <script>`
|
|
9
|
-
- Use `bunx <package> <command>` instead of `npx <package> <command>`
|
|
10
|
-
- Bun automatically loads .env, so don't use dotenv.
|
|
11
|
-
|
|
12
|
-
## APIs
|
|
13
|
-
|
|
14
|
-
- `Bun.serve()` supports WebSockets, HTTPS, and routes. Don't use `express`.
|
|
15
|
-
- `bun:sqlite` for SQLite. Don't use `better-sqlite3`.
|
|
16
|
-
- `Bun.redis` for Redis. Don't use `ioredis`.
|
|
17
|
-
- `Bun.sql` for Postgres. Don't use `pg` or `postgres.js`.
|
|
18
|
-
- `WebSocket` is built-in. Don't use `ws`.
|
|
19
|
-
- Prefer `Bun.file` over `node:fs`'s readFile/writeFile
|
|
20
|
-
- Bun.$`ls` instead of execa.
|
|
21
|
-
|
|
22
|
-
## Testing
|
|
23
|
-
|
|
24
|
-
Use `bun test` to run tests.
|
|
25
|
-
|
|
26
|
-
```ts#index.test.ts
|
|
27
|
-
import { test, expect } from "bun:test";
|
|
28
|
-
|
|
29
|
-
test("hello world", () => {
|
|
30
|
-
expect(1).toBe(1);
|
|
31
|
-
});
|
|
32
|
-
```
|
|
33
|
-
|
|
34
|
-
## Frontend
|
|
35
|
-
|
|
36
|
-
Use HTML imports with `Bun.serve()`. Don't use `vite`. HTML imports fully support React, CSS, Tailwind.
|
|
37
|
-
|
|
38
|
-
Server:
|
|
39
|
-
|
|
40
|
-
```ts#index.ts
|
|
41
|
-
import index from "./index.html"
|
|
42
|
-
|
|
43
|
-
Bun.serve({
|
|
44
|
-
routes: {
|
|
45
|
-
"/": index,
|
|
46
|
-
"/api/users/:id": {
|
|
47
|
-
GET: (req) => {
|
|
48
|
-
return new Response(JSON.stringify({ id: req.params.id }));
|
|
49
|
-
},
|
|
50
|
-
},
|
|
51
|
-
},
|
|
52
|
-
// optional websocket support
|
|
53
|
-
websocket: {
|
|
54
|
-
open: (ws) => {
|
|
55
|
-
ws.send("Hello, world!");
|
|
56
|
-
},
|
|
57
|
-
message: (ws, message) => {
|
|
58
|
-
ws.send(message);
|
|
59
|
-
},
|
|
60
|
-
close: (ws) => {
|
|
61
|
-
// handle close
|
|
62
|
-
}
|
|
63
|
-
},
|
|
64
|
-
development: {
|
|
65
|
-
hmr: true,
|
|
66
|
-
console: true,
|
|
67
|
-
}
|
|
68
|
-
})
|
|
69
|
-
```
|
|
70
|
-
|
|
71
|
-
HTML files can import .tsx, .jsx or .js files directly and Bun's bundler will transpile & bundle automatically. `<link>` tags can point to stylesheets and Bun's CSS bundler will bundle.
|
|
72
|
-
|
|
73
|
-
```html#index.html
|
|
74
|
-
<html>
|
|
75
|
-
<body>
|
|
76
|
-
<h1>Hello, world!</h1>
|
|
77
|
-
<script type="module" src="./frontend.tsx"></script>
|
|
78
|
-
</body>
|
|
79
|
-
</html>
|
|
80
|
-
```
|
|
81
|
-
|
|
82
|
-
With the following `frontend.tsx`:
|
|
83
|
-
|
|
84
|
-
```tsx#frontend.tsx
|
|
85
|
-
import React from "react";
|
|
86
|
-
import { createRoot } from "react-dom/client";
|
|
87
|
-
|
|
88
|
-
// import .css files directly and it works
|
|
89
|
-
import './index.css';
|
|
90
|
-
|
|
91
|
-
const root = createRoot(document.body);
|
|
92
|
-
|
|
93
|
-
export default function Frontend() {
|
|
94
|
-
return <h1>Hello, world!</h1>;
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
root.render(<Frontend />);
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
Then, run index.ts
|
|
101
|
-
|
|
102
|
-
```sh
|
|
103
|
-
bun --hot ./index.ts
|
|
104
|
-
```
|
|
105
|
-
|
|
106
|
-
For more information, read the Bun API docs in `node_modules/bun-types/docs/**.mdx`.
|
package/TICKET.md
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
# Deployment Ticket: GLM Daemon Telegram Bot
|
|
2
|
-
|
|
3
|
-
## Summary
|
|
4
|
-
Deploy the GLM Daemon Telegram bot to Hetzner VPS
|
|
5
|
-
|
|
6
|
-
## VPS Details
|
|
7
|
-
- **IP**: 46.225.83.174
|
|
8
|
-
- **Name**: workflow-daemon
|
|
9
|
-
- **Specs**: cax11 (2 vCPU, 4GB RAM)
|
|
10
|
-
- **Location**: nbg1
|
|
11
|
-
- **Status**: Running
|
|
12
|
-
|
|
13
|
-
## Requirements
|
|
14
|
-
1. Install dependencies (bun install)
|
|
15
|
-
2. Build TypeScript (bun run build)
|
|
16
|
-
3. Configure Doppler secrets (TELEGRAM_BOT_TOKEN, GLM_API_KEY)
|
|
17
|
-
4. Start the bot (bun run start)
|
|
18
|
-
5. Set up systemd service for persistent operation
|
|
19
|
-
|
|
20
|
-
## Known Issues
|
|
21
|
-
- **Terminal MCP has recursive loop** - Cannot use mcp__terminal__exec_ssh
|
|
22
|
-
- Use direct SSH or alternative deployment method
|
|
23
|
-
- Package structure is ready at `/packages/src/glm-daemon-telegram/`
|
|
24
|
-
|
|
25
|
-
## Files Created
|
|
26
|
-
- `package.json` - Dependencies (node-telegram-bot-api, doppler)
|
|
27
|
-
- `src/index.ts` - Telegram bot with ButlerAgent
|
|
28
|
-
- `deploy.sh` - Deployment script
|
|
29
|
-
- `run.sh` - Quick run script
|
|
30
|
-
- `tsconfig.json` - TypeScript config
|
|
31
|
-
- `README.md` - Setup documentation
|
|
32
|
-
|
|
33
|
-
## Doppler Secrets Required
|
|
34
|
-
- `TELEGRAM_BOT_TOKEN` - Telegram bot token from @BotFather
|
|
35
|
-
- `TELEGRAM_TEST_CHAT_ID` - Your Telegram chat ID (optional)
|
|
36
|
-
- `GLM_API_KEY` - GLM 4.7 API key (or ANTHROPIC_API_KEY)
|
|
37
|
-
- `DOPPLER_TOKEN` - Doppler access token
|
|
38
|
-
|
|
39
|
-
## Success Criteria
|
|
40
|
-
- Bot connects to Telegram
|
|
41
|
-
- Sends test message on startup
|
|
42
|
-
- Responds to messages with Butler personality
|
|
43
|
-
- Persistent operation via systemd
|
|
44
|
-
|
|
45
|
-
## First Steps
|
|
46
|
-
1. Build locally: `bun run build`
|
|
47
|
-
2. Test locally: `doppler run -- bun run start`
|
|
48
|
-
3. Should see test message in Telegram
|
|
49
|
-
4. Then deploy to VPS
|
|
50
|
-
|
|
51
|
-
## Created
|
|
52
|
-
2026-02-06 - Initial package structure
|
|
53
|
-
|
|
54
|
-
## Priority
|
|
55
|
-
HIGH - Ready for deployment once Terminal MCP is fixed
|
package/deploy.sh
DELETED
|
@@ -1,26 +0,0 @@
|
|
|
1
|
-
#!/bin/bash
|
|
2
|
-
set -e
|
|
3
|
-
|
|
4
|
-
echo "🚀 Deploying GLM Daemon Telegram Bot to Hetzner..."
|
|
5
|
-
|
|
6
|
-
# Load secrets from Doppler
|
|
7
|
-
echo "📦 Loading secrets from Doppler..."
|
|
8
|
-
eval "$(doppler run --print-envs --token=$DOPPLER_TOKEN | grep -E '^(TELEGRAM_BOT_TOKEN|TELEGRAM_TEST_CHAT_ID|GLM_API_KEY|DOPPLER_TOKEN)=')"
|
|
9
|
-
|
|
10
|
-
# Check required secrets
|
|
11
|
-
if [[ -z "$TELEGRAM_BOT_TOKEN" ]]; then
|
|
12
|
-
echo "❌ TELEGRAM_BOT_TOKEN not set"
|
|
13
|
-
exit 1
|
|
14
|
-
fi
|
|
15
|
-
|
|
16
|
-
# Install dependencies
|
|
17
|
-
echo "📥 Installing dependencies..."
|
|
18
|
-
bun install
|
|
19
|
-
|
|
20
|
-
# Build TypeScript
|
|
21
|
-
echo "🔨 Building..."
|
|
22
|
-
bun run build
|
|
23
|
-
|
|
24
|
-
# Start bot
|
|
25
|
-
echo "🤖 Starting Telegram bot..."
|
|
26
|
-
bun run start
|
package/run.sh
DELETED
package/tsconfig.json
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"compilerOptions": {
|
|
3
|
-
"target": "ES2022",
|
|
4
|
-
"module": "ESNext",
|
|
5
|
-
"lib": ["ES2022"],
|
|
6
|
-
"moduleResolution": "bundler",
|
|
7
|
-
"outDir": "./dist",
|
|
8
|
-
"rootDir": "./src",
|
|
9
|
-
"declaration": true,
|
|
10
|
-
"declarationMap": true,
|
|
11
|
-
"sourceMap": true,
|
|
12
|
-
"esModuleInterop": true,
|
|
13
|
-
"skipLibCheck": true,
|
|
14
|
-
"strict": true
|
|
15
|
-
},
|
|
16
|
-
"include": ["src/**/*"],
|
|
17
|
-
"exclude": ["node_modules", "dist"]
|
|
18
|
-
}
|