@geminixiang/mama 0.1.10 → 0.2.0-beta.1
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 +80 -23
- package/dist/adapter.d.ts +11 -9
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js.map +1 -1
- package/dist/adapters/discord/bot.d.ts +2 -2
- package/dist/adapters/discord/bot.d.ts.map +1 -1
- package/dist/adapters/discord/bot.js +33 -21
- package/dist/adapters/discord/bot.js.map +1 -1
- package/dist/adapters/discord/context.d.ts.map +1 -1
- package/dist/adapters/discord/context.js +20 -13
- package/dist/adapters/discord/context.js.map +1 -1
- package/dist/adapters/slack/bot.d.ts +13 -4
- package/dist/adapters/slack/bot.d.ts.map +1 -1
- package/dist/adapters/slack/bot.js +98 -43
- package/dist/adapters/slack/bot.js.map +1 -1
- package/dist/adapters/slack/context.d.ts.map +1 -1
- package/dist/adapters/slack/context.js +25 -20
- package/dist/adapters/slack/context.js.map +1 -1
- package/dist/adapters/telegram/bot.d.ts +4 -2
- package/dist/adapters/telegram/bot.d.ts.map +1 -1
- package/dist/adapters/telegram/bot.js +143 -58
- package/dist/adapters/telegram/bot.js.map +1 -1
- package/dist/adapters/telegram/context.d.ts +1 -1
- package/dist/adapters/telegram/context.d.ts.map +1 -1
- package/dist/adapters/telegram/context.js +124 -29
- package/dist/adapters/telegram/context.js.map +1 -1
- package/dist/agent.d.ts +7 -4
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +303 -89
- package/dist/agent.js.map +1 -1
- package/dist/bindings.d.ts +63 -0
- package/dist/bindings.d.ts.map +1 -0
- package/dist/bindings.js +94 -0
- package/dist/bindings.js.map +1 -0
- package/dist/config.d.ts +34 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +98 -38
- package/dist/config.js.map +1 -1
- package/dist/context.d.ts +8 -6
- package/dist/context.d.ts.map +1 -1
- package/dist/context.js +23 -14
- package/dist/context.js.map +1 -1
- package/dist/events.d.ts +4 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js +20 -5
- package/dist/events.js.map +1 -1
- package/dist/execution-resolver.d.ts +20 -0
- package/dist/execution-resolver.d.ts.map +1 -0
- package/dist/execution-resolver.js +51 -0
- package/dist/execution-resolver.js.map +1 -0
- package/dist/instrument.d.ts +2 -0
- package/dist/instrument.d.ts.map +1 -0
- package/dist/instrument.js +14 -0
- package/dist/instrument.js.map +1 -0
- package/dist/link-server.d.ts +16 -0
- package/dist/link-server.d.ts.map +1 -0
- package/dist/link-server.js +839 -0
- package/dist/link-server.js.map +1 -0
- package/dist/link-token.d.ts +32 -0
- package/dist/link-token.d.ts.map +1 -0
- package/dist/link-token.js +68 -0
- package/dist/link-token.js.map +1 -0
- package/dist/log.d.ts +3 -2
- package/dist/log.d.ts.map +1 -1
- package/dist/log.js +10 -9
- package/dist/log.js.map +1 -1
- package/dist/login.d.ts +29 -0
- package/dist/login.d.ts.map +1 -0
- package/dist/login.js +164 -0
- package/dist/login.js.map +1 -0
- package/dist/main.d.ts +1 -1
- package/dist/main.d.ts.map +1 -1
- package/dist/main.js +322 -82
- package/dist/main.js.map +1 -1
- package/dist/provisioner.d.ts +93 -0
- package/dist/provisioner.d.ts.map +1 -0
- package/dist/provisioner.js +336 -0
- package/dist/provisioner.js.map +1 -0
- package/dist/sandbox/container.d.ts +15 -0
- package/dist/sandbox/container.d.ts.map +1 -0
- package/dist/sandbox/container.js +122 -0
- package/dist/sandbox/container.js.map +1 -0
- package/dist/sandbox/errors.d.ts +6 -0
- package/dist/sandbox/errors.d.ts.map +1 -0
- package/dist/sandbox/errors.js +11 -0
- package/dist/sandbox/errors.js.map +1 -0
- package/dist/sandbox/firecracker.d.ts +16 -0
- package/dist/sandbox/firecracker.d.ts.map +1 -0
- package/dist/sandbox/firecracker.js +206 -0
- package/dist/sandbox/firecracker.js.map +1 -0
- package/dist/sandbox/host.d.ts +12 -0
- package/dist/sandbox/host.d.ts.map +1 -0
- package/dist/sandbox/host.js +89 -0
- package/dist/sandbox/host.js.map +1 -0
- package/dist/sandbox/image.d.ts +5 -0
- package/dist/sandbox/image.d.ts.map +1 -0
- package/dist/sandbox/image.js +30 -0
- package/dist/sandbox/image.js.map +1 -0
- package/dist/sandbox/index.d.ts +20 -0
- package/dist/sandbox/index.d.ts.map +1 -0
- package/dist/sandbox/index.js +51 -0
- package/dist/sandbox/index.js.map +1 -0
- package/dist/sandbox/types.d.ts +51 -0
- package/dist/sandbox/types.d.ts.map +1 -0
- package/dist/sandbox/types.js +2 -0
- package/dist/sandbox/types.js.map +1 -0
- package/dist/sandbox/utils.d.ts +4 -0
- package/dist/sandbox/utils.d.ts.map +1 -0
- package/dist/sandbox/utils.js +51 -0
- package/dist/sandbox/utils.js.map +1 -0
- package/dist/sandbox.d.ts +1 -39
- package/dist/sandbox.d.ts.map +1 -1
- package/dist/sandbox.js +1 -286
- package/dist/sandbox.js.map +1 -1
- package/dist/sentry.d.ts +31 -0
- package/dist/sentry.d.ts.map +1 -0
- package/dist/sentry.js +205 -0
- package/dist/sentry.js.map +1 -0
- package/dist/session-store.d.ts +72 -0
- package/dist/session-store.d.ts.map +1 -0
- package/dist/session-store.js +186 -0
- package/dist/session-store.js.map +1 -0
- package/dist/store.d.ts +1 -1
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +8 -8
- package/dist/store.js.map +1 -1
- package/dist/tools/event.d.ts +21 -0
- package/dist/tools/event.d.ts.map +1 -0
- package/dist/tools/event.js +103 -0
- package/dist/tools/event.js.map +1 -0
- package/dist/tools/index.d.ts +6 -1
- package/dist/tools/index.d.ts.map +1 -1
- package/dist/tools/index.js +5 -1
- package/dist/tools/index.js.map +1 -1
- package/dist/ui-copy.d.ts +11 -0
- package/dist/ui-copy.d.ts.map +1 -0
- package/dist/ui-copy.js +33 -0
- package/dist/ui-copy.js.map +1 -0
- package/dist/vault-routing.d.ts +10 -0
- package/dist/vault-routing.d.ts.map +1 -0
- package/dist/vault-routing.js +58 -0
- package/dist/vault-routing.js.map +1 -0
- package/dist/vault.d.ts +106 -0
- package/dist/vault.d.ts.map +1 -0
- package/dist/vault.js +389 -0
- package/dist/vault.js.map +1 -0
- package/dist/vault.test.d.ts +2 -0
- package/dist/vault.test.d.ts.map +1 -0
- package/dist/vault.test.js +67 -0
- package/dist/vault.test.js.map +1 -0
- package/package.json +13 -11
package/dist/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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 { readFileSync } from \"fs\";\nimport { fileURLToPath } from \"url\";\nimport { dirname, join as pathJoin } 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\n// Get version from package.json\nfunction getVersion(): string {\n // Try to find package.json in the dist directory or parent\n const possiblePaths = [\n pathJoin(dirname(fileURLToPath(import.meta.url)), \"package.json\"),\n pathJoin(dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\"),\n pathJoin(process.cwd(), \"package.json\"),\n ];\n\n for (const pkgPath of possiblePaths) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n if (pkg.version) return pkg.version;\n } catch {\n // Continue to next path\n }\n }\n return \"unknown\";\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 workingDir?: string;\n sandbox: SandboxConfig;\n downloadChannel?: string;\n showVersion?: boolean;\n}\n\nfunction parseArgs(): ParsedArgs {\n const args = process.argv.slice(2);\n let sandbox: SandboxConfig = { type: \"host\" };\n let workingDir: string | undefined;\n let downloadChannelId: string | undefined;\n let showVersion = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--version\" || arg === \"-v\" || arg === \"-V\") {\n showVersion = true;\n } else if (arg.startsWith(\"--sandbox=\")) {\n sandbox = parseSandboxArg(arg.slice(\"--sandbox=\".length));\n } else if (arg === \"--sandbox\") {\n sandbox = parseSandboxArg(args[++i] || \"\");\n } else if (arg.startsWith(\"--download=\")) {\n downloadChannelId = arg.slice(\"--download=\".length);\n } else if (arg === \"--download\") {\n downloadChannelId = args[++i];\n } else if (!arg.startsWith(\"-\")) {\n workingDir = arg;\n }\n }\n\n return {\n workingDir: workingDir ? resolve(workingDir) : undefined,\n sandbox,\n downloadChannel: downloadChannelId,\n showVersion,\n };\n}\n\nconst parsedArgs = parseArgs();\n\n// Handle --version\nif (parsedArgs.showVersion) {\n console.log(getVersion());\n process.exit(0);\n}\n\n// Handle --download mode (Slack only)\nif (parsedArgs.downloadChannel) {\n if (!MOM_SLACK_BOT_TOKEN) {\n console.error(\"Missing env: MOM_SLACK_BOT_TOKEN\");\n process.exit(1);\n }\n await downloadChannel(parsedArgs.downloadChannel, MOM_SLACK_BOT_TOKEN);\n process.exit(0);\n}\n\n// Normal bot mode - require working dir\nif (!parsedArgs.workingDir) {\n console.error(\n \"Usage: mama [--sandbox=host|docker:<name>|firecracker:<vm-id>:<host-path>] <working-directory>\",\n );\n console.error(\" mama --download <channel-id>\");\n process.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 console.error(\n \"No platform tokens found. Set one of:\\n\" +\n \" Slack: MOM_SLACK_APP_TOKEN + MOM_SLACK_BOT_TOKEN\\n\" +\n \" Telegram: MOM_TELEGRAM_BOT_TOKEN\\n\" +\n \" Discord: MOM_DISCORD_BOT_TOKEN\",\n );\n process.exit(1);\n}\n\nawait validateSandbox(sandbox);\n\n// ============================================================================\n// State (per channel)\n// ============================================================================\n\ninterface ChannelState {\n running: boolean;\n runner: AgentRunner;\n stopRequested: boolean;\n stopMessageTs?: string;\n lastAccessedAt: number;\n startedAt?: number;\n lastActivityAt?: number;\n}\n\nconst channelStates = new Map<string, ChannelState>();\n\n/**\n * Maps \"channel:botReplyTs\" → sessionKey.\n * When the bot posts a top-level reply, the Slack thread anchors to that ts.\n * Users replying in that thread will have thread_ts = botReplyTs, which differs\n * from the original sessionKey (channel:userMessageTs). This alias map lets\n * stop commands resolve the correct session even when the ts doesn't match.\n */\nconst threadAliases = new Map<string, string>();\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 const key = sessionKey ?? channelId;\n let state = channelStates.get(key);\n if (!state) {\n const channelDir = join(workingDir, channelId);\n state = {\n running: false,\n runner: await createRunner(sandbox, key, channelId, channelDir, workingDir),\n stopRequested: false,\n lastAccessedAt: Date.now(),\n };\n channelStates.set(key, state);\n } else {\n state.lastAccessedAt = Date.now();\n }\n return 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 const now = Date.now();\n\n for (const [key, state] of channelStates) {\n if (!state.running && now - state.lastAccessedAt > IDLE_TIMEOUT_MS) {\n channelStates.delete(key);\n // Clean up aliases pointing to this session\n for (const [alias, target] of threadAliases) {\n if (target === key) threadAliases.delete(alias);\n }\n }\n }\n\n if (channelStates.size > MAX_SESSIONS) {\n const idleSessions: Array<{ key: string; lastAccessedAt: number }> = [];\n for (const [key, state] of channelStates) {\n if (!state.running) {\n idleSessions.push({ key, lastAccessedAt: state.lastAccessedAt });\n }\n }\n\n idleSessions.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);\n\n const toEvict = channelStates.size - MAX_SESSIONS;\n for (let i = 0; i < toEvict && i < idleSessions.length; i++) {\n const evictedKey = idleSessions[i].key;\n channelStates.delete(evictedKey);\n for (const [alias, target] of threadAliases) {\n if (target === evictedKey) threadAliases.delete(alias);\n }\n }\n }\n}\n\n// ============================================================================\n// Handler\n// ============================================================================\n\nconst handler: BotHandler = {\n isRunning(sessionKey: string): boolean {\n const state = channelStates.get(sessionKey);\n return !!state?.running;\n },\n\n getRunningSessions() {\n const sessions: import(\"./adapter.js\").RunningSession[] = [];\n for (const [sessionKey, state] of channelStates) {\n if (state.running && state.startedAt) {\n // Get current step from runner\n const currentStep = state.runner.getCurrentStep();\n sessions.push({\n sessionKey,\n startedAt: state.startedAt,\n lastActivityAt: state.lastActivityAt,\n currentTool: currentStep?.label || currentStep?.toolName,\n });\n }\n }\n return sessions;\n },\n\n async handleStop(sessionKey: string, channelId: string, bot: Bot): Promise<void> {\n const state = channelStates.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n const ts = await bot.postMessage(channelId, \"_Stopping..._\");\n state.stopMessageTs = ts;\n } else {\n await bot.postMessage(channelId, \"_Nothing running_\");\n }\n },\n\n forceStop(sessionKey: string): void {\n const state = channelStates.get(sessionKey);\n if (state?.running) {\n log.logInfo(`[Force Stop] Force stopping session: ${sessionKey}`);\n state.stopRequested = true;\n state.runner.abort();\n state.running = false;\n }\n },\n\n resolveSessionKey(rawKey: string): string {\n return threadAliases.get(rawKey) ?? rawKey;\n },\n\n registerThreadAlias(aliasKey: string, sessionKey: string): void {\n threadAliases.set(aliasKey, sessionKey);\n },\n\n async handleEvent(\n event: BotEvent,\n bot: Bot,\n adapters: BotAdapters,\n _isEvent?: boolean,\n ): Promise<void> {\n // Don't accept new events during shutdown\n if (isShuttingDown) {\n log.logInfo(\n `[${event.channel}] Rejected event during shutdown: ${event.text.substring(0, 50)}`,\n );\n return;\n }\n\n const rawSessionKey = `${event.channel}:${event.thread_ts ?? event.ts}`;\n const sessionKey = this.resolveSessionKey(rawSessionKey);\n const state = await getState(event.channel, sessionKey);\n\n // Start run\n state.running = true;\n state.stopRequested = false;\n state.startedAt = Date.now();\n state.lastActivityAt = Date.now();\n\n log.logInfo(`[${event.channel}] Starting run: ${event.text.substring(0, 50)}`);\n\n // Wrap in-flight run tracking\n const runPromise = (async () => {\n try {\n const { message, responseCtx, platform } = adapters;\n\n // Run the agent\n await responseCtx.setTyping(true);\n await responseCtx.setWorking(true);\n const result = await state.runner.run(message, responseCtx, platform);\n await responseCtx.setWorking(false);\n\n if (result.stopReason === \"aborted\" && state.stopRequested) {\n if (state.stopMessageTs) {\n await bot.updateMessage(event.channel, state.stopMessageTs, \"_Stopped_\");\n state.stopMessageTs = undefined;\n } else {\n await bot.postMessage(event.channel, \"_Stopped_\");\n }\n }\n } catch (err) {\n log.logWarning(\n `[${event.channel}] Run error`,\n err instanceof Error ? err.message : String(err),\n );\n } finally {\n state.running = false;\n state.lastAccessedAt = Date.now();\n evictIdleSessions();\n }\n })();\n\n inFlightRuns.add(runPromise);\n try {\n await runPromise;\n } finally {\n inFlightRuns.delete(runPromise);\n }\n },\n};\n\n// ============================================================================\n// Start\n// ============================================================================\n\nconst sandboxDesc =\n sandbox.type === \"host\"\n ? \"host\"\n : sandbox.type === \"docker\"\n ? `docker:${sandbox.container}`\n : `firecracker:${sandbox.vmId}`;\nlog.logStartup(workingDir, sandboxDesc);\n\n// Create platform bots\nconst bots: Bot[] = [];\nconst botsByPlatform: Record<string, Bot> = {};\n\nif (hasSlack) {\n const sharedStore = new ChannelStore({ workingDir, botToken: MOM_SLACK_BOT_TOKEN! });\n const slackBot = new SlackBotClass(handler, {\n appToken: MOM_SLACK_APP_TOKEN!,\n botToken: MOM_SLACK_BOT_TOKEN!,\n workingDir,\n store: sharedStore,\n });\n bots.push(slackBot);\n botsByPlatform.slack = slackBot;\n log.logInfo(\"Platform: Slack\");\n}\nif (hasTelegram) {\n const telegramBot = new TelegramBot(handler, {\n token: MOM_TELEGRAM_BOT_TOKEN!,\n workingDir,\n });\n bots.push(telegramBot);\n botsByPlatform.telegram = telegramBot;\n log.logInfo(\"Platform: Telegram\");\n}\nif (hasDiscord) {\n const discordBot = new DiscordBot(handler, {\n token: MOM_DISCORD_BOT_TOKEN!,\n workingDir,\n });\n bots.push(discordBot);\n botsByPlatform.discord = discordBot;\n log.logInfo(\"Platform: Discord\");\n}\n\n// Start events watcher with explicit platform routing\nconst eventsWatcher = createEventsWatcher(workingDir, botsByPlatform);\nconst slackBot = botsByPlatform.slack as SlackBotClass | undefined;\nif (slackBot) {\n slackBot.setEventsWatcher(eventsWatcher);\n}\neventsWatcher.start();\n\n// Handle shutdown\nasync function shutdown(): Promise<void> {\n if (isShuttingDown) return;\n isShuttingDown = true;\n log.logInfo(\"Shutting down gracefully...\");\n\n const timeout = Date.now() + 30000;\n while (inFlightRuns.size > 0 && Date.now() < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n\n if (inFlightRuns.size > 0) {\n log.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);\n }\n\n eventsWatcher.stop();\n process.exit(0);\n}\n\nprocess.on(\"SIGINT\", shutdown);\nprocess.on(\"SIGTERM\", shutdown);\n\n// Start all bots\nawait Promise.all(\n bots.map((bot) =>\n bot.start().catch((err) => {\n log.logWarning(\"Failed to start bot\", err instanceof Error ? err.message : String(err));\n process.exit(1);\n }),\n ),\n);\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 \"./instrument.js\";\n\nimport { join, resolve } from \"path\";\nimport { homedir } from \"os\";\nimport { mkdirSync, readFileSync, statSync } from \"fs\";\nimport { fileURLToPath } from \"url\";\nimport { dirname, join as pathJoin } 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 {\n createManagedSessionFile,\n createManagedSessionFileAtPath,\n getChannelSessionDir,\n getThreadSessionFile,\n} from \"./session-store.js\";\nimport { downloadChannel } from \"./download.js\";\nimport { createEventsWatcher } from \"./events.js\";\nimport * as log from \"./log.js\";\nimport { FileUserBindingStore } from \"./bindings.js\";\nimport { startLinkServer } from \"./link-server.js\";\nimport { parseLoginCommand } from \"./login.js\";\nimport { InMemoryLinkTokenStore } from \"./link-token.js\";\nimport { DockerContainerManager } from \"./provisioner.js\";\nimport { SandboxError, parseSandboxArg, type SandboxConfig, validateSandbox } from \"./sandbox.js\";\nimport { formatNothingRunning, formatStopping } from \"./ui-copy.js\";\nimport { FileVaultManager } from \"./vault.js\";\nimport { ensureSettingsFile } from \"./config.js\";\nimport {\n createManagedVaultEntry,\n ensureSandboxVaultEntry,\n resolveActorVaultKey,\n} from \"./vault-routing.js\";\nimport { addLifecycleBreadcrumb, applyRunScope } from \"./sentry.js\";\nimport { ChannelStore } from \"./store.js\";\nimport * as Sentry from \"@sentry/node\";\n\n// ============================================================================\n// Config\n// ============================================================================\n\n// Get version from package.json\nfunction getVersion(): string {\n // Try to find package.json in the dist directory or parent\n const possiblePaths = [\n pathJoin(dirname(fileURLToPath(import.meta.url)), \"package.json\"),\n pathJoin(dirname(fileURLToPath(import.meta.url)), \"..\", \"package.json\"),\n pathJoin(process.cwd(), \"package.json\"),\n ];\n\n for (const pkgPath of possiblePaths) {\n try {\n const pkg = JSON.parse(readFileSync(pkgPath, \"utf-8\"));\n if (pkg.version) return pkg.version;\n } catch {\n // Continue to next path\n }\n }\n return \"unknown\";\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/** Base URL of the web login portal, e.g. https://platform.trygemini.xyz */\nconst MOM_LINK_URL = process.env.MOM_LINK_URL;\n/**\n * Port for the link callback HTTP server.\n * Defaults to 8181 when MOM_LINK_URL is set (behind a reverse proxy).\n * If neither is set, the server is not started.\n */\nconst MOM_LINK_PORT = process.env.MOM_LINK_PORT\n ? parseInt(process.env.MOM_LINK_PORT, 10)\n : MOM_LINK_URL\n ? 8181\n : undefined;\n\ninterface ParsedArgs {\n workingDir?: string;\n stateDir?: string;\n sandbox: SandboxConfig;\n downloadChannel?: string;\n showVersion?: boolean;\n}\n\nfunction parseArgs(): ParsedArgs {\n const args = process.argv.slice(2);\n let sandbox: SandboxConfig = { type: \"host\" };\n let workingDir: string | undefined;\n let stateDirArg: string | undefined;\n let downloadChannelId: string | undefined;\n let showVersion = false;\n\n for (let i = 0; i < args.length; i++) {\n const arg = args[i];\n if (arg === \"--version\" || arg === \"-v\" || arg === \"-V\") {\n showVersion = true;\n } else if (arg.startsWith(\"--sandbox=\")) {\n sandbox = parseSandboxArg(arg.slice(\"--sandbox=\".length));\n } else if (arg === \"--sandbox\") {\n sandbox = parseSandboxArg(args[++i] || \"\");\n } else if (arg.startsWith(\"--state-dir=\")) {\n stateDirArg = arg.slice(\"--state-dir=\".length);\n } else if (arg === \"--state-dir\") {\n stateDirArg = args[++i];\n } else if (arg.startsWith(\"--download=\")) {\n downloadChannelId = arg.slice(\"--download=\".length);\n } else if (arg === \"--download\") {\n downloadChannelId = args[++i];\n } else if (!arg.startsWith(\"-\")) {\n workingDir = arg;\n }\n }\n\n return {\n workingDir: workingDir ? resolve(workingDir) : undefined,\n stateDir: stateDirArg ? resolve(stateDirArg) : undefined,\n sandbox,\n downloadChannel: downloadChannelId,\n showVersion,\n };\n}\n\nconst WORLD_WRITABLE_MODE = 0o002;\n\n/**\n * Create stateDir if missing and refuse to use it if another local user could\n * tamper with its contents. stateDir holds vaults, bindings, and settings —\n * a world-writable or foreign-owned directory there would let a local attacker\n * swap in credentials or change routing.\n */\nfunction ensureSecureStateDir(path: string): void {\n let stat;\n try {\n stat = statSync(path);\n } catch (err) {\n const code = (err as NodeJS.ErrnoException).code;\n if (code === \"ENOENT\") {\n mkdirSync(path, { recursive: true, mode: 0o700 });\n return;\n }\n console.error(`Error: cannot access --state-dir ${path}: ${(err as Error).message}`);\n process.exit(1);\n }\n\n if (!stat.isDirectory()) {\n console.error(`Error: --state-dir ${path} exists but is not a directory`);\n process.exit(1);\n }\n\n if (stat.mode & WORLD_WRITABLE_MODE) {\n console.error(\n `Error: --state-dir ${path} is world-writable (mode ${(stat.mode & 0o777).toString(8)}). ` +\n `Credentials stored there would be exposed to other local users. ` +\n `Fix with: chmod 0700 ${path}`,\n );\n process.exit(1);\n }\n\n const euid = typeof process.geteuid === \"function\" ? process.geteuid() : undefined;\n if (euid !== undefined && stat.uid !== euid) {\n console.error(\n `Error: --state-dir ${path} is owned by uid ${stat.uid} but mama is running as uid ${euid}. ` +\n `Run mama as the directory owner or point --state-dir at a directory you own.`,\n );\n process.exit(1);\n }\n}\n\nfunction handleStartupError(error: unknown): never {\n if (error instanceof SandboxError) {\n for (const line of error.formatForCli()) {\n console.error(line);\n }\n process.exit(1);\n }\n throw error;\n}\n\nlet parsedArgs: ParsedArgs;\ntry {\n parsedArgs = parseArgs();\n} catch (error) {\n handleStartupError(error);\n}\n\n// Handle --version\nif (parsedArgs.showVersion) {\n console.log(getVersion());\n process.exit(0);\n}\n\n// Handle --download mode (Slack only)\nif (parsedArgs.downloadChannel) {\n if (!MOM_SLACK_BOT_TOKEN) {\n console.error(\"Missing env: MOM_SLACK_BOT_TOKEN\");\n process.exit(1);\n }\n await downloadChannel(parsedArgs.downloadChannel, MOM_SLACK_BOT_TOKEN);\n process.exit(0);\n}\n\n// Normal bot mode - require working dir\nif (!parsedArgs.workingDir) {\n console.error(\n \"Usage: mama [--sandbox=host|container:<name>|image:<image>|firecracker:<vm-id>:<host-path>] [--state-dir=<path>] <working-directory>\",\n );\n console.error(\" mama --download <channel-id>\");\n process.exit(1);\n}\n\nconst { workingDir, sandbox } = { workingDir: parsedArgs.workingDir, sandbox: parsedArgs.sandbox };\n// stateDir holds operator-managed files (vaults, settings, bindings).\n// Defaults to ~/.mama to keep secrets outside the project workspace mounted into sandboxes.\nconst stateDir = parsedArgs.stateDir ?? join(homedir(), \".mama\");\nensureSecureStateDir(stateDir);\n// Share stateDir with instrument.ts (for Sentry config loading)\nprocess.env.MAMA_STATE_DIR = stateDir;\n\n// Ensure settings.json exists; create a template if first run.\nconst { created: settingsCreated, config: agentSettings } = ensureSettingsFile(stateDir);\nif (settingsCreated) {\n console.log(`Created default settings: ${join(stateDir, \"settings.json\")}`);\n console.log(\"Review and update provider/model before starting.\");\n}\n\nif (!agentSettings.provider || !agentSettings.model) {\n console.error(`Error: 'provider' and 'model' must be set in ${join(stateDir, \"settings.json\")}`);\n process.exit(1);\n}\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 console.error(\n \"No platform tokens found. Set one of:\\n\" +\n \" Slack: MOM_SLACK_APP_TOKEN + MOM_SLACK_BOT_TOKEN\\n\" +\n \" Telegram: MOM_TELEGRAM_BOT_TOKEN\\n\" +\n \" Discord: MOM_DISCORD_BOT_TOKEN\",\n );\n process.exit(1);\n}\n\ntry {\n await validateSandbox(sandbox);\n} catch (error) {\n handleStartupError(error);\n}\n\nconst vaultManager = new FileVaultManager(stateDir);\nif (vaultManager.isEnabled()) {\n console.log(\n sandbox.type === \"container\"\n ? \" Vault system enabled. Shared container vault active.\"\n : \" Vault system enabled. Per-user credential routing active.\",\n );\n}\n\nconst bindingStore = new FileUserBindingStore(stateDir);\nif (bindingStore.isEnabled()) {\n console.log(\n sandbox.type === \"container\"\n ? \" Binding store enabled. Shared container mode ignores per-user vault bindings.\"\n : \" Binding store enabled. Platform user → vault routing active.\",\n );\n}\n\nconst provisioner =\n sandbox.type === \"image\" ? new DockerContainerManager(sandbox.image, workingDir) : undefined;\n\nconst linkTokenStore = new InMemoryLinkTokenStore();\n\n// Purge expired link tokens every 5 minutes\nsetInterval(() => linkTokenStore.purge(), 5 * 60 * 1000).unref();\n\n// ============================================================================\n// State (per conversation)\n// ============================================================================\n\ninterface ConversationState {\n running: boolean;\n runner: AgentRunner;\n stopRequested: boolean;\n stopMessageTs?: string;\n lastAccessedAt: number;\n startedAt?: number;\n lastActivityAt?: number;\n}\n\nconst conversationStates = new Map<string, ConversationState>();\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 (10 minutes) */\nconst IDLE_TIMEOUT_MS = 600000;\n\nif (provisioner) {\n await provisioner.reconcile();\n await provisioner.stopIdle(IDLE_TIMEOUT_MS);\n}\n\n// Stop idle containers every hour (same cadence as session eviction)\nif (provisioner) {\n setInterval(() => provisioner.stopIdle(IDLE_TIMEOUT_MS), IDLE_TIMEOUT_MS).unref();\n}\n\nfunction normalizeLoginBaseUrl(): string | undefined {\n if (MOM_LINK_URL) {\n return MOM_LINK_URL.replace(/\\/+$/, \"\");\n }\n if (MOM_LINK_PORT) {\n return `http://localhost:${MOM_LINK_PORT}`;\n }\n return undefined;\n}\n\nfunction ensureLoginVault(platform: string, platformUserId: string): string {\n const vaultId = resolveActorVaultKey(\n sandbox,\n vaultManager,\n bindingStore,\n platform,\n platformUserId,\n );\n\n ensureSandboxVaultEntry(sandbox, vaultManager, platform, platformUserId, vaultId);\n if (sandbox.type !== \"image\" && sandbox.type !== \"container\") {\n vaultManager.addEntry(\n vaultId,\n createManagedVaultEntry(platform, platformUserId, vaultId, false),\n );\n }\n\n return vaultId;\n}\n\nasync function getState(conversationId: string, sessionKey?: string): Promise<ConversationState> {\n const key = sessionKey ?? conversationId;\n let state = conversationStates.get(key);\n if (!state) {\n const conversationDir = join(workingDir, conversationId);\n state = {\n running: false,\n runner: await createRunner(\n sandbox,\n key,\n conversationId,\n conversationDir,\n workingDir,\n vaultManager,\n bindingStore,\n provisioner,\n stateDir,\n ),\n stopRequested: false,\n lastAccessedAt: Date.now(),\n };\n conversationStates.set(key, state);\n } else {\n state.lastAccessedAt = Date.now();\n }\n return state;\n}\n\n/**\n * Evict idle sessions from conversationStates to bound memory usage.\n * Called after each handleEvent completes.\n */\nfunction evictIdleSessions(): void {\n const now = Date.now();\n\n for (const [key, state] of conversationStates) {\n if (!state.running && now - state.lastAccessedAt > IDLE_TIMEOUT_MS) {\n conversationStates.delete(key);\n }\n }\n\n if (conversationStates.size > MAX_SESSIONS) {\n const idleSessions: Array<{ key: string; lastAccessedAt: number }> = [];\n for (const [key, state] of conversationStates) {\n if (!state.running) {\n idleSessions.push({ key, lastAccessedAt: state.lastAccessedAt });\n }\n }\n\n idleSessions.sort((a, b) => a.lastAccessedAt - b.lastAccessedAt);\n\n const toEvict = conversationStates.size - MAX_SESSIONS;\n for (let i = 0; i < toEvict && i < idleSessions.length; i++) {\n conversationStates.delete(idleSessions[i].key);\n }\n }\n}\n\n// ============================================================================\n// Handler\n// ============================================================================\n\nconst handler: BotHandler = {\n isRunning(sessionKey: string): boolean {\n const state = conversationStates.get(sessionKey);\n return !!state?.running;\n },\n\n getRunningSessions() {\n const sessions: import(\"./adapter.js\").RunningSession[] = [];\n for (const [sessionKey, state] of conversationStates) {\n if (state.running && state.startedAt) {\n // Get current step from runner\n const currentStep = state.runner.getCurrentStep();\n sessions.push({\n sessionKey,\n startedAt: state.startedAt,\n lastActivityAt: state.lastActivityAt,\n currentTool: currentStep?.label || currentStep?.toolName,\n });\n }\n }\n return sessions;\n },\n\n async handleStop(sessionKey: string, conversationId: string, bot: Bot): Promise<void> {\n const state = conversationStates.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n const ts = await bot.postMessage(conversationId, formatStopping(bot));\n state.stopMessageTs = ts;\n } else {\n await bot.postMessage(conversationId, formatNothingRunning(bot));\n }\n },\n\n forceStop(sessionKey: string): void {\n const state = conversationStates.get(sessionKey);\n if (state?.running) {\n log.logInfo(`[Force Stop] Force stopping session: ${sessionKey}`);\n state.stopRequested = true;\n state.runner.abort();\n state.running = false;\n }\n },\n\n async handleNew(sessionKey: string, conversationId: string, bot: Bot): Promise<void> {\n const state = conversationStates.get(sessionKey);\n if (state?.running) {\n state.stopRequested = true;\n state.runner.abort();\n }\n\n // Channel sessions rotate via current pointer. Thread sessions reset in place.\n const conversationDir = join(workingDir, conversationId);\n if (sessionKey.includes(\":\")) {\n createManagedSessionFileAtPath(\n getThreadSessionFile(conversationDir, sessionKey),\n conversationDir,\n );\n } else {\n createManagedSessionFile(getChannelSessionDir(conversationDir), conversationDir);\n }\n\n // Remove from in-memory cache\n conversationStates.delete(sessionKey);\n\n log.logInfo(`[${conversationId}] Session reset: ${sessionKey}`);\n await bot.postMessage(conversationId, \"Conversation reset. Send a new message to start fresh.\");\n },\n\n async handleLogin(\n platform: string,\n platformUserId: string,\n conversationId: string,\n bot: Bot,\n commandText: string,\n isPrivateConversation: boolean,\n ): Promise<void> {\n const parsed = parseLoginCommand(commandText);\n if (!parsed) {\n return;\n }\n\n if (!isPrivateConversation) {\n await bot.postMessage(\n conversationId,\n \"为了保护你的凭证,`/login` 只能在与机器人的私聊中使用。请先私信机器人,再重新执行 `/login`。\",\n );\n return;\n }\n\n const baseUrl = normalizeLoginBaseUrl();\n if (!baseUrl) {\n await bot.postMessage(\n conversationId,\n \"Login is not configured. Set `MOM_LINK_URL` or `MOM_LINK_PORT` on the server.\",\n );\n return;\n }\n\n let vaultId: string;\n try {\n vaultId = ensureLoginVault(platform, platformUserId);\n } catch (error) {\n log.logWarning(\n `[${conversationId}] Failed to prepare login vault for ${platform}/${platformUserId}`,\n error instanceof Error ? error.message : String(error),\n );\n await bot.postMessage(\n conversationId,\n \"Login setup failed on the server. 请稍后重试,或联系管理员检查 vault 存储权限。\",\n );\n return;\n }\n\n const loginLabel = \"credential\";\n const vaultLabel = sandbox.type === \"container\" ? \"the shared container vault\" : \"your vault\";\n await bot.postMessage(\n conversationId,\n `Open this link to store ${loginLabel} in ${vaultLabel} ` +\n `(expires in 15 minutes):\\n${baseUrl}/link?token=${\n linkTokenStore.create(\n platform as \"slack\" | \"discord\" | \"telegram\",\n platformUserId,\n conversationId,\n vaultId,\n \"\",\n ).token\n }`,\n );\n },\n\n async handleEvent(\n event: BotEvent,\n bot: Bot,\n adapters: BotAdapters,\n _isEvent?: boolean,\n ): Promise<void> {\n // Don't accept new events during shutdown\n if (isShuttingDown) {\n log.logInfo(\n `[${event.conversationId}] Rejected event during shutdown: ${event.text.substring(0, 50)}`,\n );\n return;\n }\n\n const sessionKey = event.sessionKey ?? `${event.conversationId}:${event.thread_ts ?? event.ts}`;\n const state = await getState(event.conversationId, sessionKey);\n\n // Start run\n state.running = true;\n state.stopRequested = false;\n state.startedAt = Date.now();\n state.lastActivityAt = Date.now();\n\n log.logInfo(`[${event.conversationId}] Starting run: ${event.text.substring(0, 50)}`);\n\n // Wrap in-flight run tracking\n Sentry.metrics.count(\"agent.run.started\", 1, {\n attributes: { channel: event.conversationId },\n });\n Sentry.metrics.gauge(\"agent.sessions.active\", inFlightRuns.size + 1);\n\n const runPromise = Sentry.startSpan(\n {\n name: \"agent.run\",\n op: \"agent\",\n attributes: { channelId: event.conversationId, sessionKey },\n },\n async () => {\n return Sentry.withScope(async (scope) => {\n const { message, responseCtx, platform } = adapters;\n applyRunScope(scope, {\n conversationId: event.conversationId,\n sessionKey,\n messageId: message.id,\n platform: platform.name,\n userId: message.userId,\n userName: message.userName,\n threadTs: message.threadTs,\n isEvent: _isEvent,\n });\n addLifecycleBreadcrumb(\"agent.run.started\", {\n channel_id: event.conversationId,\n platform: platform.name,\n has_attachments: (message.attachments?.length ?? 0) > 0,\n });\n\n try {\n await responseCtx.setTyping(true);\n await responseCtx.setWorking(true);\n const result = await state.runner.run(message, responseCtx, platform);\n await responseCtx.setWorking(false);\n\n const durationMs = Date.now() - state.startedAt!;\n Sentry.metrics.distribution(\"agent.run.duration\", durationMs, {\n unit: \"millisecond\",\n attributes: {\n channel: event.conversationId,\n platform: platform.name,\n stop_reason: result.stopReason,\n },\n });\n Sentry.metrics.count(\"agent.run.completed\", 1, {\n attributes: {\n channel: event.conversationId,\n platform: platform.name,\n stop_reason: result.stopReason,\n },\n });\n addLifecycleBreadcrumb(\"agent.run.completed\", {\n channel_id: event.conversationId,\n platform: platform.name,\n stop_reason: result.stopReason,\n duration_ms: durationMs,\n });\n\n if (result.stopReason === \"aborted\" && state.stopRequested) {\n if (state.stopMessageTs) {\n await bot.updateMessage(event.conversationId, state.stopMessageTs, \"_Stopped_\");\n state.stopMessageTs = undefined;\n } else {\n await bot.postMessage(event.conversationId, \"_Stopped_\");\n }\n }\n } catch (err) {\n scope.setContext(\"agent_run_error\", {\n conversationId: event.conversationId,\n sessionKey,\n platform: adapters.platform.name,\n messageId: adapters.message.id,\n threadTs: adapters.message.threadTs,\n });\n Sentry.captureException(err);\n Sentry.metrics.count(\"agent.run.errors\", 1, {\n attributes: { channel: event.conversationId, platform: adapters.platform.name },\n });\n log.logWarning(\n `[${event.conversationId}] Run error`,\n err instanceof Error ? err.message : String(err),\n );\n } finally {\n state.running = false;\n state.lastAccessedAt = Date.now();\n Sentry.metrics.gauge(\"agent.sessions.active\", inFlightRuns.size - 1);\n evictIdleSessions();\n }\n });\n },\n );\n\n inFlightRuns.add(runPromise);\n try {\n await runPromise;\n } finally {\n inFlightRuns.delete(runPromise);\n }\n },\n};\n\n// ============================================================================\n// Start\n// ============================================================================\n\nconst sandboxDesc =\n sandbox.type === \"host\"\n ? \"host\"\n : sandbox.type === \"container\"\n ? `container:${sandbox.container}`\n : sandbox.type === \"image\"\n ? `image:${sandbox.image}`\n : `firecracker:${sandbox.vmId}`;\nlog.logStartup(workingDir, sandboxDesc);\n\n// Start link callback server if port is configured\nif (MOM_LINK_PORT) {\n startLinkServer(\n MOM_LINK_PORT,\n linkTokenStore,\n vaultManager,\n async (platform, conversationId, msg) => {\n const bot = botsByPlatform[platform];\n if (bot) await bot.postMessage(conversationId, msg);\n },\n );\n}\n\n// Create platform bots\nconst bots: Bot[] = [];\nconst botsByPlatform: Record<string, Bot> = {};\n\nif (hasSlack) {\n const sharedStore = new ChannelStore({ workingDir, botToken: MOM_SLACK_BOT_TOKEN! });\n const slackBot = new SlackBotClass(handler, {\n appToken: MOM_SLACK_APP_TOKEN!,\n botToken: MOM_SLACK_BOT_TOKEN!,\n workingDir,\n store: sharedStore,\n });\n bots.push(slackBot);\n botsByPlatform.slack = slackBot;\n log.logInfo(\"Platform: Slack\");\n}\nif (hasTelegram) {\n const telegramBot = new TelegramBot(handler, {\n token: MOM_TELEGRAM_BOT_TOKEN!,\n workingDir,\n });\n bots.push(telegramBot);\n botsByPlatform.telegram = telegramBot;\n log.logInfo(\"Platform: Telegram\");\n}\nif (hasDiscord) {\n const discordBot = new DiscordBot(handler, {\n token: MOM_DISCORD_BOT_TOKEN!,\n workingDir,\n });\n bots.push(discordBot);\n botsByPlatform.discord = discordBot;\n log.logInfo(\"Platform: Discord\");\n}\n\n// Start events watcher with explicit platform routing\nconst eventsWatcher = createEventsWatcher(workingDir, botsByPlatform);\nconst slackBot = botsByPlatform.slack as SlackBotClass | undefined;\nif (slackBot) {\n slackBot.setEventsWatcher(eventsWatcher);\n}\neventsWatcher.start();\n\n// Handle shutdown\nasync function shutdown(): Promise<void> {\n if (isShuttingDown) return;\n isShuttingDown = true;\n log.logInfo(\"Shutting down gracefully...\");\n\n const timeout = Date.now() + 30000;\n while (inFlightRuns.size > 0 && Date.now() < timeout) {\n await new Promise((resolve) => setTimeout(resolve, 500));\n }\n\n if (inFlightRuns.size > 0) {\n log.logWarning(`Forcing exit with ${inFlightRuns.size} runs still in progress`);\n }\n\n eventsWatcher.stop();\n await Sentry.close(5000);\n process.exit(0);\n}\n\nprocess.on(\"SIGINT\", shutdown);\nprocess.on(\"SIGTERM\", shutdown);\n\n// Start all bots\nawait Promise.all(\n bots.map((bot) =>\n bot.start().catch((err) => {\n log.logWarning(\"Failed to start bot\", err instanceof Error ? err.message : String(err));\n process.exit(1);\n }),\n ),\n);\n"]}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
|
+
declare const execFileAsync: typeof execFile.__promisify__;
|
|
3
|
+
type ExecFileAsync = typeof execFileAsync;
|
|
4
|
+
export interface ContainerMount {
|
|
5
|
+
source: string;
|
|
6
|
+
target: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ProvisionOptions {
|
|
9
|
+
containerName?: string;
|
|
10
|
+
mounts?: ContainerMount[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Manages the lifecycle of per-user Docker containers.
|
|
14
|
+
*
|
|
15
|
+
* Tracks each container's status in memory (running / stopped / missing).
|
|
16
|
+
* State is always verified against Docker on provision(), so in-memory state
|
|
17
|
+
* stays accurate without polling.
|
|
18
|
+
*/
|
|
19
|
+
export declare class DockerContainerManager {
|
|
20
|
+
private readonly image;
|
|
21
|
+
private readonly workspaceDir;
|
|
22
|
+
private readonly execFileImpl;
|
|
23
|
+
private state;
|
|
24
|
+
/**
|
|
25
|
+
* In-flight provision() calls per vaultId. A concurrent second call for the
|
|
26
|
+
* same user piggybacks on the first docker start/run instead of racing —
|
|
27
|
+
* without this, two parallel messages from one user could produce duplicate
|
|
28
|
+
* containers or conflict on docker run.
|
|
29
|
+
*/
|
|
30
|
+
private inflight;
|
|
31
|
+
private static readonly MANAGED_LABEL;
|
|
32
|
+
private static readonly IMAGE_MODE_LABEL;
|
|
33
|
+
private static readonly VAULT_ID_LABEL_KEY;
|
|
34
|
+
constructor(image: string, workspaceDir: string, execFileImpl?: ExecFileAsync);
|
|
35
|
+
/** Sanitize an identifier segment for use in vault keys and container names. */
|
|
36
|
+
static sanitizeSegment(value: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* Deterministic vault key for a platform user.
|
|
39
|
+
* e.g. ("slack", "U04ABC") → "slack-u04abc"
|
|
40
|
+
*/
|
|
41
|
+
static vaultId(platform: string, platformUserId: string): string;
|
|
42
|
+
/** Deterministic container name for a vault-backed user sandbox. */
|
|
43
|
+
static containerName(vaultId: string): string;
|
|
44
|
+
/**
|
|
45
|
+
* Ensure a container exists and is running for the given vaultId.
|
|
46
|
+
* Always inspects the actual Docker state, then acts accordingly:
|
|
47
|
+
* - running → no-op
|
|
48
|
+
* - stopped → docker start
|
|
49
|
+
* - missing → docker run
|
|
50
|
+
*
|
|
51
|
+
* Returns the container name.
|
|
52
|
+
*/
|
|
53
|
+
provision(vaultId: string, options?: ProvisionOptions): Promise<string>;
|
|
54
|
+
private provisionInner;
|
|
55
|
+
/**
|
|
56
|
+
* Stop a running container (docker stop). Container is preserved and can be
|
|
57
|
+
* restarted via provision(). Intended for idle lifecycle management.
|
|
58
|
+
*/
|
|
59
|
+
stop(vaultId: string): Promise<void>;
|
|
60
|
+
/** Stop and remove a container permanently (e.g. on vault revocation). */
|
|
61
|
+
remove(vaultId: string): Promise<void>;
|
|
62
|
+
/**
|
|
63
|
+
* Stop all containers that have been idle for longer than maxIdleMs.
|
|
64
|
+
* Idle time is measured from the last provision() call.
|
|
65
|
+
*/
|
|
66
|
+
stopIdle(maxIdleMs: number): Promise<void>;
|
|
67
|
+
/**
|
|
68
|
+
* Rebuild in-memory state from existing Docker containers managed by mama image mode.
|
|
69
|
+
* Supports both new labeled containers and legacy name-prefixed containers.
|
|
70
|
+
*/
|
|
71
|
+
reconcile(): Promise<void>;
|
|
72
|
+
private setState;
|
|
73
|
+
private getContainerName;
|
|
74
|
+
private mountArgs;
|
|
75
|
+
private toBindSpec;
|
|
76
|
+
private runContainer;
|
|
77
|
+
private hasBindMountDrift;
|
|
78
|
+
private expectedBinds;
|
|
79
|
+
private sameBinds;
|
|
80
|
+
private inspectBindMounts;
|
|
81
|
+
private inspectStatus;
|
|
82
|
+
private listContainerNamesByLabel;
|
|
83
|
+
private listContainerNamesByPrefix;
|
|
84
|
+
private parseNameLines;
|
|
85
|
+
private inspectContainerDetails;
|
|
86
|
+
private normalizeDockerValue;
|
|
87
|
+
private parseDockerTimestamp;
|
|
88
|
+
private vaultIdFromContainerName;
|
|
89
|
+
}
|
|
90
|
+
/** @deprecated Use DockerContainerManager */
|
|
91
|
+
export declare const DockerProvisioner: typeof DockerContainerManager;
|
|
92
|
+
export {};
|
|
93
|
+
//# sourceMappingURL=provisioner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provisioner.d.ts","sourceRoot":"","sources":["../src/provisioner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAIzC,QAAA,MAAM,aAAa,+BAAsB,CAAC;AAC1C,KAAK,aAAa,GAAG,OAAO,aAAa,CAAC;AAU1C,MAAM,WAAW,cAAc;IAC7B,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,gBAAgB;IAC/B,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;CAC3B;AAID;;;;;;GAMG;AACH,qBAAa,sBAAsB;IAc/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,YAAY;IAC7B,OAAO,CAAC,QAAQ,CAAC,YAAY;IAf/B,OAAO,CAAC,KAAK,CAAqC;IAClD;;;;;OAKG;IACH,OAAO,CAAC,QAAQ,CAAsC;IACtD,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,aAAa,CAAuB;IAC5D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,gBAAgB,CAAwB;IAChE,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,kBAAkB,CAAmB;IAE7D,YACmB,KAAK,EAAE,MAAM,EACb,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,aAA6B,EAC1D;IAEJ,gFAAgF;IAChF,MAAM,CAAC,eAAe,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAM5C;IAED;;;OAGG;IACH,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,MAAM,CAE/D;IAED,oEAAoE;IACpE,MAAM,CAAC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAE5C;IAED;;;;;;;;OAQG;IACG,SAAS,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,GAAE,gBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,CAShF;YAEa,cAAc;IAgC5B;;;OAGG;IACG,IAAI,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAYzC;IAED,0EAA0E;IACpE,MAAM,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAY3C;IAED;;;OAGG;IACG,QAAQ,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAS/C;IAED;;;OAGG;IACG,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CA6B/B;IAED,OAAO,CAAC,QAAQ;IAIhB,OAAO,CAAC,gBAAgB;IAIxB,OAAO,CAAC,SAAS;IAIjB,OAAO,CAAC,UAAU;YAIJ,YAAY;YA0BZ,iBAAiB;IAS/B,OAAO,CAAC,aAAa;IAMrB,OAAO,CAAC,SAAS;YAQH,iBAAiB;YAqBjB,aAAa;YAcb,yBAAyB;YAsBzB,0BAA0B;IAoBxC,OAAO,CAAC,cAAc;YAOR,uBAAuB;IAwBrC,OAAO,CAAC,oBAAoB;IAM5B,OAAO,CAAC,oBAAoB;IAO5B,OAAO,CAAC,wBAAwB;CAMjC;AAED,6CAA6C;AAC7C,eAAO,MAAM,iBAAiB,+BAAyB,CAAC","sourcesContent":["import { execFile } from \"child_process\";\nimport { promisify } from \"util\";\nimport * as log from \"./log.js\";\n\nconst execFileAsync = promisify(execFile);\ntype ExecFileAsync = typeof execFileAsync;\n\ntype ContainerStatus = \"running\" | \"stopped\" | \"missing\";\n\ninterface ContainerState {\n status: ContainerStatus;\n lastUsed: number;\n containerName: string;\n}\n\nexport interface ContainerMount {\n source: string;\n target: string;\n}\n\nexport interface ProvisionOptions {\n containerName?: string;\n mounts?: ContainerMount[];\n}\n\n// ── DockerContainerManager ─────────────────────────────────────────────────────\n\n/**\n * Manages the lifecycle of per-user Docker containers.\n *\n * Tracks each container's status in memory (running / stopped / missing).\n * State is always verified against Docker on provision(), so in-memory state\n * stays accurate without polling.\n */\nexport class DockerContainerManager {\n private state = new Map<string, ContainerState>();\n /**\n * In-flight provision() calls per vaultId. A concurrent second call for the\n * same user piggybacks on the first docker start/run instead of racing —\n * without this, two parallel messages from one user could produce duplicate\n * containers or conflict on docker run.\n */\n private inflight = new Map<string, Promise<string>>();\n private static readonly MANAGED_LABEL = \"mama.managed=true\";\n private static readonly IMAGE_MODE_LABEL = \"mama.sandbox=image\";\n private static readonly VAULT_ID_LABEL_KEY = \"mama.vault-id\";\n\n constructor(\n private readonly image: string,\n private readonly workspaceDir: string,\n private readonly execFileImpl: ExecFileAsync = execFileAsync,\n ) {}\n\n /** Sanitize an identifier segment for use in vault keys and container names. */\n static sanitizeSegment(value: string): string {\n const sanitized = value\n .toLowerCase()\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/^-+|-+$/g, \"\");\n return sanitized || \"unknown\";\n }\n\n /**\n * Deterministic vault key for a platform user.\n * e.g. (\"slack\", \"U04ABC\") → \"slack-u04abc\"\n */\n static vaultId(platform: string, platformUserId: string): string {\n return `${DockerContainerManager.sanitizeSegment(platform)}-${DockerContainerManager.sanitizeSegment(platformUserId)}`;\n }\n\n /** Deterministic container name for a vault-backed user sandbox. */\n static containerName(vaultId: string): string {\n return `mama-sandbox-${vaultId}`;\n }\n\n /**\n * Ensure a container exists and is running for the given vaultId.\n * Always inspects the actual Docker state, then acts accordingly:\n * - running → no-op\n * - stopped → docker start\n * - missing → docker run\n *\n * Returns the container name.\n */\n async provision(vaultId: string, options: ProvisionOptions = {}): Promise<string> {\n const existing = this.inflight.get(vaultId);\n if (existing) return existing;\n\n const pending = this.provisionInner(vaultId, options).finally(() => {\n this.inflight.delete(vaultId);\n });\n this.inflight.set(vaultId, pending);\n return pending;\n }\n\n private async provisionInner(vaultId: string, options: ProvisionOptions): Promise<string> {\n const containerName = options.containerName ?? DockerContainerManager.containerName(vaultId);\n const mounts = options.mounts ?? [];\n const status = await this.inspectStatus(containerName);\n\n try {\n if (status !== \"missing\" && (await this.hasBindMountDrift(containerName, mounts))) {\n log.logInfo(`Container ${containerName} mounts changed; recreating container`);\n await this.execFileImpl(\"docker\", [\"rm\", \"-f\", containerName]);\n await this.runContainer(vaultId, containerName, mounts);\n log.logInfo(`Container ${containerName} recreated`);\n } else if (status === \"running\") {\n log.logInfo(`Container ${containerName} already running`);\n } else if (status === \"stopped\") {\n await this.execFileImpl(\"docker\", [\"start\", containerName]);\n log.logInfo(`Container ${containerName} started`);\n } else {\n await this.runContainer(vaultId, containerName, mounts);\n log.logInfo(`Container ${containerName} created`);\n }\n } catch (err) {\n // Drop cached state so the next provision() re-inspects Docker cleanly\n // and stopIdle doesn't keep trying to stop a container that never\n // became running. We deliberately don't bump lastUsed here.\n this.state.delete(vaultId);\n throw err;\n }\n\n this.setState(vaultId, \"running\", containerName);\n return containerName;\n }\n\n /**\n * Stop a running container (docker stop). Container is preserved and can be\n * restarted via provision(). Intended for idle lifecycle management.\n */\n async stop(vaultId: string): Promise<void> {\n const containerName = this.getContainerName(vaultId);\n try {\n await this.execFileImpl(\"docker\", [\"stop\", containerName]);\n this.setState(vaultId, \"stopped\", containerName);\n log.logInfo(`Container ${containerName} stopped (idle)`);\n } catch (err) {\n log.logWarning(\n `Failed to stop container ${containerName}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n /** Stop and remove a container permanently (e.g. on vault revocation). */\n async remove(vaultId: string): Promise<void> {\n const containerName = this.getContainerName(vaultId);\n try {\n await this.execFileImpl(\"docker\", [\"rm\", \"-f\", containerName]);\n this.state.delete(vaultId);\n log.logInfo(`Container ${containerName} removed`);\n } catch (err) {\n log.logWarning(\n `Failed to remove container ${containerName}`,\n err instanceof Error ? err.message : String(err),\n );\n }\n }\n\n /**\n * Stop all containers that have been idle for longer than maxIdleMs.\n * Idle time is measured from the last provision() call.\n */\n async stopIdle(maxIdleMs: number): Promise<void> {\n const now = Date.now();\n const toStop: string[] = [];\n for (const [vaultId, containerState] of this.state) {\n if (containerState.status === \"running\" && now - containerState.lastUsed > maxIdleMs) {\n toStop.push(vaultId);\n }\n }\n await Promise.all(toStop.map((vaultId) => this.stop(vaultId)));\n }\n\n /**\n * Rebuild in-memory state from existing Docker containers managed by mama image mode.\n * Supports both new labeled containers and legacy name-prefixed containers.\n */\n async reconcile(): Promise<void> {\n const discovered = new Set<string>();\n const labeledNames = await this.listContainerNamesByLabel();\n for (const name of labeledNames) discovered.add(name);\n const legacyNames = await this.listContainerNamesByPrefix();\n for (const name of legacyNames) discovered.add(name);\n\n this.state.clear();\n\n for (const containerName of discovered) {\n const details = await this.inspectContainerDetails(containerName);\n if (!details) continue;\n\n const vaultId = details.vaultId || this.vaultIdFromContainerName(containerName);\n if (!vaultId) {\n log.logWarning(`Skipping unmanaged-style container without vault id`, containerName);\n continue;\n }\n\n const status: ContainerStatus = details.running ? \"running\" : \"stopped\";\n const lastUsed = details.startedAtMs ?? Date.now();\n this.state.set(vaultId, { status, lastUsed, containerName });\n }\n\n const running = Array.from(this.state.values()).filter((s) => s.status === \"running\").length;\n const stopped = this.state.size - running;\n log.logInfo(\n `Reconciled ${this.state.size} managed containers (running=${running}, stopped=${stopped})`,\n );\n }\n\n private setState(vaultId: string, status: ContainerStatus, containerName: string): void {\n this.state.set(vaultId, { status, lastUsed: Date.now(), containerName });\n }\n\n private getContainerName(vaultId: string): string {\n return this.state.get(vaultId)?.containerName ?? DockerContainerManager.containerName(vaultId);\n }\n\n private mountArgs(mounts: ContainerMount[]): string[] {\n return mounts.flatMap((mount) => [\"-v\", this.toBindSpec(mount)]);\n }\n\n private toBindSpec(mount: ContainerMount): string {\n return `${mount.source}:${mount.target}`;\n }\n\n private async runContainer(\n vaultId: string,\n containerName: string,\n mounts: ContainerMount[],\n ): Promise<void> {\n log.logInfo(`Creating container ${containerName} from image ${this.image}`);\n await this.execFileImpl(\"docker\", [\n \"run\",\n \"-d\",\n \"--name\",\n containerName,\n \"--label\",\n DockerContainerManager.MANAGED_LABEL,\n \"--label\",\n DockerContainerManager.IMAGE_MODE_LABEL,\n \"--label\",\n `${DockerContainerManager.VAULT_ID_LABEL_KEY}=${vaultId}`,\n \"-v\",\n `${this.workspaceDir}:/workspace`,\n ...this.mountArgs(mounts),\n this.image,\n \"sleep\",\n \"infinity\",\n ]);\n }\n\n private async hasBindMountDrift(\n containerName: string,\n mounts: ContainerMount[],\n ): Promise<boolean> {\n const expected = this.expectedBinds(mounts);\n const actual = await this.inspectBindMounts(containerName);\n return !this.sameBinds(expected, actual);\n }\n\n private expectedBinds(mounts: ContainerMount[]): string[] {\n return [`${this.workspaceDir}:/workspace`, ...mounts.map((mount) => this.toBindSpec(mount))]\n .slice()\n .sort();\n }\n\n private sameBinds(expected: string[], actual: string[]): boolean {\n if (expected.length !== actual.length) {\n return false;\n }\n\n return expected.every((bind, index) => bind === actual[index]);\n }\n\n private async inspectBindMounts(containerName: string): Promise<string[]> {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{json .HostConfig.Binds}}\",\n containerName,\n ]);\n const payload = stdout.trim();\n const parsed = JSON.parse(payload.length > 0 ? payload : \"null\") as unknown;\n\n if (parsed === null) {\n return [];\n }\n\n if (!Array.isArray(parsed) || parsed.some((bind) => typeof bind !== \"string\")) {\n throw new Error(`Unexpected docker bind mount payload for container \"${containerName}\"`);\n }\n\n return [...parsed].sort();\n }\n\n private async inspectStatus(containerName: string): Promise<ContainerStatus> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n \"{{.State.Running}}\",\n containerName,\n ]);\n return stdout.trim() === \"true\" ? \"running\" : \"stopped\";\n } catch {\n return \"missing\";\n }\n }\n\n private async listContainerNamesByLabel(): Promise<string[]> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"ps\",\n \"-a\",\n \"--filter\",\n `label=${DockerContainerManager.MANAGED_LABEL}`,\n \"--filter\",\n `label=${DockerContainerManager.IMAGE_MODE_LABEL}`,\n \"--format\",\n \"{{.Names}}\",\n ]);\n return this.parseNameLines(stdout);\n } catch (err) {\n log.logWarning(\n \"Failed to list labeled managed containers\",\n err instanceof Error ? err.message : String(err),\n );\n return [];\n }\n }\n\n private async listContainerNamesByPrefix(): Promise<string[]> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"ps\",\n \"-a\",\n \"--filter\",\n `name=${DockerContainerManager.containerName(\"\")}`,\n \"--format\",\n \"{{.Names}}\",\n ]);\n return this.parseNameLines(stdout);\n } catch (err) {\n log.logWarning(\n \"Failed to list legacy managed containers\",\n err instanceof Error ? err.message : String(err),\n );\n return [];\n }\n }\n\n private parseNameLines(stdout: string): string[] {\n return stdout\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter((line) => line.length > 0);\n }\n\n private async inspectContainerDetails(\n containerName: string,\n ): Promise<{ running: boolean; startedAtMs?: number; vaultId?: string } | undefined> {\n try {\n const { stdout } = await this.execFileImpl(\"docker\", [\n \"inspect\",\n \"-f\",\n `{{.State.Running}}\\t{{.State.StartedAt}}\\t{{index .Config.Labels \"${DockerContainerManager.VAULT_ID_LABEL_KEY}\"}}`,\n containerName,\n ]);\n const [runningRaw, startedAtRaw, vaultIdRaw] = stdout.trim().split(\"\\t\");\n const running = runningRaw === \"true\";\n const startedAtMs = this.parseDockerTimestamp(startedAtRaw);\n const vaultId = this.normalizeDockerValue(vaultIdRaw);\n return { running, startedAtMs, vaultId };\n } catch (err) {\n log.logWarning(\n `Failed to inspect container ${containerName} during reconcile`,\n err instanceof Error ? err.message : String(err),\n );\n return undefined;\n }\n }\n\n private normalizeDockerValue(value?: string): string | undefined {\n if (!value || value === \"<no value>\") return undefined;\n const trimmed = value.trim();\n return trimmed.length > 0 ? trimmed : undefined;\n }\n\n private parseDockerTimestamp(value?: string): number | undefined {\n const normalized = this.normalizeDockerValue(value);\n if (!normalized || normalized.startsWith(\"0001-\")) return undefined;\n const parsed = Date.parse(normalized);\n return Number.isNaN(parsed) ? undefined : parsed;\n }\n\n private vaultIdFromContainerName(containerName: string): string | undefined {\n const prefix = DockerContainerManager.containerName(\"\");\n if (!containerName.startsWith(prefix)) return undefined;\n const vaultId = containerName.slice(prefix.length);\n return vaultId.length > 0 ? vaultId : undefined;\n }\n}\n\n/** @deprecated Use DockerContainerManager */\nexport const DockerProvisioner = DockerContainerManager;\n"]}
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
import { execFile } from "child_process";
|
|
2
|
+
import { promisify } from "util";
|
|
3
|
+
import * as log from "./log.js";
|
|
4
|
+
const execFileAsync = promisify(execFile);
|
|
5
|
+
// ── DockerContainerManager ─────────────────────────────────────────────────────
|
|
6
|
+
/**
|
|
7
|
+
* Manages the lifecycle of per-user Docker containers.
|
|
8
|
+
*
|
|
9
|
+
* Tracks each container's status in memory (running / stopped / missing).
|
|
10
|
+
* State is always verified against Docker on provision(), so in-memory state
|
|
11
|
+
* stays accurate without polling.
|
|
12
|
+
*/
|
|
13
|
+
export class DockerContainerManager {
|
|
14
|
+
static { this.MANAGED_LABEL = "mama.managed=true"; }
|
|
15
|
+
static { this.IMAGE_MODE_LABEL = "mama.sandbox=image"; }
|
|
16
|
+
static { this.VAULT_ID_LABEL_KEY = "mama.vault-id"; }
|
|
17
|
+
constructor(image, workspaceDir, execFileImpl = execFileAsync) {
|
|
18
|
+
this.image = image;
|
|
19
|
+
this.workspaceDir = workspaceDir;
|
|
20
|
+
this.execFileImpl = execFileImpl;
|
|
21
|
+
this.state = new Map();
|
|
22
|
+
/**
|
|
23
|
+
* In-flight provision() calls per vaultId. A concurrent second call for the
|
|
24
|
+
* same user piggybacks on the first docker start/run instead of racing —
|
|
25
|
+
* without this, two parallel messages from one user could produce duplicate
|
|
26
|
+
* containers or conflict on docker run.
|
|
27
|
+
*/
|
|
28
|
+
this.inflight = new Map();
|
|
29
|
+
}
|
|
30
|
+
/** Sanitize an identifier segment for use in vault keys and container names. */
|
|
31
|
+
static sanitizeSegment(value) {
|
|
32
|
+
const sanitized = value
|
|
33
|
+
.toLowerCase()
|
|
34
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
35
|
+
.replace(/^-+|-+$/g, "");
|
|
36
|
+
return sanitized || "unknown";
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Deterministic vault key for a platform user.
|
|
40
|
+
* e.g. ("slack", "U04ABC") → "slack-u04abc"
|
|
41
|
+
*/
|
|
42
|
+
static vaultId(platform, platformUserId) {
|
|
43
|
+
return `${DockerContainerManager.sanitizeSegment(platform)}-${DockerContainerManager.sanitizeSegment(platformUserId)}`;
|
|
44
|
+
}
|
|
45
|
+
/** Deterministic container name for a vault-backed user sandbox. */
|
|
46
|
+
static containerName(vaultId) {
|
|
47
|
+
return `mama-sandbox-${vaultId}`;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Ensure a container exists and is running for the given vaultId.
|
|
51
|
+
* Always inspects the actual Docker state, then acts accordingly:
|
|
52
|
+
* - running → no-op
|
|
53
|
+
* - stopped → docker start
|
|
54
|
+
* - missing → docker run
|
|
55
|
+
*
|
|
56
|
+
* Returns the container name.
|
|
57
|
+
*/
|
|
58
|
+
async provision(vaultId, options = {}) {
|
|
59
|
+
const existing = this.inflight.get(vaultId);
|
|
60
|
+
if (existing)
|
|
61
|
+
return existing;
|
|
62
|
+
const pending = this.provisionInner(vaultId, options).finally(() => {
|
|
63
|
+
this.inflight.delete(vaultId);
|
|
64
|
+
});
|
|
65
|
+
this.inflight.set(vaultId, pending);
|
|
66
|
+
return pending;
|
|
67
|
+
}
|
|
68
|
+
async provisionInner(vaultId, options) {
|
|
69
|
+
const containerName = options.containerName ?? DockerContainerManager.containerName(vaultId);
|
|
70
|
+
const mounts = options.mounts ?? [];
|
|
71
|
+
const status = await this.inspectStatus(containerName);
|
|
72
|
+
try {
|
|
73
|
+
if (status !== "missing" && (await this.hasBindMountDrift(containerName, mounts))) {
|
|
74
|
+
log.logInfo(`Container ${containerName} mounts changed; recreating container`);
|
|
75
|
+
await this.execFileImpl("docker", ["rm", "-f", containerName]);
|
|
76
|
+
await this.runContainer(vaultId, containerName, mounts);
|
|
77
|
+
log.logInfo(`Container ${containerName} recreated`);
|
|
78
|
+
}
|
|
79
|
+
else if (status === "running") {
|
|
80
|
+
log.logInfo(`Container ${containerName} already running`);
|
|
81
|
+
}
|
|
82
|
+
else if (status === "stopped") {
|
|
83
|
+
await this.execFileImpl("docker", ["start", containerName]);
|
|
84
|
+
log.logInfo(`Container ${containerName} started`);
|
|
85
|
+
}
|
|
86
|
+
else {
|
|
87
|
+
await this.runContainer(vaultId, containerName, mounts);
|
|
88
|
+
log.logInfo(`Container ${containerName} created`);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
catch (err) {
|
|
92
|
+
// Drop cached state so the next provision() re-inspects Docker cleanly
|
|
93
|
+
// and stopIdle doesn't keep trying to stop a container that never
|
|
94
|
+
// became running. We deliberately don't bump lastUsed here.
|
|
95
|
+
this.state.delete(vaultId);
|
|
96
|
+
throw err;
|
|
97
|
+
}
|
|
98
|
+
this.setState(vaultId, "running", containerName);
|
|
99
|
+
return containerName;
|
|
100
|
+
}
|
|
101
|
+
/**
|
|
102
|
+
* Stop a running container (docker stop). Container is preserved and can be
|
|
103
|
+
* restarted via provision(). Intended for idle lifecycle management.
|
|
104
|
+
*/
|
|
105
|
+
async stop(vaultId) {
|
|
106
|
+
const containerName = this.getContainerName(vaultId);
|
|
107
|
+
try {
|
|
108
|
+
await this.execFileImpl("docker", ["stop", containerName]);
|
|
109
|
+
this.setState(vaultId, "stopped", containerName);
|
|
110
|
+
log.logInfo(`Container ${containerName} stopped (idle)`);
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
log.logWarning(`Failed to stop container ${containerName}`, err instanceof Error ? err.message : String(err));
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Stop and remove a container permanently (e.g. on vault revocation). */
|
|
117
|
+
async remove(vaultId) {
|
|
118
|
+
const containerName = this.getContainerName(vaultId);
|
|
119
|
+
try {
|
|
120
|
+
await this.execFileImpl("docker", ["rm", "-f", containerName]);
|
|
121
|
+
this.state.delete(vaultId);
|
|
122
|
+
log.logInfo(`Container ${containerName} removed`);
|
|
123
|
+
}
|
|
124
|
+
catch (err) {
|
|
125
|
+
log.logWarning(`Failed to remove container ${containerName}`, err instanceof Error ? err.message : String(err));
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Stop all containers that have been idle for longer than maxIdleMs.
|
|
130
|
+
* Idle time is measured from the last provision() call.
|
|
131
|
+
*/
|
|
132
|
+
async stopIdle(maxIdleMs) {
|
|
133
|
+
const now = Date.now();
|
|
134
|
+
const toStop = [];
|
|
135
|
+
for (const [vaultId, containerState] of this.state) {
|
|
136
|
+
if (containerState.status === "running" && now - containerState.lastUsed > maxIdleMs) {
|
|
137
|
+
toStop.push(vaultId);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
await Promise.all(toStop.map((vaultId) => this.stop(vaultId)));
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Rebuild in-memory state from existing Docker containers managed by mama image mode.
|
|
144
|
+
* Supports both new labeled containers and legacy name-prefixed containers.
|
|
145
|
+
*/
|
|
146
|
+
async reconcile() {
|
|
147
|
+
const discovered = new Set();
|
|
148
|
+
const labeledNames = await this.listContainerNamesByLabel();
|
|
149
|
+
for (const name of labeledNames)
|
|
150
|
+
discovered.add(name);
|
|
151
|
+
const legacyNames = await this.listContainerNamesByPrefix();
|
|
152
|
+
for (const name of legacyNames)
|
|
153
|
+
discovered.add(name);
|
|
154
|
+
this.state.clear();
|
|
155
|
+
for (const containerName of discovered) {
|
|
156
|
+
const details = await this.inspectContainerDetails(containerName);
|
|
157
|
+
if (!details)
|
|
158
|
+
continue;
|
|
159
|
+
const vaultId = details.vaultId || this.vaultIdFromContainerName(containerName);
|
|
160
|
+
if (!vaultId) {
|
|
161
|
+
log.logWarning(`Skipping unmanaged-style container without vault id`, containerName);
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
const status = details.running ? "running" : "stopped";
|
|
165
|
+
const lastUsed = details.startedAtMs ?? Date.now();
|
|
166
|
+
this.state.set(vaultId, { status, lastUsed, containerName });
|
|
167
|
+
}
|
|
168
|
+
const running = Array.from(this.state.values()).filter((s) => s.status === "running").length;
|
|
169
|
+
const stopped = this.state.size - running;
|
|
170
|
+
log.logInfo(`Reconciled ${this.state.size} managed containers (running=${running}, stopped=${stopped})`);
|
|
171
|
+
}
|
|
172
|
+
setState(vaultId, status, containerName) {
|
|
173
|
+
this.state.set(vaultId, { status, lastUsed: Date.now(), containerName });
|
|
174
|
+
}
|
|
175
|
+
getContainerName(vaultId) {
|
|
176
|
+
return this.state.get(vaultId)?.containerName ?? DockerContainerManager.containerName(vaultId);
|
|
177
|
+
}
|
|
178
|
+
mountArgs(mounts) {
|
|
179
|
+
return mounts.flatMap((mount) => ["-v", this.toBindSpec(mount)]);
|
|
180
|
+
}
|
|
181
|
+
toBindSpec(mount) {
|
|
182
|
+
return `${mount.source}:${mount.target}`;
|
|
183
|
+
}
|
|
184
|
+
async runContainer(vaultId, containerName, mounts) {
|
|
185
|
+
log.logInfo(`Creating container ${containerName} from image ${this.image}`);
|
|
186
|
+
await this.execFileImpl("docker", [
|
|
187
|
+
"run",
|
|
188
|
+
"-d",
|
|
189
|
+
"--name",
|
|
190
|
+
containerName,
|
|
191
|
+
"--label",
|
|
192
|
+
DockerContainerManager.MANAGED_LABEL,
|
|
193
|
+
"--label",
|
|
194
|
+
DockerContainerManager.IMAGE_MODE_LABEL,
|
|
195
|
+
"--label",
|
|
196
|
+
`${DockerContainerManager.VAULT_ID_LABEL_KEY}=${vaultId}`,
|
|
197
|
+
"-v",
|
|
198
|
+
`${this.workspaceDir}:/workspace`,
|
|
199
|
+
...this.mountArgs(mounts),
|
|
200
|
+
this.image,
|
|
201
|
+
"sleep",
|
|
202
|
+
"infinity",
|
|
203
|
+
]);
|
|
204
|
+
}
|
|
205
|
+
async hasBindMountDrift(containerName, mounts) {
|
|
206
|
+
const expected = this.expectedBinds(mounts);
|
|
207
|
+
const actual = await this.inspectBindMounts(containerName);
|
|
208
|
+
return !this.sameBinds(expected, actual);
|
|
209
|
+
}
|
|
210
|
+
expectedBinds(mounts) {
|
|
211
|
+
return [`${this.workspaceDir}:/workspace`, ...mounts.map((mount) => this.toBindSpec(mount))]
|
|
212
|
+
.slice()
|
|
213
|
+
.sort();
|
|
214
|
+
}
|
|
215
|
+
sameBinds(expected, actual) {
|
|
216
|
+
if (expected.length !== actual.length) {
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
return expected.every((bind, index) => bind === actual[index]);
|
|
220
|
+
}
|
|
221
|
+
async inspectBindMounts(containerName) {
|
|
222
|
+
const { stdout } = await this.execFileImpl("docker", [
|
|
223
|
+
"inspect",
|
|
224
|
+
"-f",
|
|
225
|
+
"{{json .HostConfig.Binds}}",
|
|
226
|
+
containerName,
|
|
227
|
+
]);
|
|
228
|
+
const payload = stdout.trim();
|
|
229
|
+
const parsed = JSON.parse(payload.length > 0 ? payload : "null");
|
|
230
|
+
if (parsed === null) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
if (!Array.isArray(parsed) || parsed.some((bind) => typeof bind !== "string")) {
|
|
234
|
+
throw new Error(`Unexpected docker bind mount payload for container "${containerName}"`);
|
|
235
|
+
}
|
|
236
|
+
return [...parsed].sort();
|
|
237
|
+
}
|
|
238
|
+
async inspectStatus(containerName) {
|
|
239
|
+
try {
|
|
240
|
+
const { stdout } = await this.execFileImpl("docker", [
|
|
241
|
+
"inspect",
|
|
242
|
+
"-f",
|
|
243
|
+
"{{.State.Running}}",
|
|
244
|
+
containerName,
|
|
245
|
+
]);
|
|
246
|
+
return stdout.trim() === "true" ? "running" : "stopped";
|
|
247
|
+
}
|
|
248
|
+
catch {
|
|
249
|
+
return "missing";
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
async listContainerNamesByLabel() {
|
|
253
|
+
try {
|
|
254
|
+
const { stdout } = await this.execFileImpl("docker", [
|
|
255
|
+
"ps",
|
|
256
|
+
"-a",
|
|
257
|
+
"--filter",
|
|
258
|
+
`label=${DockerContainerManager.MANAGED_LABEL}`,
|
|
259
|
+
"--filter",
|
|
260
|
+
`label=${DockerContainerManager.IMAGE_MODE_LABEL}`,
|
|
261
|
+
"--format",
|
|
262
|
+
"{{.Names}}",
|
|
263
|
+
]);
|
|
264
|
+
return this.parseNameLines(stdout);
|
|
265
|
+
}
|
|
266
|
+
catch (err) {
|
|
267
|
+
log.logWarning("Failed to list labeled managed containers", err instanceof Error ? err.message : String(err));
|
|
268
|
+
return [];
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
async listContainerNamesByPrefix() {
|
|
272
|
+
try {
|
|
273
|
+
const { stdout } = await this.execFileImpl("docker", [
|
|
274
|
+
"ps",
|
|
275
|
+
"-a",
|
|
276
|
+
"--filter",
|
|
277
|
+
`name=${DockerContainerManager.containerName("")}`,
|
|
278
|
+
"--format",
|
|
279
|
+
"{{.Names}}",
|
|
280
|
+
]);
|
|
281
|
+
return this.parseNameLines(stdout);
|
|
282
|
+
}
|
|
283
|
+
catch (err) {
|
|
284
|
+
log.logWarning("Failed to list legacy managed containers", err instanceof Error ? err.message : String(err));
|
|
285
|
+
return [];
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
parseNameLines(stdout) {
|
|
289
|
+
return stdout
|
|
290
|
+
.split("\n")
|
|
291
|
+
.map((line) => line.trim())
|
|
292
|
+
.filter((line) => line.length > 0);
|
|
293
|
+
}
|
|
294
|
+
async inspectContainerDetails(containerName) {
|
|
295
|
+
try {
|
|
296
|
+
const { stdout } = await this.execFileImpl("docker", [
|
|
297
|
+
"inspect",
|
|
298
|
+
"-f",
|
|
299
|
+
`{{.State.Running}}\t{{.State.StartedAt}}\t{{index .Config.Labels "${DockerContainerManager.VAULT_ID_LABEL_KEY}"}}`,
|
|
300
|
+
containerName,
|
|
301
|
+
]);
|
|
302
|
+
const [runningRaw, startedAtRaw, vaultIdRaw] = stdout.trim().split("\t");
|
|
303
|
+
const running = runningRaw === "true";
|
|
304
|
+
const startedAtMs = this.parseDockerTimestamp(startedAtRaw);
|
|
305
|
+
const vaultId = this.normalizeDockerValue(vaultIdRaw);
|
|
306
|
+
return { running, startedAtMs, vaultId };
|
|
307
|
+
}
|
|
308
|
+
catch (err) {
|
|
309
|
+
log.logWarning(`Failed to inspect container ${containerName} during reconcile`, err instanceof Error ? err.message : String(err));
|
|
310
|
+
return undefined;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
normalizeDockerValue(value) {
|
|
314
|
+
if (!value || value === "<no value>")
|
|
315
|
+
return undefined;
|
|
316
|
+
const trimmed = value.trim();
|
|
317
|
+
return trimmed.length > 0 ? trimmed : undefined;
|
|
318
|
+
}
|
|
319
|
+
parseDockerTimestamp(value) {
|
|
320
|
+
const normalized = this.normalizeDockerValue(value);
|
|
321
|
+
if (!normalized || normalized.startsWith("0001-"))
|
|
322
|
+
return undefined;
|
|
323
|
+
const parsed = Date.parse(normalized);
|
|
324
|
+
return Number.isNaN(parsed) ? undefined : parsed;
|
|
325
|
+
}
|
|
326
|
+
vaultIdFromContainerName(containerName) {
|
|
327
|
+
const prefix = DockerContainerManager.containerName("");
|
|
328
|
+
if (!containerName.startsWith(prefix))
|
|
329
|
+
return undefined;
|
|
330
|
+
const vaultId = containerName.slice(prefix.length);
|
|
331
|
+
return vaultId.length > 0 ? vaultId : undefined;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
/** @deprecated Use DockerContainerManager */
|
|
335
|
+
export const DockerProvisioner = DockerContainerManager;
|
|
336
|
+
//# sourceMappingURL=provisioner.js.map
|