@geminixiang/mama 0.1.1 → 0.1.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/CHANGELOG.md +3 -0
- package/README.md +60 -9
- package/dist/adapter.d.ts +50 -0
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +47 -0
- package/dist/adapters/discord/bot.d.ts.map +1 -0
- package/dist/adapters/discord/bot.js +292 -0
- package/dist/adapters/discord/bot.js.map +1 -0
- package/dist/adapters/discord/context.d.ts +9 -0
- package/dist/adapters/discord/context.d.ts.map +1 -0
- package/dist/adapters/discord/context.js +148 -0
- package/dist/adapters/discord/context.js.map +1 -0
- package/dist/adapters/discord/index.d.ts +3 -0
- package/dist/adapters/discord/index.d.ts.map +1 -0
- package/dist/adapters/discord/index.js +3 -0
- package/dist/adapters/discord/index.js.map +1 -0
- package/dist/adapters/slack/bot.d.ts +7 -21
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +21 -3
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts +1 -3
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +35 -0
- package/dist/adapters/telegram/bot.d.ts.map +1 -0
- package/dist/adapters/telegram/bot.js +234 -0
- package/dist/adapters/telegram/bot.js.map +1 -0
- package/dist/adapters/telegram/context.d.ts +9 -0
- package/dist/adapters/telegram/context.d.ts.map +1 -0
- package/dist/adapters/telegram/context.js +144 -0
- package/dist/adapters/telegram/context.js.map +1 -0
- package/dist/adapters/telegram/index.d.ts +3 -0
- package/dist/adapters/telegram/index.d.ts.map +1 -0
- package/dist/adapters/telegram/index.js +3 -0
- package/dist/adapters/telegram/index.js.map +1 -0
- package/dist/events.d.ts +4 -4
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +7 -7
- package/dist/events.js.map +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +55 -36
- package/dist/main.js.map +1 -1
- package/package.json +6 -2
package/dist/main.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { join, resolve } from "path";
|
|
3
|
-
import {
|
|
3
|
+
import { DiscordBot } from "./adapters/discord/index.js";
|
|
4
|
+
import { TelegramBot } from "./adapters/telegram/index.js";
|
|
5
|
+
import { SlackBot as SlackBotClass } from "./adapters/slack/index.js";
|
|
4
6
|
import { createRunner } from "./agent.js";
|
|
5
7
|
import { downloadChannel } from "./download.js";
|
|
6
8
|
import { createEventsWatcher } from "./events.js";
|
|
@@ -12,6 +14,8 @@ import { ChannelStore } from "./store.js";
|
|
|
12
14
|
// ============================================================================
|
|
13
15
|
const MOM_SLACK_APP_TOKEN = process.env.MOM_SLACK_APP_TOKEN;
|
|
14
16
|
const MOM_SLACK_BOT_TOKEN = process.env.MOM_SLACK_BOT_TOKEN;
|
|
17
|
+
const MOM_TELEGRAM_BOT_TOKEN = process.env.MOM_TELEGRAM_BOT_TOKEN;
|
|
18
|
+
const MOM_DISCORD_BOT_TOKEN = process.env.MOM_DISCORD_BOT_TOKEN;
|
|
15
19
|
function parseArgs() {
|
|
16
20
|
const args = process.argv.slice(2);
|
|
17
21
|
let sandbox = { type: "host" };
|
|
@@ -42,7 +46,7 @@ function parseArgs() {
|
|
|
42
46
|
};
|
|
43
47
|
}
|
|
44
48
|
const parsedArgs = parseArgs();
|
|
45
|
-
// Handle --download mode
|
|
49
|
+
// Handle --download mode (Slack only)
|
|
46
50
|
if (parsedArgs.downloadChannel) {
|
|
47
51
|
if (!MOM_SLACK_BOT_TOKEN) {
|
|
48
52
|
console.error("Missing env: MOM_SLACK_BOT_TOKEN");
|
|
@@ -58,8 +62,15 @@ if (!parsedArgs.workingDir) {
|
|
|
58
62
|
process.exit(1);
|
|
59
63
|
}
|
|
60
64
|
const { workingDir, sandbox } = { workingDir: parsedArgs.workingDir, sandbox: parsedArgs.sandbox };
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
// Validate platform tokens
|
|
66
|
+
const hasSlack = !!(MOM_SLACK_APP_TOKEN && MOM_SLACK_BOT_TOKEN);
|
|
67
|
+
const hasTelegram = !!MOM_TELEGRAM_BOT_TOKEN;
|
|
68
|
+
const hasDiscord = !!MOM_DISCORD_BOT_TOKEN;
|
|
69
|
+
if (!hasSlack && !hasTelegram && !hasDiscord) {
|
|
70
|
+
console.error("No platform tokens found. Set one of:\n" +
|
|
71
|
+
" Slack: MOM_SLACK_APP_TOKEN + MOM_SLACK_BOT_TOKEN\n" +
|
|
72
|
+
" Telegram: MOM_TELEGRAM_BOT_TOKEN\n" +
|
|
73
|
+
" Discord: MOM_DISCORD_BOT_TOKEN");
|
|
63
74
|
process.exit(1);
|
|
64
75
|
}
|
|
65
76
|
await validateSandbox(sandbox);
|
|
@@ -93,32 +104,22 @@ async function getState(channelId, sessionKey) {
|
|
|
93
104
|
/**
|
|
94
105
|
* Evict idle sessions from channelStates to bound memory usage.
|
|
95
106
|
* Called after each handleEvent completes.
|
|
96
|
-
*
|
|
97
|
-
* Eviction rules:
|
|
98
|
-
* - Never evict sessions that are currently running
|
|
99
|
-
* - Evict sessions idle for more than IDLE_TIMEOUT_MS
|
|
100
|
-
* - If still over MAX_SESSIONS, evict oldest idle sessions first
|
|
101
107
|
*/
|
|
102
108
|
function evictIdleSessions() {
|
|
103
109
|
const now = Date.now();
|
|
104
|
-
// First pass: evict sessions that are idle and past the timeout
|
|
105
110
|
for (const [key, state] of channelStates) {
|
|
106
111
|
if (!state.running && now - state.lastAccessedAt > IDLE_TIMEOUT_MS) {
|
|
107
112
|
channelStates.delete(key);
|
|
108
113
|
}
|
|
109
114
|
}
|
|
110
|
-
// Second pass: if still over capacity, evict oldest idle sessions
|
|
111
115
|
if (channelStates.size > MAX_SESSIONS) {
|
|
112
|
-
// Collect all non-running sessions with their last access time
|
|
113
116
|
const idleSessions = [];
|
|
114
117
|
for (const [key, state] of channelStates) {
|
|
115
118
|
if (!state.running) {
|
|
116
119
|
idleSessions.push({ key, lastAccessedAt: state.lastAccessedAt });
|
|
117
120
|
}
|
|
118
121
|
}
|
|
119
|
-
// Sort oldest first
|
|
120
122
|
idleSessions.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);
|
|
121
|
-
// Evict until under capacity
|
|
122
123
|
const toEvict = channelStates.size - MAX_SESSIONS;
|
|
123
124
|
for (let i = 0; i < toEvict && i < idleSessions.length; i++) {
|
|
124
125
|
channelStates.delete(idleSessions[i].key);
|
|
@@ -133,19 +134,19 @@ const handler = {
|
|
|
133
134
|
const state = channelStates.get(sessionKey);
|
|
134
135
|
return state?.running ?? false;
|
|
135
136
|
},
|
|
136
|
-
async handleStop(sessionKey, channelId,
|
|
137
|
+
async handleStop(sessionKey, channelId, bot) {
|
|
137
138
|
const state = channelStates.get(sessionKey);
|
|
138
139
|
if (state?.running) {
|
|
139
140
|
state.stopRequested = true;
|
|
140
141
|
state.runner.abort();
|
|
141
|
-
const ts = await
|
|
142
|
-
state.stopMessageTs = ts;
|
|
142
|
+
const ts = await bot.postMessage(channelId, "_Stopping..._");
|
|
143
|
+
state.stopMessageTs = ts;
|
|
143
144
|
}
|
|
144
145
|
else {
|
|
145
|
-
await
|
|
146
|
+
await bot.postMessage(channelId, "_Nothing running_");
|
|
146
147
|
}
|
|
147
148
|
},
|
|
148
|
-
async handleEvent(event,
|
|
149
|
+
async handleEvent(event, bot, adapters, isEvent) {
|
|
149
150
|
// Don't accept new events during shutdown
|
|
150
151
|
if (isShuttingDown) {
|
|
151
152
|
log.logInfo(`[${event.channel}] Rejected event during shutdown: ${event.text.substring(0, 50)}`);
|
|
@@ -160,8 +161,7 @@ const handler = {
|
|
|
160
161
|
// Wrap in-flight run tracking
|
|
161
162
|
const runPromise = (async () => {
|
|
162
163
|
try {
|
|
163
|
-
|
|
164
|
-
const { message, responseCtx, platform } = createSlackAdapters(event, slack, isEvent);
|
|
164
|
+
const { message, responseCtx, platform } = adapters;
|
|
165
165
|
// Run the agent
|
|
166
166
|
await responseCtx.setTyping(true);
|
|
167
167
|
await responseCtx.setWorking(true);
|
|
@@ -169,11 +169,11 @@ const handler = {
|
|
|
169
169
|
await responseCtx.setWorking(false);
|
|
170
170
|
if (result.stopReason === "aborted" && state.stopRequested) {
|
|
171
171
|
if (state.stopMessageTs) {
|
|
172
|
-
await
|
|
172
|
+
await bot.updateMessage(event.channel, state.stopMessageTs, "_Stopped_");
|
|
173
173
|
state.stopMessageTs = undefined;
|
|
174
174
|
}
|
|
175
175
|
else {
|
|
176
|
-
await
|
|
176
|
+
await bot.postMessage(event.channel, "_Stopped_");
|
|
177
177
|
}
|
|
178
178
|
}
|
|
179
179
|
}
|
|
@@ -199,24 +199,41 @@ const handler = {
|
|
|
199
199
|
// Start
|
|
200
200
|
// ============================================================================
|
|
201
201
|
log.logStartup(workingDir, sandbox.type === "host" ? "host" : `docker:${sandbox.container}`);
|
|
202
|
-
//
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
202
|
+
// Create the appropriate platform bot
|
|
203
|
+
let bot;
|
|
204
|
+
if (hasSlack) {
|
|
205
|
+
const sharedStore = new ChannelStore({ workingDir, botToken: MOM_SLACK_BOT_TOKEN });
|
|
206
|
+
bot = new SlackBotClass(handler, {
|
|
207
|
+
appToken: MOM_SLACK_APP_TOKEN,
|
|
208
|
+
botToken: MOM_SLACK_BOT_TOKEN,
|
|
209
|
+
workingDir,
|
|
210
|
+
store: sharedStore,
|
|
211
|
+
});
|
|
212
|
+
log.logInfo("Platform: Slack");
|
|
213
|
+
}
|
|
214
|
+
else if (hasTelegram) {
|
|
215
|
+
bot = new TelegramBot(handler, {
|
|
216
|
+
token: MOM_TELEGRAM_BOT_TOKEN,
|
|
217
|
+
workingDir,
|
|
218
|
+
});
|
|
219
|
+
log.logInfo("Platform: Telegram");
|
|
220
|
+
}
|
|
221
|
+
else {
|
|
222
|
+
bot = new DiscordBot(handler, {
|
|
223
|
+
token: MOM_DISCORD_BOT_TOKEN,
|
|
224
|
+
workingDir,
|
|
225
|
+
});
|
|
226
|
+
log.logInfo("Platform: Discord");
|
|
227
|
+
}
|
|
210
228
|
// Start events watcher
|
|
211
229
|
const eventsWatcher = createEventsWatcher(workingDir, bot);
|
|
212
230
|
eventsWatcher.start();
|
|
213
231
|
// Handle shutdown
|
|
214
232
|
process.on("SIGINT", async () => {
|
|
215
233
|
if (isShuttingDown)
|
|
216
|
-
return;
|
|
234
|
+
return;
|
|
217
235
|
isShuttingDown = true;
|
|
218
236
|
log.logInfo("Shutting down gracefully...");
|
|
219
|
-
// Wait for in-flight runs (max 30 seconds)
|
|
220
237
|
const timeout = Date.now() + 30000;
|
|
221
238
|
while (inFlightRuns.size > 0 && Date.now() < timeout) {
|
|
222
239
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -229,10 +246,9 @@ process.on("SIGINT", async () => {
|
|
|
229
246
|
});
|
|
230
247
|
process.on("SIGTERM", async () => {
|
|
231
248
|
if (isShuttingDown)
|
|
232
|
-
return;
|
|
249
|
+
return;
|
|
233
250
|
isShuttingDown = true;
|
|
234
251
|
log.logInfo("Shutting down gracefully...");
|
|
235
|
-
// Wait for in-flight runs (max 30 seconds)
|
|
236
252
|
const timeout = Date.now() + 30000;
|
|
237
253
|
while (inFlightRuns.size > 0 && Date.now() < timeout) {
|
|
238
254
|
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
@@ -243,5 +259,8 @@ process.on("SIGTERM", async () => {
|
|
|
243
259
|
eventsWatcher.stop();
|
|
244
260
|
process.exit(0);
|
|
245
261
|
});
|
|
246
|
-
bot.start()
|
|
262
|
+
bot.start().catch((err) => {
|
|
263
|
+
log.logWarning("Failed to start bot", err instanceof Error ? err.message : String(err));
|
|
264
|
+
process.exit(1);
|
|
265
|
+
});
|
|
247
266
|
//# sourceMappingURL=main.js.map
|
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AACrC,OAAO,EACN,mBAAmB,EAGnB,QAAQ,IAAI,aAAa,GAEzB,MAAM,2BAA2B,CAAC;AACnC,OAAO,EAAoB,YAAY,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,eAAe,CAAC;AAChD,OAAO,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,GAAG,MAAM,UAAU,CAAC;AAChC,OAAO,EAAE,eAAe,EAAsB,eAAe,EAAE,MAAM,cAAc,CAAC;AACpF,OAAO,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAE1C,+EAA+E;AAC/E,SAAS;AACT,+EAA+E;AAE/E,MAAM,mBAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;AAC5D,MAAM,mBAAmB,GAAG,OAAO,CAAC,GAAG,CAAC,mBAAmB,CAAC;AAQ5D,SAAS,SAAS,GAAe;IAChC,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACnC,IAAI,OAAO,GAAkB,EAAE,IAAI,EAAE,MAAM,EAAE,CAAC;IAC9C,IAAI,UAA8B,CAAC;IACnC,IAAI,iBAAqC,CAAC;IAE1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,MAAM,GAAG,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;QACpB,IAAI,GAAG,CAAC,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;YAClC,OAAO,GAAG,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;QAC3D,CAAC;aAAM,IAAI,GAAG,KAAK,WAAW,EAAE,CAAC;YAChC,OAAO,GAAG,eAAe,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;QAC5C,CAAC;aAAM,IAAI,GAAG,CAAC,UAAU,CAAC,aAAa,CAAC,EAAE,CAAC;YAC1C,iBAAiB,GAAG,GAAG,CAAC,KAAK,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC;QACrD,CAAC;aAAM,IAAI,GAAG,KAAK,YAAY,EAAE,CAAC;YACjC,iBAAiB,GAAG,IAAI,CAAC,EAAE,CAAC,CAAC,CAAC;QAC/B,CAAC;aAAM,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,UAAU,GAAG,GAAG,CAAC;QAClB,CAAC;IACF,CAAC;IAED,OAAO;QACN,UAAU,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,SAAS;QACxD,OAAO;QACP,eAAe,EAAE,iBAAiB;KAClC,CAAC;AAAA,CACF;AAED,MAAM,UAAU,GAAG,SAAS,EAAE,CAAC;AAE/B,yBAAyB;AACzB,IAAI,UAAU,CAAC,eAAe,EAAE,CAAC;IAChC,IAAI,CAAC,mBAAmB,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,kCAAkC,CAAC,CAAC;QAClD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,MAAM,eAAe,CAAC,UAAU,CAAC,eAAe,EAAE,mBAAmB,CAAC,CAAC;IACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,wCAAwC;AACxC,IAAI,CAAC,UAAU,CAAC,UAAU,EAAE,CAAC;IAC5B,OAAO,CAAC,KAAK,CAAC,gEAAgE,CAAC,CAAC;IAChF,OAAO,CAAC,KAAK,CAAC,qCAAqC,CAAC,CAAC;IACrD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,GAAG,EAAE,UAAU,EAAE,UAAU,CAAC,UAAU,EAAE,OAAO,EAAE,UAAU,CAAC,OAAO,EAAE,CAAC;AAEnG,IAAI,CAAC,mBAAmB,IAAI,CAAC,mBAAmB,EAAE,CAAC;IAClD,OAAO,CAAC,KAAK,CAAC,uDAAuD,CAAC,CAAC;IACvE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;AAc/B,MAAM,aAAa,GAAG,IAAI,GAAG,EAAwB,CAAC;AAEtD,iDAAiD;AACjD,MAAM,YAAY,GAAG,IAAI,GAAG,EAAiB,CAAC;AAE9C,wDAAwD;AACxD,IAAI,cAAc,GAAG,KAAK,CAAC;AAE3B,wCAAwC;AACxC,MAAM,YAAY,GAAG,GAAG,CAAC;AACzB,wEAAwE;AACxE,MAAM,eAAe,GAAG,OAAO,CAAC;AAEhC,KAAK,UAAU,QAAQ,CAAC,SAAiB,EAAE,UAAmB,EAAyB;IACtF,MAAM,GAAG,GAAG,UAAU,IAAI,SAAS,CAAC;IACpC,IAAI,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACnC,IAAI,CAAC,KAAK,EAAE,CAAC;QACZ,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC;QAC/C,KAAK,GAAG;YACP,OAAO,EAAE,KAAK;YACd,MAAM,EAAE,MAAM,YAAY,CAAC,OAAO,EAAE,GAAG,EAAE,SAAS,EAAE,UAAU,EAAE,UAAU,CAAC;YAC3E,aAAa,EAAE,KAAK;YACpB,cAAc,EAAE,IAAI,CAAC,GAAG,EAAE;SAC1B,CAAC;QACF,aAAa,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,CAAC;IAC/B,CAAC;SAAM,CAAC;QACP,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACnC,CAAC;IACD,OAAO,KAAK,CAAC;AAAA,CACb;AAED;;;;;;;;GAQG;AACH,SAAS,iBAAiB,GAAS;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IAEvB,gEAAgE;IAChE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,aAAa,EAAE,CAAC;QAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,IAAI,GAAG,GAAG,KAAK,CAAC,cAAc,GAAG,eAAe,EAAE,CAAC;YACpE,aAAa,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC;QAC3B,CAAC;IACF,CAAC;IAED,kEAAkE;IAClE,IAAI,aAAa,CAAC,IAAI,GAAG,YAAY,EAAE,CAAC;QACvC,+DAA+D;QAC/D,MAAM,YAAY,GAAmD,EAAE,CAAC;QACxE,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,aAAa,EAAE,CAAC;YAC1C,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;gBACpB,YAAY,CAAC,IAAI,CAAC,EAAE,GAAG,EAAE,cAAc,EAAE,KAAK,CAAC,cAAc,EAAE,CAAC,CAAC;YAClE,CAAC;QACF,CAAC;QAED,oBAAoB;QACpB,YAAY,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,cAAc,GAAG,CAAC,CAAC,cAAc,CAAC,CAAC;QAEjE,6BAA6B;QAC7B,MAAM,OAAO,GAAG,aAAa,CAAC,IAAI,GAAG,YAAY,CAAC;QAClD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,OAAO,IAAI,CAAC,GAAG,YAAY,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YAC7D,aAAa,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;QAC3C,CAAC;IACF,CAAC;AAAA,CACD;AAED,+EAA+E;AAC/E,UAAU;AACV,+EAA+E;AAE/E,MAAM,OAAO,GAAe;IAC3B,SAAS,CAAC,UAAkB,EAAW;QACtC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5C,OAAO,KAAK,EAAE,OAAO,IAAI,KAAK,CAAC;IAAA,CAC/B;IAED,KAAK,CAAC,UAAU,CAAC,UAAkB,EAAE,SAAiB,EAAE,KAAe,EAAiB;QACvF,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC5C,IAAI,KAAK,EAAE,OAAO,EAAE,CAAC;YACpB,KAAK,CAAC,aAAa,GAAG,IAAI,CAAC;YAC3B,KAAK,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC;YACrB,MAAM,EAAE,GAAG,MAAM,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,eAAe,CAAC,CAAC;YAC/D,KAAK,CAAC,aAAa,GAAG,EAAE,CAAC,CAAC,0BAA0B;QACrD,CAAC;aAAM,CAAC;YACP,MAAM,KAAK,CAAC,WAAW,CAAC,SAAS,EAAE,mBAAmB,CAAC,CAAC;QACzD,CAAC;IAAA,CACD;IAED,KAAK,CAAC,WAAW,CAAC,KAAiB,EAAE,KAAe,EAAE,OAAiB,EAAiB;QACvF,0CAA0C;QAC1C,IAAI,cAAc,EAAE,CAAC;YACpB,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,qCAAqC,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;YACjG,OAAO;QACR,CAAC;QAED,MAAM,UAAU,GAAG,GAAG,KAAK,CAAC,OAAO,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;QACrE,MAAM,KAAK,GAAG,MAAM,QAAQ,CAAC,KAAK,CAAC,OAAO,EAAE,UAAU,CAAC,CAAC;QAExD,YAAY;QACZ,KAAK,CAAC,OAAO,GAAG,IAAI,CAAC;QACrB,KAAK,CAAC,aAAa,GAAG,KAAK,CAAC;QAE5B,GAAG,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,mBAAmB,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,CAAC;QAE/E,8BAA8B;QAC9B,MAAM,UAAU,GAAG,CAAC,KAAK,IAAI,EAAE,CAAC;YAC/B,IAAI,CAAC;gBACJ,2CAA2C;gBAC3C,MAAM,EAAE,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,GAAG,mBAAmB,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,CAAC;gBAEtF,gBAAgB;gBAChB,MAAM,WAAW,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC;gBAClC,MAAM,WAAW,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC;gBACnC,MAAM,MAAM,GAAG,MAAM,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,EAAE,QAAQ,CAAC,CAAC;gBACtE,MAAM,WAAW,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;gBAEpC,IAAI,MAAM,CAAC,UAAU,KAAK,SAAS,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;oBAC5D,IAAI,KAAK,CAAC,aAAa,EAAE,CAAC;wBACzB,MAAM,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,aAAa,EAAE,WAAW,CAAC,CAAC;wBAC3E,KAAK,CAAC,aAAa,GAAG,SAAS,CAAC;oBACjC,CAAC;yBAAM,CAAC;wBACP,MAAM,KAAK,CAAC,WAAW,CAAC,KAAK,CAAC,OAAO,EAAE,WAAW,CAAC,CAAC;oBACrD,CAAC;gBACF,CAAC;YACF,CAAC;YAAC,OAAO,GAAG,EAAE,CAAC;gBACd,GAAG,CAAC,UAAU,CAAC,IAAI,KAAK,CAAC,OAAO,aAAa,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC;YAClG,CAAC;oBAAS,CAAC;gBACV,KAAK,CAAC,OAAO,GAAG,KAAK,CAAC;gBACtB,KAAK,CAAC,cAAc,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAClC,iBAAiB,EAAE,CAAC;YACrB,CAAC;QAAA,CACD,CAAC,EAAE,CAAC;QAEL,YAAY,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QAC7B,IAAI,CAAC;YACJ,MAAM,UAAU,CAAC;QAClB,CAAC;gBAAS,CAAC;YACV,YAAY,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACjC,CAAC;IAAA,CACD;CACD,CAAC;AAEF,+EAA+E;AAC/E,QAAQ;AACR,+EAA+E;AAE/E,GAAG,CAAC,UAAU,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,KAAK,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,UAAU,OAAO,CAAC,SAAS,EAAE,CAAC,CAAC;AAE7F,4EAA4E;AAC5E,MAAM,WAAW,GAAG,IAAI,YAAY,CAAC,EAAE,UAAU,EAAE,QAAQ,EAAE,mBAAoB,EAAE,CAAC,CAAC;AAErF,MAAM,GAAG,GAAG,IAAI,aAAa,CAAC,OAAO,EAAE;IACtC,QAAQ,EAAE,mBAAmB;IAC7B,QAAQ,EAAE,mBAAmB;IAC7B,UAAU;IACV,KAAK,EAAE,WAAW;CAClB,CAAC,CAAC;AAEH,uBAAuB;AACvB,MAAM,aAAa,GAAG,mBAAmB,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC;AAC3D,aAAa,CAAC,KAAK,EAAE,CAAC;AAEtB,kBAAkB;AAClB,OAAO,CAAC,EAAE,CAAC,QAAQ,EAAE,KAAK,IAAI,EAAE,CAAC;IAChC,IAAI,cAAc;QAAE,OAAO,CAAC,4BAA4B;IACxD,cAAc,GAAG,IAAI,CAAC;IACtB,GAAG,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;IAE3C,2CAA2C;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;IACnC,OAAO,YAAY,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;QACtD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,YAAY,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QAC3B,GAAG,CAAC,UAAU,CAAC,qBAAqB,YAAY,CAAC,IAAI,yBAAyB,CAAC,CAAC;IACjF,CAAC;IAED,aAAa,CAAC,IAAI,EAAE,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAAA,CAChB,CAAC,CAAC;AAEH,OAAO,CAAC,EAAE,CAAC,SAAS,EAAE,KAAK,IAAI,EAAE,CAAC;IACjC,IAAI,cAAc;QAAE,OAAO,CAAC,4BAA4B;IACxD,cAAc,GAAG,IAAI,CAAC;IACtB,GAAG,CAAC,OAAO,CAAC,6BAA6B,CAAC,CAAC;IAE3C,2CAA2C;IAC3C,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;IACnC,OAAO,YAAY,CAAC,IAAI,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,OAAO,EAAE,CAAC;QACtD,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;IAC1D,CAAC;IAED,IAAI,YAAY,CAAC,IAAI,GAAG,CAAC,EAAE,CAAC;QAC3B,GAAG,CAAC,UAAU,CAAC,qBAAqB,YAAY,CAAC,IAAI,yBAAyB,CAAC,CAAC;IACjF,CAAC;IAED,aAAa,CAAC,IAAI,EAAE,CAAC;IACrB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAAA,CAChB,CAAC,CAAC;AAEH,GAAG,CAAC,KAAK,EAAE,CAAC","sourcesContent":["#!/usr/bin/env node\n\nimport { join, resolve } from \"path\";\nimport {\n\tcreateSlackAdapters,\n\ttype MomHandler,\n\ttype SlackBot,\n\tSlackBot as SlackBotClass,\n\ttype SlackEvent,\n} from \"./adapters/slack/index.js\";\nimport { type AgentRunner, createRunner } from \"./agent.js\";\nimport { downloadChannel } from \"./download.js\";\nimport { createEventsWatcher } from \"./events.js\";\nimport * as log from \"./log.js\";\nimport { parseSandboxArg, type SandboxConfig, validateSandbox } from \"./sandbox.js\";\nimport { ChannelStore } from \"./store.js\";\n\n// ============================================================================\n// Config\n// ============================================================================\n\nconst MOM_SLACK_APP_TOKEN = process.env.MOM_SLACK_APP_TOKEN;\nconst MOM_SLACK_BOT_TOKEN = process.env.MOM_SLACK_BOT_TOKEN;\n\ninterface ParsedArgs {\n\tworkingDir?: string;\n\tsandbox: SandboxConfig;\n\tdownloadChannel?: string;\n}\n\nfunction parseArgs(): ParsedArgs {\n\tconst args = process.argv.slice(2);\n\tlet sandbox: SandboxConfig = { type: \"host\" };\n\tlet workingDir: string | undefined;\n\tlet downloadChannelId: string | undefined;\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\t\tif (arg.startsWith(\"--sandbox=\")) {\n\t\t\tsandbox = parseSandboxArg(arg.slice(\"--sandbox=\".length));\n\t\t} else if (arg === \"--sandbox\") {\n\t\t\tsandbox = parseSandboxArg(args[++i] || \"\");\n\t\t} else if (arg.startsWith(\"--download=\")) {\n\t\t\tdownloadChannelId = arg.slice(\"--download=\".length);\n\t\t} else if (arg === \"--download\") {\n\t\t\tdownloadChannelId = args[++i];\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tworkingDir = arg;\n\t\t}\n\t}\n\n\treturn {\n\t\tworkingDir: workingDir ? resolve(workingDir) : undefined,\n\t\tsandbox,\n\t\tdownloadChannel: downloadChannelId,\n\t};\n}\n\nconst parsedArgs = parseArgs();\n\n// Handle --download mode\nif (parsedArgs.downloadChannel) {\n\tif (!MOM_SLACK_BOT_TOKEN) {\n\t\tconsole.error(\"Missing env: MOM_SLACK_BOT_TOKEN\");\n\t\tprocess.exit(1);\n\t}\n\tawait downloadChannel(parsedArgs.downloadChannel, MOM_SLACK_BOT_TOKEN);\n\tprocess.exit(0);\n}\n\n// Normal bot mode - require working dir\nif (!parsedArgs.workingDir) {\n\tconsole.error(\"Usage: mama [--sandbox=host|docker:<name>] <working-directory>\");\n\tconsole.error(\" mama --download <channel-id>\");\n\tprocess.exit(1);\n}\n\nconst { workingDir, sandbox } = { workingDir: parsedArgs.workingDir, sandbox: parsedArgs.sandbox };\n\nif (!MOM_SLACK_APP_TOKEN || !MOM_SLACK_BOT_TOKEN) {\n\tconsole.error(\"Missing env: MOM_SLACK_APP_TOKEN, MOM_SLACK_BOT_TOKEN\");\n\tprocess.exit(1);\n}\n\nawait validateSandbox(sandbox);\n\n// ============================================================================\n// State (per channel)\n// ============================================================================\n\ninterface ChannelState {\n\trunning: boolean;\n\trunner: AgentRunner;\n\tstopRequested: boolean;\n\tstopMessageTs?: string;\n\tlastAccessedAt: number;\n}\n\nconst channelStates = new Map<string, ChannelState>();\n\n/** Track in-flight runs for graceful shutdown */\nconst inFlightRuns = new Set<Promise<void>>();\n\n/** Flag to stop accepting new events during shutdown */\nlet isShuttingDown = false;\n\n/** Maximum number of cached sessions */\nconst MAX_SESSIONS = 500;\n/** Idle timeout before a non-running session can be evicted (1 hour) */\nconst IDLE_TIMEOUT_MS = 3600000;\n\nasync function getState(channelId: string, sessionKey?: string): Promise<ChannelState> {\n\tconst key = sessionKey ?? channelId;\n\tlet state = channelStates.get(key);\n\tif (!state) {\n\t\tconst channelDir = join(workingDir, channelId);\n\t\tstate = {\n\t\t\trunning: false,\n\t\t\trunner: await createRunner(sandbox, key, channelId, channelDir, workingDir),\n\t\t\tstopRequested: false,\n\t\t\tlastAccessedAt: Date.now(),\n\t\t};\n\t\tchannelStates.set(key, state);\n\t} else {\n\t\tstate.lastAccessedAt = Date.now();\n\t}\n\treturn state;\n}\n\n/**\n * Evict idle sessions from channelStates to bound memory usage.\n * Called after each handleEvent completes.\n *\n * Eviction rules:\n * - Never evict sessions that are currently running\n * - Evict sessions idle for more than IDLE_TIMEOUT_MS\n * - If still over MAX_SESSIONS, evict oldest idle sessions first\n */\nfunction evictIdleSessions(): void {\n\tconst now = Date.now();\n\n\t// First pass: evict sessions that are idle and past the timeout\n\tfor (const [key, state] of channelStates) {\n\t\tif (!state.running && now - state.lastAccessedAt > IDLE_TIMEOUT_MS) {\n\t\t\tchannelStates.delete(key);\n\t\t}\n\t}\n\n\t// Second pass: if still over capacity, evict oldest idle sessions\n\tif (channelStates.size > MAX_SESSIONS) {\n\t\t// Collect all non-running sessions with their last access time\n\t\tconst idleSessions: Array<{ key: string; lastAccessedAt: number }> = [];\n\t\tfor (const [key, state] of channelStates) {\n\t\t\tif (!state.running) {\n\t\t\t\tidleSessions.push({ key, lastAccessedAt: state.lastAccessedAt });\n\t\t\t}\n\t\t}\n\n\t\t// Sort oldest first\n\t\tidleSessions.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);\n\n\t\t// Evict until under capacity\n\t\tconst toEvict = channelStates.size - MAX_SESSIONS;\n\t\tfor (let i = 0; i < toEvict && i < idleSessions.length; i++) {\n\t\t\tchannelStates.delete(idleSessions[i].key);\n\t\t}\n\t}\n}\n\n// ============================================================================\n// Handler\n// ============================================================================\n\nconst handler: MomHandler = {\n\tisRunning(sessionKey: string): boolean {\n\t\tconst state = channelStates.get(sessionKey);\n\t\treturn state?.running ?? false;\n\t},\n\n\tasync handleStop(sessionKey: string, channelId: string, slack: SlackBot): Promise<void> {\n\t\tconst state = channelStates.get(sessionKey);\n\t\tif (state?.running) {\n\t\t\tstate.stopRequested = true;\n\t\t\tstate.runner.abort();\n\t\t\tconst ts = await slack.postMessage(channelId, \"_Stopping..._\");\n\t\t\tstate.stopMessageTs = ts; // Save for updating later\n\t\t} else {\n\t\t\tawait slack.postMessage(channelId, \"_Nothing running_\");\n\t\t}\n\t},\n\n\tasync handleEvent(event: SlackEvent, slack: SlackBot, isEvent?: boolean): Promise<void> {\n\t\t// Don't accept new events during shutdown\n\t\tif (isShuttingDown) {\n\t\t\tlog.logInfo(`[${event.channel}] Rejected event during shutdown: ${event.text.substring(0, 50)}`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst sessionKey = `${event.channel}:${event.thread_ts ?? event.ts}`;\n\t\tconst state = await getState(event.channel, sessionKey);\n\n\t\t// Start run\n\t\tstate.running = true;\n\t\tstate.stopRequested = false;\n\n\t\tlog.logInfo(`[${event.channel}] Starting run: ${event.text.substring(0, 50)}`);\n\n\t\t// Wrap in-flight run tracking\n\t\tconst runPromise = (async () => {\n\t\t\ttry {\n\t\t\t\t// Create platform-agnostic adapter objects\n\t\t\t\tconst { message, responseCtx, platform } = createSlackAdapters(event, slack, isEvent);\n\n\t\t\t\t// Run the agent\n\t\t\t\tawait responseCtx.setTyping(true);\n\t\t\t\tawait responseCtx.setWorking(true);\n\t\t\t\tconst result = await state.runner.run(message, responseCtx, platform);\n\t\t\t\tawait responseCtx.setWorking(false);\n\n\t\t\t\tif (result.stopReason === \"aborted\" && state.stopRequested) {\n\t\t\t\t\tif (state.stopMessageTs) {\n\t\t\t\t\t\tawait slack.updateMessage(event.channel, state.stopMessageTs, \"_Stopped_\");\n\t\t\t\t\t\tstate.stopMessageTs = undefined;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait slack.postMessage(event.channel, \"_Stopped_\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tlog.logWarning(`[${event.channel}] Run error`, err instanceof Error ? err.message : String(err));\n\t\t\t} finally {\n\t\t\t\tstate.running = false;\n\t\t\t\tstate.lastAccessedAt = Date.now();\n\t\t\t\tevictIdleSessions();\n\t\t\t}\n\t\t})();\n\n\t\tinFlightRuns.add(runPromise);\n\t\ttry {\n\t\t\tawait runPromise;\n\t\t} finally {\n\t\t\tinFlightRuns.delete(runPromise);\n\t\t}\n\t},\n};\n\n// ============================================================================\n// Start\n// ============================================================================\n\nlog.logStartup(workingDir, sandbox.type === \"host\" ? \"host\" : `docker:${sandbox.container}`);\n\n// Shared store for attachment downloads (also used per-channel in getState)\nconst sharedStore = new ChannelStore({ workingDir, botToken: MOM_SLACK_BOT_TOKEN! });\n\nconst bot = new SlackBotClass(handler, {\n\tappToken: MOM_SLACK_APP_TOKEN,\n\tbotToken: MOM_SLACK_BOT_TOKEN,\n\tworkingDir,\n\tstore: sharedStore,\n});\n\n// Start events watcher\nconst eventsWatcher = createEventsWatcher(workingDir, bot);\neventsWatcher.start();\n\n// Handle shutdown\nprocess.on(\"SIGINT\", async () => {\n\tif (isShuttingDown) return; // Prevent duplicate signals\n\tisShuttingDown = true;\n\tlog.logInfo(\"Shutting down gracefully...\");\n\n\t// Wait for in-flight runs (max 30 seconds)\n\tconst timeout = Date.now() + 30000;\n\twhile (inFlightRuns.size > 0 && Date.now() < timeout) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\t}\n\n\tif (inFlightRuns.size > 0) {\n\t\tlog.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);\n\t}\n\n\teventsWatcher.stop();\n\tprocess.exit(0);\n});\n\nprocess.on(\"SIGTERM\", async () => {\n\tif (isShuttingDown) return; // Prevent duplicate signals\n\tisShuttingDown = true;\n\tlog.logInfo(\"Shutting down gracefully...\");\n\n\t// Wait for in-flight runs (max 30 seconds)\n\tconst timeout = Date.now() + 30000;\n\twhile (inFlightRuns.size > 0 && Date.now() < timeout) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\t}\n\n\tif (inFlightRuns.size > 0) {\n\t\tlog.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);\n\t}\n\n\teventsWatcher.stop();\n\tprocess.exit(0);\n});\n\nbot.start();\n"]}
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../src/main.ts"],"names":[],"mappings":"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","sourcesContent":["#!/usr/bin/env node\n\nimport { join, resolve } from \"path\";\nimport type { Bot, BotAdapters, BotEvent, BotHandler } from \"./adapter.js\";\nimport { DiscordBot } from \"./adapters/discord/index.js\";\nimport { TelegramBot } from \"./adapters/telegram/index.js\";\nimport { SlackBot as SlackBotClass } from \"./adapters/slack/index.js\";\nimport { type AgentRunner, createRunner } from \"./agent.js\";\nimport { downloadChannel } from \"./download.js\";\nimport { createEventsWatcher } from \"./events.js\";\nimport * as log from \"./log.js\";\nimport { parseSandboxArg, type SandboxConfig, validateSandbox } from \"./sandbox.js\";\nimport { ChannelStore } from \"./store.js\";\n\n// ============================================================================\n// Config\n// ============================================================================\n\nconst MOM_SLACK_APP_TOKEN = process.env.MOM_SLACK_APP_TOKEN;\nconst MOM_SLACK_BOT_TOKEN = process.env.MOM_SLACK_BOT_TOKEN;\nconst MOM_TELEGRAM_BOT_TOKEN = process.env.MOM_TELEGRAM_BOT_TOKEN;\nconst MOM_DISCORD_BOT_TOKEN = process.env.MOM_DISCORD_BOT_TOKEN;\n\ninterface ParsedArgs {\n\tworkingDir?: string;\n\tsandbox: SandboxConfig;\n\tdownloadChannel?: string;\n}\n\nfunction parseArgs(): ParsedArgs {\n\tconst args = process.argv.slice(2);\n\tlet sandbox: SandboxConfig = { type: \"host\" };\n\tlet workingDir: string | undefined;\n\tlet downloadChannelId: string | undefined;\n\n\tfor (let i = 0; i < args.length; i++) {\n\t\tconst arg = args[i];\n\t\tif (arg.startsWith(\"--sandbox=\")) {\n\t\t\tsandbox = parseSandboxArg(arg.slice(\"--sandbox=\".length));\n\t\t} else if (arg === \"--sandbox\") {\n\t\t\tsandbox = parseSandboxArg(args[++i] || \"\");\n\t\t} else if (arg.startsWith(\"--download=\")) {\n\t\t\tdownloadChannelId = arg.slice(\"--download=\".length);\n\t\t} else if (arg === \"--download\") {\n\t\t\tdownloadChannelId = args[++i];\n\t\t} else if (!arg.startsWith(\"-\")) {\n\t\t\tworkingDir = arg;\n\t\t}\n\t}\n\n\treturn {\n\t\tworkingDir: workingDir ? resolve(workingDir) : undefined,\n\t\tsandbox,\n\t\tdownloadChannel: downloadChannelId,\n\t};\n}\n\nconst parsedArgs = parseArgs();\n\n// Handle --download mode (Slack only)\nif (parsedArgs.downloadChannel) {\n\tif (!MOM_SLACK_BOT_TOKEN) {\n\t\tconsole.error(\"Missing env: MOM_SLACK_BOT_TOKEN\");\n\t\tprocess.exit(1);\n\t}\n\tawait downloadChannel(parsedArgs.downloadChannel, MOM_SLACK_BOT_TOKEN);\n\tprocess.exit(0);\n}\n\n// Normal bot mode - require working dir\nif (!parsedArgs.workingDir) {\n\tconsole.error(\"Usage: mama [--sandbox=host|docker:<name>] <working-directory>\");\n\tconsole.error(\" mama --download <channel-id>\");\n\tprocess.exit(1);\n}\n\nconst { workingDir, sandbox } = { workingDir: parsedArgs.workingDir, sandbox: parsedArgs.sandbox };\n\n// Validate platform tokens\nconst hasSlack = !!(MOM_SLACK_APP_TOKEN && MOM_SLACK_BOT_TOKEN);\nconst hasTelegram = !!MOM_TELEGRAM_BOT_TOKEN;\nconst hasDiscord = !!MOM_DISCORD_BOT_TOKEN;\n\nif (!hasSlack && !hasTelegram && !hasDiscord) {\n\tconsole.error(\n\t\t\"No platform tokens found. Set one of:\\n\" +\n\t\t\t\" Slack: MOM_SLACK_APP_TOKEN + MOM_SLACK_BOT_TOKEN\\n\" +\n\t\t\t\" Telegram: MOM_TELEGRAM_BOT_TOKEN\\n\" +\n\t\t\t\" Discord: MOM_DISCORD_BOT_TOKEN\",\n\t);\n\tprocess.exit(1);\n}\n\nawait validateSandbox(sandbox);\n\n// ============================================================================\n// State (per channel)\n// ============================================================================\n\ninterface ChannelState {\n\trunning: boolean;\n\trunner: AgentRunner;\n\tstopRequested: boolean;\n\tstopMessageTs?: string;\n\tlastAccessedAt: number;\n}\n\nconst channelStates = new Map<string, ChannelState>();\n\n/** Track in-flight runs for graceful shutdown */\nconst inFlightRuns = new Set<Promise<void>>();\n\n/** Flag to stop accepting new events during shutdown */\nlet isShuttingDown = false;\n\n/** Maximum number of cached sessions */\nconst MAX_SESSIONS = 500;\n/** Idle timeout before a non-running session can be evicted (1 hour) */\nconst IDLE_TIMEOUT_MS = 3600000;\n\nasync function getState(channelId: string, sessionKey?: string): Promise<ChannelState> {\n\tconst key = sessionKey ?? channelId;\n\tlet state = channelStates.get(key);\n\tif (!state) {\n\t\tconst channelDir = join(workingDir, channelId);\n\t\tstate = {\n\t\t\trunning: false,\n\t\t\trunner: await createRunner(sandbox, key, channelId, channelDir, workingDir),\n\t\t\tstopRequested: false,\n\t\t\tlastAccessedAt: Date.now(),\n\t\t};\n\t\tchannelStates.set(key, state);\n\t} else {\n\t\tstate.lastAccessedAt = Date.now();\n\t}\n\treturn state;\n}\n\n/**\n * Evict idle sessions from channelStates to bound memory usage.\n * Called after each handleEvent completes.\n */\nfunction evictIdleSessions(): void {\n\tconst now = Date.now();\n\n\tfor (const [key, state] of channelStates) {\n\t\tif (!state.running && now - state.lastAccessedAt > IDLE_TIMEOUT_MS) {\n\t\t\tchannelStates.delete(key);\n\t\t}\n\t}\n\n\tif (channelStates.size > MAX_SESSIONS) {\n\t\tconst idleSessions: Array<{ key: string; lastAccessedAt: number }> = [];\n\t\tfor (const [key, state] of channelStates) {\n\t\t\tif (!state.running) {\n\t\t\t\tidleSessions.push({ key, lastAccessedAt: state.lastAccessedAt });\n\t\t\t}\n\t\t}\n\n\t\tidleSessions.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);\n\n\t\tconst toEvict = channelStates.size - MAX_SESSIONS;\n\t\tfor (let i = 0; i < toEvict && i < idleSessions.length; i++) {\n\t\t\tchannelStates.delete(idleSessions[i].key);\n\t\t}\n\t}\n}\n\n// ============================================================================\n// Handler\n// ============================================================================\n\nconst handler: BotHandler = {\n\tisRunning(sessionKey: string): boolean {\n\t\tconst state = channelStates.get(sessionKey);\n\t\treturn state?.running ?? false;\n\t},\n\n\tasync handleStop(sessionKey: string, channelId: string, bot: Bot): Promise<void> {\n\t\tconst state = channelStates.get(sessionKey);\n\t\tif (state?.running) {\n\t\t\tstate.stopRequested = true;\n\t\t\tstate.runner.abort();\n\t\t\tconst ts = await bot.postMessage(channelId, \"_Stopping..._\");\n\t\t\tstate.stopMessageTs = ts;\n\t\t} else {\n\t\t\tawait bot.postMessage(channelId, \"_Nothing running_\");\n\t\t}\n\t},\n\n\tasync handleEvent(event: BotEvent, bot: Bot, adapters: BotAdapters, isEvent?: boolean): Promise<void> {\n\t\t// Don't accept new events during shutdown\n\t\tif (isShuttingDown) {\n\t\t\tlog.logInfo(`[${event.channel}] Rejected event during shutdown: ${event.text.substring(0, 50)}`);\n\t\t\treturn;\n\t\t}\n\n\t\tconst sessionKey = `${event.channel}:${event.thread_ts ?? event.ts}`;\n\t\tconst state = await getState(event.channel, sessionKey);\n\n\t\t// Start run\n\t\tstate.running = true;\n\t\tstate.stopRequested = false;\n\n\t\tlog.logInfo(`[${event.channel}] Starting run: ${event.text.substring(0, 50)}`);\n\n\t\t// Wrap in-flight run tracking\n\t\tconst runPromise = (async () => {\n\t\t\ttry {\n\t\t\t\tconst { message, responseCtx, platform } = adapters;\n\n\t\t\t\t// Run the agent\n\t\t\t\tawait responseCtx.setTyping(true);\n\t\t\t\tawait responseCtx.setWorking(true);\n\t\t\t\tconst result = await state.runner.run(message, responseCtx, platform);\n\t\t\t\tawait responseCtx.setWorking(false);\n\n\t\t\t\tif (result.stopReason === \"aborted\" && state.stopRequested) {\n\t\t\t\t\tif (state.stopMessageTs) {\n\t\t\t\t\t\tawait bot.updateMessage(event.channel, state.stopMessageTs, \"_Stopped_\");\n\t\t\t\t\t\tstate.stopMessageTs = undefined;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tawait bot.postMessage(event.channel, \"_Stopped_\");\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t} catch (err) {\n\t\t\t\tlog.logWarning(`[${event.channel}] Run error`, err instanceof Error ? err.message : String(err));\n\t\t\t} finally {\n\t\t\t\tstate.running = false;\n\t\t\t\tstate.lastAccessedAt = Date.now();\n\t\t\t\tevictIdleSessions();\n\t\t\t}\n\t\t})();\n\n\t\tinFlightRuns.add(runPromise);\n\t\ttry {\n\t\t\tawait runPromise;\n\t\t} finally {\n\t\t\tinFlightRuns.delete(runPromise);\n\t\t}\n\t},\n};\n\n// ============================================================================\n// Start\n// ============================================================================\n\nlog.logStartup(workingDir, sandbox.type === \"host\" ? \"host\" : `docker:${sandbox.container}`);\n\n// Create the appropriate platform bot\nlet bot: Bot;\n\nif (hasSlack) {\n\tconst sharedStore = new ChannelStore({ workingDir, botToken: MOM_SLACK_BOT_TOKEN! });\n\tbot = new SlackBotClass(handler, {\n\t\tappToken: MOM_SLACK_APP_TOKEN!,\n\t\tbotToken: MOM_SLACK_BOT_TOKEN!,\n\t\tworkingDir,\n\t\tstore: sharedStore,\n\t});\n\tlog.logInfo(\"Platform: Slack\");\n} else if (hasTelegram) {\n\tbot = new TelegramBot(handler, {\n\t\ttoken: MOM_TELEGRAM_BOT_TOKEN!,\n\t\tworkingDir,\n\t});\n\tlog.logInfo(\"Platform: Telegram\");\n} else {\n\tbot = new DiscordBot(handler, {\n\t\ttoken: MOM_DISCORD_BOT_TOKEN!,\n\t\tworkingDir,\n\t});\n\tlog.logInfo(\"Platform: Discord\");\n}\n\n// Start events watcher\nconst eventsWatcher = createEventsWatcher(workingDir, bot);\neventsWatcher.start();\n\n// Handle shutdown\nprocess.on(\"SIGINT\", async () => {\n\tif (isShuttingDown) return;\n\tisShuttingDown = true;\n\tlog.logInfo(\"Shutting down gracefully...\");\n\n\tconst timeout = Date.now() + 30000;\n\twhile (inFlightRuns.size > 0 && Date.now() < timeout) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\t}\n\n\tif (inFlightRuns.size > 0) {\n\t\tlog.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);\n\t}\n\n\teventsWatcher.stop();\n\tprocess.exit(0);\n});\n\nprocess.on(\"SIGTERM\", async () => {\n\tif (isShuttingDown) return;\n\tisShuttingDown = true;\n\tlog.logInfo(\"Shutting down gracefully...\");\n\n\tconst timeout = Date.now() + 30000;\n\twhile (inFlightRuns.size > 0 && Date.now() < timeout) {\n\t\tawait new Promise((resolve) => setTimeout(resolve, 500));\n\t}\n\n\tif (inFlightRuns.size > 0) {\n\t\tlog.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);\n\t}\n\n\teventsWatcher.stop();\n\tprocess.exit(0);\n});\n\nbot.start().catch((err) => {\n\tlog.logWarning(\"Failed to start bot\", err instanceof Error ? err.message : String(err));\n\tprocess.exit(1);\n});\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@geminixiang/mama",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Slack bot that delegates messages to the pi coding agent",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -29,7 +29,9 @@
|
|
|
29
29
|
"@slack/web-api": "^7.0.0",
|
|
30
30
|
"chalk": "^5.6.2",
|
|
31
31
|
"croner": "^9.1.0",
|
|
32
|
-
"diff": "^8.0.2"
|
|
32
|
+
"diff": "^8.0.2",
|
|
33
|
+
"discord.js": "^14.0.0",
|
|
34
|
+
"grammy": "^1.41.1"
|
|
33
35
|
},
|
|
34
36
|
"devDependencies": {
|
|
35
37
|
"@types/diff": "^7.0.2",
|
|
@@ -41,6 +43,8 @@
|
|
|
41
43
|
},
|
|
42
44
|
"keywords": [
|
|
43
45
|
"slack",
|
|
46
|
+
"telegram",
|
|
47
|
+
"discord",
|
|
44
48
|
"bot",
|
|
45
49
|
"ai",
|
|
46
50
|
"agent"
|