@ebowwa/daemons 0.5.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/README.md +264 -0
- package/dist/bin/discord-cli.js +124118 -0
- package/dist/bin/manager.js +143 -0
- package/dist/bin/telegram-cli.js +124114 -0
- package/dist/index.js +125340 -0
- package/package.json +94 -0
- package/src/agent.ts +111 -0
- package/src/channels/base.ts +573 -0
- package/src/channels/discord.ts +306 -0
- package/src/channels/index.ts +169 -0
- package/src/channels/telegram.ts +315 -0
- package/src/daemon.ts +534 -0
- package/src/hooks.ts +97 -0
- package/src/index.ts +111 -0
- package/src/memory.ts +369 -0
- package/src/skills/coding/commit.ts +202 -0
- package/src/skills/coding/execute-subtask.ts +136 -0
- package/src/skills/coding/fix-issues.ts +126 -0
- package/src/skills/coding/index.ts +26 -0
- package/src/skills/coding/plan-task.ts +158 -0
- package/src/skills/coding/quality-check.ts +155 -0
- package/src/skills/index.ts +65 -0
- package/src/skills/registry.ts +380 -0
- package/src/skills/shared/index.ts +21 -0
- package/src/skills/shared/reflect.ts +156 -0
- package/src/skills/shared/review.ts +201 -0
- package/src/skills/shared/trajectory.ts +319 -0
- package/src/skills/trading/analyze-market.ts +144 -0
- package/src/skills/trading/check-risk.ts +176 -0
- package/src/skills/trading/execute-trade.ts +185 -0
- package/src/skills/trading/generate-signal.ts +160 -0
- package/src/skills/trading/index.ts +26 -0
- package/src/skills/trading/monitor-position.ts +179 -0
- package/src/skills/types.ts +235 -0
- package/src/skills/workflows.ts +340 -0
- package/src/state.ts +77 -0
- package/src/tools.ts +134 -0
- package/src/types.ts +314 -0
- package/src/workflow.ts +341 -0
- package/src/workflows/coding.ts +580 -0
- package/src/workflows/index.ts +61 -0
- package/src/workflows/trading.ts +608 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram Channel for GLM Daemon
|
|
3
|
+
*
|
|
4
|
+
* Wraps @ebowwa/channel-telegram (pure protocol adapter)
|
|
5
|
+
* with GLM Daemon intelligence (GLM client, daemon delegation, routing).
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import {
|
|
9
|
+
type ChannelId,
|
|
10
|
+
type ChannelCapabilities,
|
|
11
|
+
} from "@ebowwa/channel-types";
|
|
12
|
+
import {
|
|
13
|
+
TelegramChannel as TelegramProtocolAdapter,
|
|
14
|
+
type TelegramConfig as ProtocolConfig,
|
|
15
|
+
// Telegram target normalization utilities
|
|
16
|
+
normalizeTelegramMessagingTarget,
|
|
17
|
+
looksLikeTelegramTargetId,
|
|
18
|
+
extractRawTarget,
|
|
19
|
+
isUsernameTarget,
|
|
20
|
+
isNumericIdTarget,
|
|
21
|
+
} from "@ebowwa/channel-telegram";
|
|
22
|
+
import {
|
|
23
|
+
BaseChannel,
|
|
24
|
+
type GLMChannelConfig,
|
|
25
|
+
} from "./base.js";
|
|
26
|
+
|
|
27
|
+
// Re-export normalization utilities for consumers
|
|
28
|
+
export {
|
|
29
|
+
normalizeTelegramMessagingTarget,
|
|
30
|
+
looksLikeTelegramTargetId,
|
|
31
|
+
extractRawTarget,
|
|
32
|
+
isUsernameTarget,
|
|
33
|
+
isNumericIdTarget,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
// ============================================================
|
|
37
|
+
// TELEGRAM CHANNEL CONFIG
|
|
38
|
+
// ============================================================
|
|
39
|
+
|
|
40
|
+
export interface TelegramChannelConfig extends GLMChannelConfig {
|
|
41
|
+
platform: "telegram";
|
|
42
|
+
/** Bot token from @BotFather */
|
|
43
|
+
botToken: string;
|
|
44
|
+
/** Test chat ID for startup notification */
|
|
45
|
+
testChatId?: string;
|
|
46
|
+
/** Allowed user IDs (empty = all) */
|
|
47
|
+
allowedUsers?: number[];
|
|
48
|
+
/** Allowed chat IDs (empty = all) */
|
|
49
|
+
allowedChats?: number[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Legacy type alias for backwards compat
|
|
53
|
+
export type { TelegramChannelConfig as TelegramConfig };
|
|
54
|
+
|
|
55
|
+
// ============================================================
|
|
56
|
+
// TELEGRAM CHANNEL (Wraps @ebowwa/channel-telegram)
|
|
57
|
+
// ============================================================
|
|
58
|
+
|
|
59
|
+
export class GLMTelegramChannel extends BaseChannel {
|
|
60
|
+
readonly id: ChannelId;
|
|
61
|
+
readonly label = "Telegram";
|
|
62
|
+
readonly capabilities: ChannelCapabilities = {
|
|
63
|
+
supports: {
|
|
64
|
+
text: true,
|
|
65
|
+
media: true,
|
|
66
|
+
replies: true,
|
|
67
|
+
threads: false,
|
|
68
|
+
reactions: true,
|
|
69
|
+
editing: true,
|
|
70
|
+
streaming: false,
|
|
71
|
+
},
|
|
72
|
+
media: {
|
|
73
|
+
maxFileSize: 50 * 1024 * 1024, // 50MB
|
|
74
|
+
supportedMimeTypes: ["image/*", "video/*", "audio/*", "application/pdf"],
|
|
75
|
+
},
|
|
76
|
+
rateLimits: {
|
|
77
|
+
messagesPerMinute: 30,
|
|
78
|
+
charactersPerMessage: 4096,
|
|
79
|
+
},
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
private protocolAdapter: TelegramProtocolAdapter;
|
|
83
|
+
private telegramConfig: {
|
|
84
|
+
botToken: string;
|
|
85
|
+
testChatId?: string;
|
|
86
|
+
allowedUsers?: number[];
|
|
87
|
+
allowedChats?: number[];
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
constructor(config: TelegramChannelConfig) {
|
|
91
|
+
super(config);
|
|
92
|
+
|
|
93
|
+
this.telegramConfig = {
|
|
94
|
+
botToken: config.botToken,
|
|
95
|
+
testChatId: config.testChatId,
|
|
96
|
+
allowedUsers: config.allowedUsers,
|
|
97
|
+
allowedChats: config.allowedChats,
|
|
98
|
+
};
|
|
99
|
+
|
|
100
|
+
this.id = this.createId();
|
|
101
|
+
this.protocolAdapter = new TelegramProtocolAdapter(this.telegramConfig);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// ============================================================
|
|
105
|
+
// ChannelConnector Implementation
|
|
106
|
+
// ============================================================
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* Send response to Telegram via protocol adapter
|
|
110
|
+
*/
|
|
111
|
+
async send(response: Parameters<TelegramProtocolAdapter['send']>[0]): Promise<void> {
|
|
112
|
+
await this.protocolAdapter.send(response);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ============================================================
|
|
116
|
+
// Platform-Specific Implementation (BaseChannel abstract methods)
|
|
117
|
+
// ============================================================
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Start Telegram bot via protocol adapter
|
|
121
|
+
*/
|
|
122
|
+
protected async startPlatform(): Promise<void> {
|
|
123
|
+
console.log("[TelegramChannel] Starting via protocol adapter...");
|
|
124
|
+
|
|
125
|
+
// Connect protocol adapter to our routing logic
|
|
126
|
+
this.protocolAdapter.onMessage(async (message) => {
|
|
127
|
+
// Route through BaseChannel's routing logic (GLM + daemon)
|
|
128
|
+
return this.routeChannelMessage(message);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
await this.protocolAdapter.start();
|
|
132
|
+
console.log("[TelegramChannel] Protocol adapter started");
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Stop Telegram bot via protocol adapter
|
|
137
|
+
*/
|
|
138
|
+
protected async stopPlatform(): Promise<void> {
|
|
139
|
+
console.log("[TelegramChannel] Stopping protocol adapter...");
|
|
140
|
+
await this.protocolAdapter.stop();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// ============================================================
|
|
144
|
+
// Telegram-Specific Helpers (delegated to protocol adapter)
|
|
145
|
+
// ============================================================
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Get underlying bot instance for advanced operations
|
|
149
|
+
*/
|
|
150
|
+
getBot() {
|
|
151
|
+
return this.protocolAdapter.getBot();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Send a simple message
|
|
156
|
+
*/
|
|
157
|
+
async sendMessage(chatId: number, text: string) {
|
|
158
|
+
await this.protocolAdapter.sendMessage(chatId, text);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Start typing indicator
|
|
163
|
+
*/
|
|
164
|
+
startTypingIndicator(chatId: number) {
|
|
165
|
+
this.protocolAdapter.startTypingIndicator(chatId);
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Stop typing indicator
|
|
170
|
+
*/
|
|
171
|
+
stopTypingIndicator(chatId: number) {
|
|
172
|
+
this.protocolAdapter.stopTypingIndicator(chatId);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Check if user/chat is allowed
|
|
177
|
+
*/
|
|
178
|
+
isAllowed(userId?: number, chatId?: number): boolean {
|
|
179
|
+
return this.protocolAdapter.isAllowed(userId, chatId);
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// ============================================================
|
|
183
|
+
// Target Normalization Helpers
|
|
184
|
+
// ============================================================
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Normalize a Telegram target (username, URL, or ID) to standard format.
|
|
188
|
+
*
|
|
189
|
+
* @param raw - Raw input (@username, t.me/user, 123456, etc.)
|
|
190
|
+
* @returns Normalized target or undefined if invalid
|
|
191
|
+
*
|
|
192
|
+
* @example
|
|
193
|
+
* channel.normalizeTarget("@durov") // "telegram:@durov"
|
|
194
|
+
* channel.normalizeTarget("t.me/durov") // "telegram:@durov"
|
|
195
|
+
* channel.normalizeTarget("123456") // "telegram:123456"
|
|
196
|
+
*/
|
|
197
|
+
normalizeTarget(raw: string): string | undefined {
|
|
198
|
+
return normalizeTelegramMessagingTarget(raw);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
/**
|
|
202
|
+
* Check if a string looks like a Telegram target.
|
|
203
|
+
*
|
|
204
|
+
* @param raw - Raw input string
|
|
205
|
+
* @returns true if it looks like a Telegram target
|
|
206
|
+
*/
|
|
207
|
+
isValidTarget(raw: string): boolean {
|
|
208
|
+
return looksLikeTelegramTargetId(raw);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Parse a target from user input and extract the usable form.
|
|
213
|
+
*
|
|
214
|
+
* @param input - User input like "send to @user" or "dm t.me/user"
|
|
215
|
+
* @returns Extracted raw target (@username or numeric ID) or undefined
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* channel.parseTargetFromInput("send to @durov") // "@durov"
|
|
219
|
+
* channel.parseTargetFromInput("dm 123456") // "123456"
|
|
220
|
+
*/
|
|
221
|
+
parseTargetFromInput(input: string): string | undefined {
|
|
222
|
+
const words = input.split(/\s+/);
|
|
223
|
+
for (const word of words) {
|
|
224
|
+
if (looksLikeTelegramTargetId(word)) {
|
|
225
|
+
const normalized = normalizeTelegramMessagingTarget(word);
|
|
226
|
+
if (normalized) {
|
|
227
|
+
return extractRawTarget(normalized);
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
return undefined;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
/**
|
|
235
|
+
* Send a message to a target (username or chat ID).
|
|
236
|
+
*
|
|
237
|
+
* @param target - Telegram target (@username, numeric ID, or normalized format)
|
|
238
|
+
* @param text - Message text
|
|
239
|
+
*
|
|
240
|
+
* @example
|
|
241
|
+
* await channel.sendToTarget("@durov", "Hello!")
|
|
242
|
+
* await channel.sendToTarget("123456", "Hello!")
|
|
243
|
+
*/
|
|
244
|
+
async sendToTarget(target: string, text: string): Promise<void> {
|
|
245
|
+
const normalized = normalizeTelegramMessagingTarget(target);
|
|
246
|
+
if (!normalized) {
|
|
247
|
+
throw new Error(`Invalid Telegram target: ${target}`);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const rawTarget = extractRawTarget(normalized);
|
|
251
|
+
if (!rawTarget) {
|
|
252
|
+
throw new Error(`Failed to extract target from: ${normalized}`);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
// Parse chat ID (numeric or resolve username)
|
|
256
|
+
let chatId: number;
|
|
257
|
+
|
|
258
|
+
if (isNumericIdTarget(normalized)) {
|
|
259
|
+
chatId = parseInt(rawTarget, 10);
|
|
260
|
+
} else {
|
|
261
|
+
// Username target - need to resolve via bot API
|
|
262
|
+
// Note: This requires the bot to have seen the user/channel before
|
|
263
|
+
// For now, we'll try direct message with username (may fail if not cached)
|
|
264
|
+
throw new Error(
|
|
265
|
+
`Username targets require resolution. Use numeric chat ID instead, or implement username resolution. Target: ${rawTarget}`
|
|
266
|
+
);
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
await this.sendMessage(chatId, text);
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// ============================================================
|
|
274
|
+
// FACTORY FUNCTION
|
|
275
|
+
// ============================================================
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Create a Telegram channel from config
|
|
279
|
+
*/
|
|
280
|
+
export function createTelegramChannel(config: TelegramChannelConfig): GLMTelegramChannel {
|
|
281
|
+
return new GLMTelegramChannel(config);
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Create Telegram channel config from environment (Doppler)
|
|
286
|
+
*/
|
|
287
|
+
export function createTelegramConfigFromEnv(): TelegramChannelConfig | null {
|
|
288
|
+
const token = process.env.TELEGRAM_BOT_TOKEN;
|
|
289
|
+
if (!token) return null;
|
|
290
|
+
|
|
291
|
+
const allowedUsers = process.env.TELEGRAM_ALLOWED_USERS
|
|
292
|
+
?.split(",")
|
|
293
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
294
|
+
.filter((n) => !isNaN(n));
|
|
295
|
+
|
|
296
|
+
const allowedChats = process.env.TELEGRAM_ALLOWED_CHATS
|
|
297
|
+
?.split(",")
|
|
298
|
+
.map((s) => parseInt(s.trim(), 10))
|
|
299
|
+
.filter((n) => !isNaN(n));
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
platform: "telegram",
|
|
303
|
+
accountId: process.env.TELEGRAM_ACCOUNT_ID || "default",
|
|
304
|
+
instanceId: process.env.TELEGRAM_INSTANCE_ID,
|
|
305
|
+
botToken: token,
|
|
306
|
+
testChatId: process.env.TELEGRAM_TEST_CHAT_ID,
|
|
307
|
+
allowedUsers,
|
|
308
|
+
allowedChats,
|
|
309
|
+
daemonWorkdir: process.env.DAEMON_WORKDIR,
|
|
310
|
+
daemonBaseBranch: process.env.DAEMON_BASE_BRANCH,
|
|
311
|
+
enableDaemonAutoPR: process.env.DAEMON_AUTO_PR === "true",
|
|
312
|
+
enableDaemonAutoCommit: process.env.DAEMON_AUTO_COMMIT === "true",
|
|
313
|
+
butlerStorageDir: process.env.BUTLER_STORAGE_DIR,
|
|
314
|
+
};
|
|
315
|
+
}
|