multi-project-gateway 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +21 -6
- package/dist/cli.js +15 -2
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,9 +13,10 @@ Discord reply <-- Chunker <---------------------- JSON response <------'
|
|
|
13
13
|
|
|
14
14
|
1. User posts a message in a mapped Discord channel
|
|
15
15
|
2. Router resolves the channel to a project config
|
|
16
|
-
3.
|
|
17
|
-
4.
|
|
18
|
-
5.
|
|
16
|
+
3. If the message is in a main channel, the bot creates a thread for the response; if already in a thread, replies there directly
|
|
17
|
+
4. Session manager spawns `claude --print` in the project directory (or resumes an existing session)
|
|
18
|
+
5. Response is chunked to fit Discord's 2000-char limit and sent back in the thread
|
|
19
|
+
6. Sessions persist to disk and resume across gateway restarts
|
|
19
20
|
|
|
20
21
|
## Security model
|
|
21
22
|
|
|
@@ -166,6 +167,19 @@ claude --resume <session-id>
|
|
|
166
167
|
|
|
167
168
|
**Important:** You must run `claude --resume` from the same directory the session was started in (i.e., the project's `directory` in `config.json`). Claude will not find the session if you run it from a different working directory.
|
|
168
169
|
|
|
170
|
+
## Threading and per-thread sessions
|
|
171
|
+
|
|
172
|
+
When a user posts a message in a mapped channel, the bot automatically creates a Discord thread and replies there instead of cluttering the main channel. Follow-up messages within the thread continue the same conversation.
|
|
173
|
+
|
|
174
|
+
Each thread gets its **own Claude session**, isolated from the main channel and other threads. This means:
|
|
175
|
+
|
|
176
|
+
- Multiple users can work in the same project channel without their conversations interleaving
|
|
177
|
+
- Each thread maintains its own context and history
|
|
178
|
+
- The thread inherits the project config (directory, Claude args) from the parent channel
|
|
179
|
+
- Threads auto-archive after 60 minutes of inactivity
|
|
180
|
+
|
|
181
|
+
If thread creation fails (e.g., due to permissions), the bot falls back to replying in the main channel.
|
|
182
|
+
|
|
169
183
|
## Discord commands
|
|
170
184
|
|
|
171
185
|
The gateway responds to commands in any mapped Discord channel:
|
|
@@ -174,6 +188,7 @@ The gateway responds to commands in any mapped Discord channel:
|
|
|
174
188
|
|---------|-------------|
|
|
175
189
|
| `!sessions` | List all active sessions with idle time and queue depth |
|
|
176
190
|
| `!session <name>` | Inspect a specific project's session (ID, idle time, queue) |
|
|
191
|
+
| `!restart <name>` | Reset a session (fresh context, keeps worktree) |
|
|
177
192
|
| `!kill <name>` | Force-close a project's session |
|
|
178
193
|
| `!help` | Show available commands |
|
|
179
194
|
|
|
@@ -184,8 +199,8 @@ The gateway responds to commands in any mapped Discord channel:
|
|
|
184
199
|
| `src/cli.ts` | CLI entry point — `mpg start`, `mpg init`, `mpg status` |
|
|
185
200
|
| `src/init.ts` | Interactive setup wizard |
|
|
186
201
|
| `src/config.ts` | Validates and merges `config.json` with defaults |
|
|
187
|
-
| `src/router.ts` | Maps channel IDs to project configs
|
|
188
|
-
| `src/session-manager.ts` | One session per
|
|
202
|
+
| `src/router.ts` | Maps channel IDs to project configs; threads resolve to their own session using the parent channel's project config |
|
|
203
|
+
| `src/session-manager.ts` | One session per channel/thread, queues concurrent messages, manages idle timeouts |
|
|
189
204
|
| `src/session-store.ts` | Persists session IDs to `.sessions.json` for resume across restarts |
|
|
190
205
|
| `src/claude-cli.ts` | Spawns `claude --print` subprocess, parses JSON output |
|
|
191
206
|
| `src/discord.ts` | Discord.js client, message routing, response chunking |
|
|
@@ -204,7 +219,7 @@ The gateway responds to commands in any mapped Discord channel:
|
|
|
204
219
|
|
|
205
220
|
- **Text only** — attachments and embeds are not forwarded to Claude
|
|
206
221
|
- **One message at a time per project** — concurrent messages to the same project are queued
|
|
207
|
-
- **
|
|
222
|
+
- **Per-thread sessions** — each thread gets its own Claude session scoped to the parent channel's project; threads auto-archive after 60 minutes of inactivity
|
|
208
223
|
- **Local only** — the gateway runs on the same machine as the project directories
|
|
209
224
|
- **No Discord access control** — any user in a mapped channel can send prompts; restrict channel access in Discord server settings
|
|
210
225
|
|
package/dist/cli.js
CHANGED
|
@@ -451,6 +451,19 @@ function createDiscordBot(router, sessionManager, config) {
|
|
|
451
451
|
await message.react("\u{1F440}");
|
|
452
452
|
} catch {
|
|
453
453
|
}
|
|
454
|
+
let replyChannel;
|
|
455
|
+
if (message.channel.isThread()) {
|
|
456
|
+
replyChannel = message.channel;
|
|
457
|
+
} else {
|
|
458
|
+
try {
|
|
459
|
+
replyChannel = await message.startThread({
|
|
460
|
+
name: message.content.slice(0, 100) || "Claude response",
|
|
461
|
+
autoArchiveDuration: 60
|
|
462
|
+
});
|
|
463
|
+
} catch {
|
|
464
|
+
replyChannel = message.channel;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
454
467
|
try {
|
|
455
468
|
const result = await sessionManager.send(
|
|
456
469
|
resolved.channelId,
|
|
@@ -459,11 +472,11 @@ function createDiscordBot(router, sessionManager, config) {
|
|
|
459
472
|
);
|
|
460
473
|
const chunks = chunkMessage(result.text, 2e3);
|
|
461
474
|
for (const chunk of chunks) {
|
|
462
|
-
await
|
|
475
|
+
await replyChannel.send(chunk);
|
|
463
476
|
}
|
|
464
477
|
} catch (err) {
|
|
465
478
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
466
|
-
await
|
|
479
|
+
await replyChannel.send(
|
|
467
480
|
`**Error** (${resolved.name}): ${errorMsg.slice(0, 1800)}`
|
|
468
481
|
);
|
|
469
482
|
}
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/router.ts","../src/claude-cli.ts","../src/session-manager.ts","../src/session-store.ts","../src/discord.ts","../src/init.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { config as loadEnv } from 'dotenv';\nimport { loadConfig } from './config.js';\nimport { createRouter } from './router.js';\nimport { createSessionManager } from './session-manager.js';\nimport { createFileSessionStore } from './session-store.js';\nimport { createDiscordBot } from './discord.js';\nimport { runInit } from './init.js';\n\nconst args = process.argv.slice(2);\nconst command = args[0] ?? 'start';\n\nasync function main() {\n switch (command) {\n case 'start':\n return start();\n case 'init':\n return runInit();\n case 'status':\n return status();\n case 'help':\n case '--help':\n case '-h':\n return help();\n case '--version':\n case '-v':\n return version();\n default:\n console.error(`Unknown command: ${command}`);\n help();\n process.exit(1);\n }\n}\n\nfunction help() {\n console.log(`\nmpg — multi-project gateway for Claude Code\n\nUsage: mpg <command>\n\nCommands:\n start Start the gateway (default)\n init Interactive setup wizard\n status Show session status\n help Show this message\n\nOptions:\n -v, --version Show version\n -h, --help Show this message\n`.trim());\n}\n\nfunction version() {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));\n console.log(`mpg v${pkg.version}`);\n}\n\nfunction start() {\n const configDir = process.cwd();\n const envPath = resolve(configDir, '.env');\n if (existsSync(envPath)) {\n loadEnv({ path: envPath });\n }\n\n const token = process.env.DISCORD_BOT_TOKEN;\n if (!token) {\n console.error('DISCORD_BOT_TOKEN is not set. Run `mpg init` or set it in .env');\n process.exit(1);\n }\n\n const configPath = resolve(configDir, 'config.json');\n if (!existsSync(configPath)) {\n console.error('config.json not found. Run `mpg init` to create one.');\n process.exit(1);\n }\n\n const rawConfig = JSON.parse(readFileSync(configPath, 'utf-8'));\n const config = loadConfig(rawConfig);\n\n const projectCount = Object.keys(config.projects).length;\n if (projectCount === 0) {\n console.error('No projects configured in config.json');\n process.exit(1);\n }\n\n // Validate project directories exist\n for (const [channelId, project] of Object.entries(config.projects)) {\n if (!existsSync(project.directory)) {\n console.warn(`Warning: directory not found for ${project.name}: ${project.directory}`);\n }\n }\n\n console.log(`Loaded ${projectCount} project(s) from config`);\n\n const router = createRouter(config);\n const sessionStore = createFileSessionStore(resolve(configDir, '.sessions.json'));\n const sessionManager = createSessionManager(config.defaults, sessionStore);\n const bot = createDiscordBot(router, sessionManager, config);\n\n function shutdown() {\n console.log('Shutting down...');\n sessionManager.shutdown();\n bot.stop();\n process.exit(0);\n }\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n bot.start(token).catch((err) => {\n console.error('Failed to start bot:', err);\n process.exit(1);\n });\n}\n\nfunction status() {\n const configDir = process.cwd();\n const sessionsPath = resolve(configDir, '.sessions.json');\n\n if (!existsSync(sessionsPath)) {\n console.log('No sessions file found. Is the gateway running?');\n return;\n }\n\n const configPath = resolve(configDir, 'config.json');\n let projectNames: Record<string, string> = {};\n if (existsSync(configPath)) {\n try {\n const raw = JSON.parse(readFileSync(configPath, 'utf-8'));\n const config = loadConfig(raw);\n for (const [channelId, project] of Object.entries(config.projects)) {\n projectNames[channelId] = project.name;\n }\n } catch {\n // ignore config errors for status\n }\n }\n\n const sessions = JSON.parse(readFileSync(sessionsPath, 'utf-8')) as Array<{\n sessionId: string;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n }>;\n\n if (sessions.length === 0) {\n console.log('No active sessions.');\n return;\n }\n\n console.log(`Sessions (${sessions.length}):\\n`);\n for (const s of sessions) {\n const name = projectNames[s.projectKey] ?? s.projectKey;\n const ago = Math.floor((Date.now() - s.lastActivity) / 60000);\n console.log(` ${name}`);\n console.log(` Session: ${s.sessionId}`);\n console.log(` Dir: ${s.cwd}`);\n console.log(` Idle: ${ago}m`);\n console.log();\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","export interface ProjectConfig {\n name: string;\n directory: string;\n idleTimeoutMs?: number;\n claudeArgs?: string[];\n}\n\nexport interface GatewayDefaults {\n idleTimeoutMs: number;\n maxConcurrentSessions: number;\n claudeArgs: string[];\n}\n\nexport interface GatewayConfig {\n defaults: GatewayDefaults;\n projects: Record<string, ProjectConfig>;\n}\n\nexport function loadConfig(raw: unknown): GatewayConfig {\n if (!raw || typeof raw !== 'object') {\n throw new Error('Config must be an object');\n }\n\n const obj = raw as Record<string, unknown>;\n\n if (!obj.projects || typeof obj.projects !== 'object') {\n throw new Error('Config must have a \"projects\" object');\n }\n\n const projects = obj.projects as Record<string, unknown>;\n const validated: Record<string, ProjectConfig> = {};\n\n for (const [channelId, project] of Object.entries(projects)) {\n if (!project || typeof project !== 'object') {\n throw new Error(`Project for channel ${channelId} must be an object`);\n }\n const p = project as Record<string, unknown>;\n if (typeof p.directory !== 'string' || !p.directory) {\n throw new Error(`Project for channel ${channelId} must have a \"directory\" string`);\n }\n validated[channelId] = {\n name: typeof p.name === 'string' ? p.name : channelId,\n directory: p.directory,\n ...(p.idleTimeoutMs !== undefined && { idleTimeoutMs: Number(p.idleTimeoutMs) }),\n ...(Array.isArray(p.claudeArgs) && { claudeArgs: p.claudeArgs as string[] }),\n };\n }\n\n const defaults = (obj.defaults ?? {}) as Record<string, unknown>;\n\n return {\n defaults: {\n idleTimeoutMs: typeof defaults.idleTimeoutMs === 'number' ? defaults.idleTimeoutMs : 1800000,\n maxConcurrentSessions: typeof defaults.maxConcurrentSessions === 'number' ? defaults.maxConcurrentSessions : 4,\n claudeArgs: Array.isArray(defaults.claudeArgs) ? (defaults.claudeArgs as string[]) : ['--permission-mode', 'acceptEdits', '--output-format', 'json'],\n },\n projects: validated,\n };\n}\n","import type { GatewayConfig, ProjectConfig } from './config.js';\n\nexport interface ResolvedProject {\n channelId: string;\n name: string;\n directory: string;\n}\n\nexport interface Router {\n resolve(channelId: string, parentChannelId?: string): ResolvedProject | null;\n}\n\nexport function createRouter(config: GatewayConfig): Router {\n return {\n resolve(channelId: string, parentChannelId?: string): ResolvedProject | null {\n const project = config.projects[channelId];\n if (project) {\n return { channelId, name: project.name, directory: project.directory };\n }\n\n if (parentChannelId) {\n const parentProject = config.projects[parentChannelId];\n if (parentProject) {\n return { channelId: parentChannelId, name: parentProject.name, directory: parentProject.directory };\n }\n }\n\n return null;\n },\n };\n}\n","import { spawn } from 'node:child_process';\n\nexport interface ClaudeResult {\n text: string;\n sessionId: string;\n isError: boolean;\n}\n\nexport function parseClaudeJsonOutput(raw: string): ClaudeResult {\n const data = JSON.parse(raw);\n return {\n text: data.result ?? '',\n sessionId: data.session_id ?? '',\n isError: Boolean(data.is_error),\n };\n}\n\nexport function buildClaudeArgs(\n baseArgs: string[],\n prompt: string,\n sessionId: string | undefined,\n): string[] {\n const args = ['--print', ...baseArgs];\n if (sessionId) {\n args.push('--resume', sessionId);\n }\n args.push(prompt);\n return args;\n}\n\nexport function runClaude(\n cwd: string,\n baseArgs: string[],\n prompt: string,\n sessionId: string | undefined,\n): Promise<ClaudeResult> {\n return new Promise((resolve, reject) => {\n const args = buildClaudeArgs(baseArgs, prompt, sessionId);\n const proc = spawn('claude', args, {\n cwd,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n proc.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n\n proc.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n proc.on('close', (code) => {\n if (code !== 0) {\n reject(new Error(`claude exited with code ${code}: ${stderr}`));\n return;\n }\n try {\n const result = parseClaudeJsonOutput(stdout.trim());\n resolve(result);\n } catch (err) {\n reject(new Error(`Failed to parse claude output: ${stdout.slice(0, 200)}`));\n }\n });\n\n proc.on('error', (err) => {\n reject(new Error(`Failed to spawn claude: ${err.message}`));\n });\n });\n}\n","import { runClaude, type ClaudeResult } from './claude-cli.js';\nimport type { SessionStore, PersistedSession } from './session-store.js';\n\nexport interface SessionInfo {\n sessionId: string;\n projectKey: string;\n lastActivity: number;\n queueLength: number;\n}\n\nexport interface SessionManager {\n send(projectKey: string, cwd: string, prompt: string): Promise<ClaudeResult>;\n getSession(projectKey: string): SessionInfo | undefined;\n listSessions(): SessionInfo[];\n clearSession(projectKey: string): boolean;\n shutdown(): void;\n}\n\ninterface InternalSession {\n sessionId: string | undefined;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n processing: boolean;\n queue: Array<{\n prompt: string;\n resolve: (result: ClaudeResult) => void;\n reject: (error: Error) => void;\n }>;\n idleTimer: ReturnType<typeof setTimeout> | null;\n}\n\nexport function createSessionManager(defaults: {\n idleTimeoutMs: number;\n maxConcurrentSessions: number;\n claudeArgs: string[];\n}, store?: SessionStore): SessionManager {\n const sessions = new Map<string, InternalSession>();\n\n let activeProcesses = 0;\n const waiters: Array<() => void> = [];\n\n function persistSessions(): void {\n if (!store) return;\n // Merge in-memory sessions with existing persisted data.\n // In-memory sessions take precedence; persisted-only entries are preserved.\n const persisted = store.load();\n for (const [key, s] of sessions) {\n if (s.sessionId) {\n persisted.set(key, {\n sessionId: s.sessionId,\n projectKey: s.projectKey,\n cwd: s.cwd,\n lastActivity: s.lastActivity,\n });\n }\n }\n store.save(persisted);\n }\n\n async function acquireSlot(): Promise<void> {\n if (activeProcesses < defaults.maxConcurrentSessions) {\n activeProcesses++;\n return;\n }\n return new Promise<void>((resolve) => {\n waiters.push(() => {\n activeProcesses++;\n resolve();\n });\n });\n }\n\n function releaseSlot(): void {\n activeProcesses--;\n const next = waiters.shift();\n if (next) next();\n }\n\n function resetIdleTimer(session: InternalSession) {\n if (session.idleTimer) clearTimeout(session.idleTimer);\n session.idleTimer = setTimeout(() => {\n // Remove from memory only; session ID stays on disk for later resume\n sessions.delete(session.projectKey);\n }, defaults.idleTimeoutMs);\n }\n\n async function processQueue(session: InternalSession): Promise<void> {\n if (session.processing || session.queue.length === 0) return;\n session.processing = true;\n\n while (session.queue.length > 0) {\n const item = session.queue.shift()!;\n await acquireSlot();\n try {\n const result = await runClaude(\n session.cwd,\n defaults.claudeArgs,\n item.prompt,\n session.sessionId,\n );\n session.sessionId = result.sessionId || session.sessionId;\n session.lastActivity = Date.now();\n resetIdleTimer(session);\n persistSessions();\n item.resolve(result);\n } catch (err) {\n if (session.sessionId) {\n session.sessionId = undefined;\n try {\n const result = await runClaude(session.cwd, defaults.claudeArgs, item.prompt, undefined);\n session.sessionId = result.sessionId || undefined;\n session.lastActivity = Date.now();\n resetIdleTimer(session);\n persistSessions();\n item.resolve(result);\n } catch (retryErr) {\n item.reject(retryErr instanceof Error ? retryErr : new Error(String(retryErr)));\n }\n } else {\n item.reject(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n releaseSlot();\n }\n }\n\n session.processing = false;\n }\n\n function getOrCreateSession(projectKey: string, cwd: string): InternalSession {\n let session = sessions.get(projectKey);\n if (!session) {\n // Check store for a previously persisted session ID\n let restoredSessionId: string | undefined;\n if (store) {\n const persisted = store.load();\n const entry = persisted.get(projectKey);\n if (entry?.sessionId) {\n restoredSessionId = entry.sessionId;\n }\n }\n\n session = {\n sessionId: restoredSessionId,\n projectKey,\n cwd,\n lastActivity: Date.now(),\n processing: false,\n queue: [],\n idleTimer: null,\n };\n sessions.set(projectKey, session);\n resetIdleTimer(session);\n }\n return session;\n }\n\n // Restore persisted sessions into memory at startup\n if (store) {\n const persisted = store.load();\n for (const [key, entry] of persisted) {\n sessions.set(key, {\n sessionId: entry.sessionId,\n projectKey: entry.projectKey,\n cwd: entry.cwd,\n lastActivity: entry.lastActivity,\n processing: false,\n queue: [],\n idleTimer: null,\n });\n }\n for (const session of sessions.values()) {\n resetIdleTimer(session);\n }\n if (persisted.size > 0) {\n console.log(`Restored ${persisted.size} session(s) from disk`);\n }\n }\n\n return {\n send(projectKey: string, cwd: string, prompt: string): Promise<ClaudeResult> {\n const session = getOrCreateSession(projectKey, cwd);\n return new Promise<ClaudeResult>((resolve, reject) => {\n session.queue.push({ prompt, resolve, reject });\n processQueue(session);\n });\n },\n\n getSession(projectKey: string): SessionInfo | undefined {\n const session = sessions.get(projectKey);\n if (!session) return undefined;\n return {\n sessionId: session.sessionId ?? '',\n projectKey: session.projectKey,\n lastActivity: session.lastActivity,\n queueLength: session.queue.length,\n };\n },\n\n listSessions(): SessionInfo[] {\n return Array.from(sessions.values()).map((s) => ({\n sessionId: s.sessionId ?? '',\n projectKey: s.projectKey,\n lastActivity: s.lastActivity,\n queueLength: s.queue.length,\n }));\n },\n\n clearSession(projectKey: string): boolean {\n const session = sessions.get(projectKey);\n if (!session) return false;\n if (session.idleTimer) clearTimeout(session.idleTimer);\n sessions.delete(projectKey);\n persistSessions();\n return true;\n },\n\n shutdown() {\n persistSessions();\n for (const session of sessions.values()) {\n if (session.idleTimer) clearTimeout(session.idleTimer);\n }\n sessions.clear();\n },\n };\n}\n","import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nexport interface PersistedSession {\n sessionId: string;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n}\n\nexport interface SessionStore {\n load(): Map<string, PersistedSession>;\n save(sessions: Map<string, PersistedSession>): void;\n}\n\nexport function createFileSessionStore(filePath: string): SessionStore {\n return {\n load(): Map<string, PersistedSession> {\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const entries: PersistedSession[] = JSON.parse(raw);\n const map = new Map<string, PersistedSession>();\n for (const entry of entries) {\n if (entry.sessionId && entry.projectKey) {\n map.set(entry.projectKey, entry);\n }\n }\n return map;\n } catch {\n return new Map();\n }\n },\n\n save(sessions: Map<string, PersistedSession>): void {\n const entries = Array.from(sessions.values());\n try {\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, JSON.stringify(entries, null, 2) + '\\n');\n } catch (err) {\n console.error('Failed to persist sessions:', err);\n }\n },\n };\n}\n","import { Client, GatewayIntentBits, Events, type Message } from 'discord.js';\nimport type { Router } from './router.js';\nimport type { SessionManager } from './session-manager.js';\nimport type { GatewayConfig } from './config.js';\n\nexport function chunkMessage(text: string, limit: number): string[] {\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const lines = text.split('\\n');\n let current = '';\n\n for (const line of lines) {\n if (line.length > limit) {\n if (current) {\n chunks.push(current);\n current = '';\n }\n for (let i = 0; i < line.length; i += limit) {\n chunks.push(line.slice(i, i + limit));\n }\n continue;\n }\n\n const candidate = current ? `${current}\\n${line}` : line;\n if (candidate.length > limit) {\n chunks.push(current);\n current = line;\n } else {\n current = candidate;\n }\n }\n\n if (current || chunks.length === 0) {\n chunks.push(current);\n }\n\n return chunks;\n}\n\nexport interface DiscordBot {\n start(token: string): Promise<void>;\n stop(): void;\n}\n\nfunction resolveProjectName(config: GatewayConfig, channelId: string): string {\n return config.projects[channelId]?.name ?? channelId;\n}\n\nfunction findProjectByName(config: GatewayConfig, name: string): { channelId: string; name: string } | null {\n const lower = name.toLowerCase();\n for (const [channelId, project] of Object.entries(config.projects)) {\n if (project.name.toLowerCase() === lower) {\n return { channelId, name: project.name };\n }\n }\n return null;\n}\n\nfunction formatTimeSince(timestamp: number): string {\n const seconds = Math.floor((Date.now() - timestamp) / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m ago`;\n}\n\nexport function handleCommand(\n command: string,\n config: GatewayConfig,\n sessionManager: SessionManager,\n): string | null {\n const parts = command.trim().split(/\\s+/);\n const cmd = parts[0]?.toLowerCase();\n\n if (cmd === '!sessions') {\n const allSessions = sessionManager.listSessions();\n if (allSessions.length === 0) {\n return 'No active sessions.';\n }\n const lines = allSessions.map((s) => {\n const name = resolveProjectName(config, s.projectKey);\n const idle = formatTimeSince(s.lastActivity);\n const queue = s.queueLength > 0 ? ` | queue: ${s.queueLength}` : '';\n const sid = s.sessionId ? ` | \\`${s.sessionId.slice(0, 8)}…\\`` : '';\n return `- **${name}** — last active ${idle}${queue}${sid}`;\n });\n return `**Active sessions (${allSessions.length})**\\n${lines.join('\\n')}`;\n }\n\n if (cmd === '!session') {\n const name = parts.slice(1).join(' ');\n if (!name) return 'Usage: `!session <project name>`';\n const project = findProjectByName(config, name);\n if (!project) return `No project found matching \"${name}\".`;\n const info = sessionManager.getSession(project.channelId);\n if (!info) return `**${project.name}** — no active session.`;\n const idle = formatTimeSince(info.lastActivity);\n const sid = info.sessionId || 'none';\n return [\n `**${project.name}**`,\n `Session ID: \\`${sid}\\``,\n `Last active: ${idle}`,\n `Queue depth: ${info.queueLength}`,\n ].join('\\n');\n }\n\n if (cmd === '!kill') {\n const name = parts.slice(1).join(' ');\n if (!name) return 'Usage: `!kill <project name>`';\n const project = findProjectByName(config, name);\n if (!project) return `No project found matching \"${name}\".`;\n const cleared = sessionManager.clearSession(project.channelId);\n if (cleared) return `Session for **${project.name}** cleared.`;\n return `**${project.name}** — no active session to clear.`;\n }\n\n if (cmd === '!help') {\n return [\n '**Gateway commands**',\n '`!sessions` — list all active sessions',\n '`!session <name>` — inspect a specific project session',\n '`!kill <name>` — force-close a project session',\n '`!help` — show this message',\n ].join('\\n');\n }\n\n return null;\n}\n\nexport function createDiscordBot(router: Router, sessionManager: SessionManager, config: GatewayConfig): DiscordBot {\n const client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n ],\n });\n\n client.on(Events.MessageCreate, async (message: Message) => {\n if (message.author.bot) return;\n\n if (!('send' in message.channel)) return;\n\n // Handle gateway commands from any mapped channel\n if (message.content.startsWith('!')) {\n const parentId = message.channel.isThread() ? message.channel.parentId ?? undefined : undefined;\n const resolved = router.resolve(message.channelId, parentId);\n if (resolved) {\n const response = handleCommand(message.content, config, sessionManager);\n if (response) {\n await message.channel.send(response);\n return;\n }\n }\n }\n\n const parentId = message.channel.isThread() ? message.channel.parentId ?? undefined : undefined;\n const resolved = router.resolve(message.channelId, parentId);\n if (!resolved) return;\n\n try {\n await message.react('👀');\n } catch {\n // Reaction may fail if permissions are missing — non-critical\n }\n\n try {\n const result = await sessionManager.send(\n resolved.channelId,\n resolved.directory,\n message.content,\n );\n\n const chunks = chunkMessage(result.text, 2000);\n for (const chunk of chunks) {\n await message.channel.send(chunk);\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n await message.channel.send(\n `**Error** (${resolved.name}): ${errorMsg.slice(0, 1800)}`,\n );\n }\n });\n\n return {\n async start(token: string) {\n await client.login(token);\n console.log(`Gateway connected as ${client.user?.tag}`);\n },\n stop() {\n client.destroy();\n },\n };\n}\n","import { createInterface } from 'node:readline';\nimport { writeFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nfunction createPrompt(): (question: string) => Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return (question: string) =>\n new Promise((resolve) => rl.question(question, (answer) => resolve(answer.trim())));\n}\n\nexport async function runInit() {\n const ask = createPrompt();\n const configDir = process.cwd();\n\n console.log('\\nmpg init — set up multi-project gateway\\n');\n\n // Check for claude CLI\n try {\n execSync('claude --version', { stdio: 'pipe' });\n console.log('Claude CLI found.');\n } catch {\n console.warn('Warning: `claude` not found on PATH. Make sure it is installed before starting the gateway.');\n }\n\n // Discord bot token\n let token = process.env.DISCORD_BOT_TOKEN ?? '';\n const inputToken = await ask(`Discord bot token${token ? ' (press Enter to keep existing)' : ''}: `);\n if (inputToken) token = inputToken;\n if (!token) {\n console.error('A Discord bot token is required. Create one at https://discord.com/developers/applications');\n process.exit(1);\n }\n\n // Write .env\n const envPath = resolve(configDir, '.env');\n writeFileSync(envPath, `DISCORD_BOT_TOKEN=${token}\\n`);\n console.log(`Wrote ${envPath}`);\n\n // Collect projects\n interface ProjectEntry {\n name: string;\n directory: string;\n channelId: string;\n }\n\n const projects: ProjectEntry[] = [];\n\n // Load existing config if present\n const configPath = resolve(configDir, 'config.json');\n if (existsSync(configPath)) {\n try {\n const existing = JSON.parse(\n (await import('node:fs')).readFileSync(configPath, 'utf-8'),\n );\n if (existing.projects) {\n for (const [channelId, project] of Object.entries(existing.projects)) {\n const p = project as { name?: string; directory: string };\n projects.push({ name: p.name ?? channelId, directory: p.directory, channelId });\n }\n if (projects.length > 0) {\n console.log(`\\nExisting projects (${projects.length}):`);\n for (const p of projects) {\n console.log(` ${p.name} → ${p.directory} (${p.channelId})`);\n }\n }\n }\n } catch {\n // ignore parse errors\n }\n }\n\n console.log('\\nAdd projects (empty name to finish):\\n');\n\n while (true) {\n const name = await ask('Project name: ');\n if (!name) break;\n\n const directory = await ask('Project directory (absolute path): ');\n if (!directory) {\n console.log('Directory is required, skipping.');\n continue;\n }\n if (!existsSync(directory)) {\n console.warn(`Warning: ${directory} does not exist.`);\n }\n\n const channelId = await ask('Discord channel ID: ');\n if (!channelId) {\n console.log('Channel ID is required, skipping.');\n continue;\n }\n\n projects.push({ name, directory, channelId });\n console.log(`Added ${name}\\n`);\n }\n\n if (projects.length === 0) {\n console.log('No projects configured. You can edit config.json later.');\n }\n\n // Build config\n const config = {\n defaults: {\n idleTimeoutMs: 1800000,\n maxConcurrentSessions: 4,\n claudeArgs: ['--permission-mode', 'acceptEdits', '--output-format', 'json'],\n },\n projects: Object.fromEntries(\n projects.map((p) => [p.channelId, { name: p.name, directory: p.directory }]),\n ),\n };\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n');\n console.log(`Wrote ${configPath}`);\n\n console.log('\\nSetup complete! Run `mpg start` to launch the gateway.');\n}\n"],"mappings":"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","names":["resolve","existsSync","readFileSync","args","resolve","resolve","command","parentId","resolved","writeFileSync","resolve","readFileSync","resolve","existsSync"]}
|
|
1
|
+
{"version":3,"sources":["../src/cli.ts","../src/config.ts","../src/router.ts","../src/claude-cli.ts","../src/session-manager.ts","../src/session-store.ts","../src/discord.ts","../src/init.ts"],"sourcesContent":["import { resolve } from 'node:path';\nimport { existsSync, readFileSync } from 'node:fs';\nimport { config as loadEnv } from 'dotenv';\nimport { loadConfig } from './config.js';\nimport { createRouter } from './router.js';\nimport { createSessionManager } from './session-manager.js';\nimport { createFileSessionStore } from './session-store.js';\nimport { createDiscordBot } from './discord.js';\nimport { runInit } from './init.js';\n\nconst args = process.argv.slice(2);\nconst command = args[0] ?? 'start';\n\nasync function main() {\n switch (command) {\n case 'start':\n return start();\n case 'init':\n return runInit();\n case 'status':\n return status();\n case 'help':\n case '--help':\n case '-h':\n return help();\n case '--version':\n case '-v':\n return version();\n default:\n console.error(`Unknown command: ${command}`);\n help();\n process.exit(1);\n }\n}\n\nfunction help() {\n console.log(`\nmpg — multi-project gateway for Claude Code\n\nUsage: mpg <command>\n\nCommands:\n start Start the gateway (default)\n init Interactive setup wizard\n status Show session status\n help Show this message\n\nOptions:\n -v, --version Show version\n -h, --help Show this message\n`.trim());\n}\n\nfunction version() {\n const pkg = JSON.parse(readFileSync(new URL('../package.json', import.meta.url), 'utf-8'));\n console.log(`mpg v${pkg.version}`);\n}\n\nfunction start() {\n const configDir = process.cwd();\n const envPath = resolve(configDir, '.env');\n if (existsSync(envPath)) {\n loadEnv({ path: envPath });\n }\n\n const token = process.env.DISCORD_BOT_TOKEN;\n if (!token) {\n console.error('DISCORD_BOT_TOKEN is not set. Run `mpg init` or set it in .env');\n process.exit(1);\n }\n\n const configPath = resolve(configDir, 'config.json');\n if (!existsSync(configPath)) {\n console.error('config.json not found. Run `mpg init` to create one.');\n process.exit(1);\n }\n\n const rawConfig = JSON.parse(readFileSync(configPath, 'utf-8'));\n const config = loadConfig(rawConfig);\n\n const projectCount = Object.keys(config.projects).length;\n if (projectCount === 0) {\n console.error('No projects configured in config.json');\n process.exit(1);\n }\n\n // Validate project directories exist\n for (const [channelId, project] of Object.entries(config.projects)) {\n if (!existsSync(project.directory)) {\n console.warn(`Warning: directory not found for ${project.name}: ${project.directory}`);\n }\n }\n\n console.log(`Loaded ${projectCount} project(s) from config`);\n\n const router = createRouter(config);\n const sessionStore = createFileSessionStore(resolve(configDir, '.sessions.json'));\n const sessionManager = createSessionManager(config.defaults, sessionStore);\n const bot = createDiscordBot(router, sessionManager, config);\n\n function shutdown() {\n console.log('Shutting down...');\n sessionManager.shutdown();\n bot.stop();\n process.exit(0);\n }\n\n process.on('SIGINT', shutdown);\n process.on('SIGTERM', shutdown);\n\n bot.start(token).catch((err) => {\n console.error('Failed to start bot:', err);\n process.exit(1);\n });\n}\n\nfunction status() {\n const configDir = process.cwd();\n const sessionsPath = resolve(configDir, '.sessions.json');\n\n if (!existsSync(sessionsPath)) {\n console.log('No sessions file found. Is the gateway running?');\n return;\n }\n\n const configPath = resolve(configDir, 'config.json');\n let projectNames: Record<string, string> = {};\n if (existsSync(configPath)) {\n try {\n const raw = JSON.parse(readFileSync(configPath, 'utf-8'));\n const config = loadConfig(raw);\n for (const [channelId, project] of Object.entries(config.projects)) {\n projectNames[channelId] = project.name;\n }\n } catch {\n // ignore config errors for status\n }\n }\n\n const sessions = JSON.parse(readFileSync(sessionsPath, 'utf-8')) as Array<{\n sessionId: string;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n }>;\n\n if (sessions.length === 0) {\n console.log('No active sessions.');\n return;\n }\n\n console.log(`Sessions (${sessions.length}):\\n`);\n for (const s of sessions) {\n const name = projectNames[s.projectKey] ?? s.projectKey;\n const ago = Math.floor((Date.now() - s.lastActivity) / 60000);\n console.log(` ${name}`);\n console.log(` Session: ${s.sessionId}`);\n console.log(` Dir: ${s.cwd}`);\n console.log(` Idle: ${ago}m`);\n console.log();\n }\n}\n\nmain().catch((err) => {\n console.error(err);\n process.exit(1);\n});\n","export interface ProjectConfig {\n name: string;\n directory: string;\n idleTimeoutMs?: number;\n claudeArgs?: string[];\n}\n\nexport interface GatewayDefaults {\n idleTimeoutMs: number;\n maxConcurrentSessions: number;\n claudeArgs: string[];\n}\n\nexport interface GatewayConfig {\n defaults: GatewayDefaults;\n projects: Record<string, ProjectConfig>;\n}\n\nexport function loadConfig(raw: unknown): GatewayConfig {\n if (!raw || typeof raw !== 'object') {\n throw new Error('Config must be an object');\n }\n\n const obj = raw as Record<string, unknown>;\n\n if (!obj.projects || typeof obj.projects !== 'object') {\n throw new Error('Config must have a \"projects\" object');\n }\n\n const projects = obj.projects as Record<string, unknown>;\n const validated: Record<string, ProjectConfig> = {};\n\n for (const [channelId, project] of Object.entries(projects)) {\n if (!project || typeof project !== 'object') {\n throw new Error(`Project for channel ${channelId} must be an object`);\n }\n const p = project as Record<string, unknown>;\n if (typeof p.directory !== 'string' || !p.directory) {\n throw new Error(`Project for channel ${channelId} must have a \"directory\" string`);\n }\n validated[channelId] = {\n name: typeof p.name === 'string' ? p.name : channelId,\n directory: p.directory,\n ...(p.idleTimeoutMs !== undefined && { idleTimeoutMs: Number(p.idleTimeoutMs) }),\n ...(Array.isArray(p.claudeArgs) && { claudeArgs: p.claudeArgs as string[] }),\n };\n }\n\n const defaults = (obj.defaults ?? {}) as Record<string, unknown>;\n\n return {\n defaults: {\n idleTimeoutMs: typeof defaults.idleTimeoutMs === 'number' ? defaults.idleTimeoutMs : 1800000,\n maxConcurrentSessions: typeof defaults.maxConcurrentSessions === 'number' ? defaults.maxConcurrentSessions : 4,\n claudeArgs: Array.isArray(defaults.claudeArgs) ? (defaults.claudeArgs as string[]) : ['--permission-mode', 'acceptEdits', '--output-format', 'json'],\n },\n projects: validated,\n };\n}\n","import type { GatewayConfig, ProjectConfig } from './config.js';\n\nexport interface ResolvedProject {\n channelId: string;\n name: string;\n directory: string;\n}\n\nexport interface Router {\n resolve(channelId: string, parentChannelId?: string): ResolvedProject | null;\n}\n\nexport function createRouter(config: GatewayConfig): Router {\n return {\n resolve(channelId: string, parentChannelId?: string): ResolvedProject | null {\n const project = config.projects[channelId];\n if (project) {\n return { channelId, name: project.name, directory: project.directory };\n }\n\n if (parentChannelId) {\n const parentProject = config.projects[parentChannelId];\n if (parentProject) {\n return { channelId: parentChannelId, name: parentProject.name, directory: parentProject.directory };\n }\n }\n\n return null;\n },\n };\n}\n","import { spawn } from 'node:child_process';\n\nexport interface ClaudeResult {\n text: string;\n sessionId: string;\n isError: boolean;\n}\n\nexport function parseClaudeJsonOutput(raw: string): ClaudeResult {\n const data = JSON.parse(raw);\n return {\n text: data.result ?? '',\n sessionId: data.session_id ?? '',\n isError: Boolean(data.is_error),\n };\n}\n\nexport function buildClaudeArgs(\n baseArgs: string[],\n prompt: string,\n sessionId: string | undefined,\n): string[] {\n const args = ['--print', ...baseArgs];\n if (sessionId) {\n args.push('--resume', sessionId);\n }\n args.push(prompt);\n return args;\n}\n\nexport function runClaude(\n cwd: string,\n baseArgs: string[],\n prompt: string,\n sessionId: string | undefined,\n): Promise<ClaudeResult> {\n return new Promise((resolve, reject) => {\n const args = buildClaudeArgs(baseArgs, prompt, sessionId);\n const proc = spawn('claude', args, {\n cwd,\n stdio: ['ignore', 'pipe', 'pipe'],\n });\n\n let stdout = '';\n let stderr = '';\n\n proc.stdout.on('data', (chunk: Buffer) => {\n stdout += chunk.toString();\n });\n\n proc.stderr.on('data', (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n proc.on('close', (code) => {\n if (code !== 0) {\n reject(new Error(`claude exited with code ${code}: ${stderr}`));\n return;\n }\n try {\n const result = parseClaudeJsonOutput(stdout.trim());\n resolve(result);\n } catch (err) {\n reject(new Error(`Failed to parse claude output: ${stdout.slice(0, 200)}`));\n }\n });\n\n proc.on('error', (err) => {\n reject(new Error(`Failed to spawn claude: ${err.message}`));\n });\n });\n}\n","import { runClaude, type ClaudeResult } from './claude-cli.js';\nimport type { SessionStore, PersistedSession } from './session-store.js';\n\nexport interface SessionInfo {\n sessionId: string;\n projectKey: string;\n lastActivity: number;\n queueLength: number;\n}\n\nexport interface SessionManager {\n send(projectKey: string, cwd: string, prompt: string): Promise<ClaudeResult>;\n getSession(projectKey: string): SessionInfo | undefined;\n listSessions(): SessionInfo[];\n clearSession(projectKey: string): boolean;\n shutdown(): void;\n}\n\ninterface InternalSession {\n sessionId: string | undefined;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n processing: boolean;\n queue: Array<{\n prompt: string;\n resolve: (result: ClaudeResult) => void;\n reject: (error: Error) => void;\n }>;\n idleTimer: ReturnType<typeof setTimeout> | null;\n}\n\nexport function createSessionManager(defaults: {\n idleTimeoutMs: number;\n maxConcurrentSessions: number;\n claudeArgs: string[];\n}, store?: SessionStore): SessionManager {\n const sessions = new Map<string, InternalSession>();\n\n let activeProcesses = 0;\n const waiters: Array<() => void> = [];\n\n function persistSessions(): void {\n if (!store) return;\n // Merge in-memory sessions with existing persisted data.\n // In-memory sessions take precedence; persisted-only entries are preserved.\n const persisted = store.load();\n for (const [key, s] of sessions) {\n if (s.sessionId) {\n persisted.set(key, {\n sessionId: s.sessionId,\n projectKey: s.projectKey,\n cwd: s.cwd,\n lastActivity: s.lastActivity,\n });\n }\n }\n store.save(persisted);\n }\n\n async function acquireSlot(): Promise<void> {\n if (activeProcesses < defaults.maxConcurrentSessions) {\n activeProcesses++;\n return;\n }\n return new Promise<void>((resolve) => {\n waiters.push(() => {\n activeProcesses++;\n resolve();\n });\n });\n }\n\n function releaseSlot(): void {\n activeProcesses--;\n const next = waiters.shift();\n if (next) next();\n }\n\n function resetIdleTimer(session: InternalSession) {\n if (session.idleTimer) clearTimeout(session.idleTimer);\n session.idleTimer = setTimeout(() => {\n // Remove from memory only; session ID stays on disk for later resume\n sessions.delete(session.projectKey);\n }, defaults.idleTimeoutMs);\n }\n\n async function processQueue(session: InternalSession): Promise<void> {\n if (session.processing || session.queue.length === 0) return;\n session.processing = true;\n\n while (session.queue.length > 0) {\n const item = session.queue.shift()!;\n await acquireSlot();\n try {\n const result = await runClaude(\n session.cwd,\n defaults.claudeArgs,\n item.prompt,\n session.sessionId,\n );\n session.sessionId = result.sessionId || session.sessionId;\n session.lastActivity = Date.now();\n resetIdleTimer(session);\n persistSessions();\n item.resolve(result);\n } catch (err) {\n if (session.sessionId) {\n session.sessionId = undefined;\n try {\n const result = await runClaude(session.cwd, defaults.claudeArgs, item.prompt, undefined);\n session.sessionId = result.sessionId || undefined;\n session.lastActivity = Date.now();\n resetIdleTimer(session);\n persistSessions();\n item.resolve(result);\n } catch (retryErr) {\n item.reject(retryErr instanceof Error ? retryErr : new Error(String(retryErr)));\n }\n } else {\n item.reject(err instanceof Error ? err : new Error(String(err)));\n }\n } finally {\n releaseSlot();\n }\n }\n\n session.processing = false;\n }\n\n function getOrCreateSession(projectKey: string, cwd: string): InternalSession {\n let session = sessions.get(projectKey);\n if (!session) {\n // Check store for a previously persisted session ID\n let restoredSessionId: string | undefined;\n if (store) {\n const persisted = store.load();\n const entry = persisted.get(projectKey);\n if (entry?.sessionId) {\n restoredSessionId = entry.sessionId;\n }\n }\n\n session = {\n sessionId: restoredSessionId,\n projectKey,\n cwd,\n lastActivity: Date.now(),\n processing: false,\n queue: [],\n idleTimer: null,\n };\n sessions.set(projectKey, session);\n resetIdleTimer(session);\n }\n return session;\n }\n\n // Restore persisted sessions into memory at startup\n if (store) {\n const persisted = store.load();\n for (const [key, entry] of persisted) {\n sessions.set(key, {\n sessionId: entry.sessionId,\n projectKey: entry.projectKey,\n cwd: entry.cwd,\n lastActivity: entry.lastActivity,\n processing: false,\n queue: [],\n idleTimer: null,\n });\n }\n for (const session of sessions.values()) {\n resetIdleTimer(session);\n }\n if (persisted.size > 0) {\n console.log(`Restored ${persisted.size} session(s) from disk`);\n }\n }\n\n return {\n send(projectKey: string, cwd: string, prompt: string): Promise<ClaudeResult> {\n const session = getOrCreateSession(projectKey, cwd);\n return new Promise<ClaudeResult>((resolve, reject) => {\n session.queue.push({ prompt, resolve, reject });\n processQueue(session);\n });\n },\n\n getSession(projectKey: string): SessionInfo | undefined {\n const session = sessions.get(projectKey);\n if (!session) return undefined;\n return {\n sessionId: session.sessionId ?? '',\n projectKey: session.projectKey,\n lastActivity: session.lastActivity,\n queueLength: session.queue.length,\n };\n },\n\n listSessions(): SessionInfo[] {\n return Array.from(sessions.values()).map((s) => ({\n sessionId: s.sessionId ?? '',\n projectKey: s.projectKey,\n lastActivity: s.lastActivity,\n queueLength: s.queue.length,\n }));\n },\n\n clearSession(projectKey: string): boolean {\n const session = sessions.get(projectKey);\n if (!session) return false;\n if (session.idleTimer) clearTimeout(session.idleTimer);\n sessions.delete(projectKey);\n persistSessions();\n return true;\n },\n\n shutdown() {\n persistSessions();\n for (const session of sessions.values()) {\n if (session.idleTimer) clearTimeout(session.idleTimer);\n }\n sessions.clear();\n },\n };\n}\n","import { readFileSync, writeFileSync, mkdirSync } from 'node:fs';\nimport { dirname } from 'node:path';\n\nexport interface PersistedSession {\n sessionId: string;\n projectKey: string;\n cwd: string;\n lastActivity: number;\n}\n\nexport interface SessionStore {\n load(): Map<string, PersistedSession>;\n save(sessions: Map<string, PersistedSession>): void;\n}\n\nexport function createFileSessionStore(filePath: string): SessionStore {\n return {\n load(): Map<string, PersistedSession> {\n try {\n const raw = readFileSync(filePath, 'utf-8');\n const entries: PersistedSession[] = JSON.parse(raw);\n const map = new Map<string, PersistedSession>();\n for (const entry of entries) {\n if (entry.sessionId && entry.projectKey) {\n map.set(entry.projectKey, entry);\n }\n }\n return map;\n } catch {\n return new Map();\n }\n },\n\n save(sessions: Map<string, PersistedSession>): void {\n const entries = Array.from(sessions.values());\n try {\n mkdirSync(dirname(filePath), { recursive: true });\n writeFileSync(filePath, JSON.stringify(entries, null, 2) + '\\n');\n } catch (err) {\n console.error('Failed to persist sessions:', err);\n }\n },\n };\n}\n","import { Client, GatewayIntentBits, Events, ChannelType, type Message, type TextChannel, type ThreadChannel } from 'discord.js';\nimport type { Router } from './router.js';\nimport type { SessionManager } from './session-manager.js';\nimport type { GatewayConfig } from './config.js';\n\nexport function chunkMessage(text: string, limit: number): string[] {\n if (text.length <= limit) return [text];\n\n const chunks: string[] = [];\n const lines = text.split('\\n');\n let current = '';\n\n for (const line of lines) {\n if (line.length > limit) {\n if (current) {\n chunks.push(current);\n current = '';\n }\n for (let i = 0; i < line.length; i += limit) {\n chunks.push(line.slice(i, i + limit));\n }\n continue;\n }\n\n const candidate = current ? `${current}\\n${line}` : line;\n if (candidate.length > limit) {\n chunks.push(current);\n current = line;\n } else {\n current = candidate;\n }\n }\n\n if (current || chunks.length === 0) {\n chunks.push(current);\n }\n\n return chunks;\n}\n\nexport interface DiscordBot {\n start(token: string): Promise<void>;\n stop(): void;\n}\n\nfunction resolveProjectName(config: GatewayConfig, channelId: string): string {\n return config.projects[channelId]?.name ?? channelId;\n}\n\nfunction findProjectByName(config: GatewayConfig, name: string): { channelId: string; name: string } | null {\n const lower = name.toLowerCase();\n for (const [channelId, project] of Object.entries(config.projects)) {\n if (project.name.toLowerCase() === lower) {\n return { channelId, name: project.name };\n }\n }\n return null;\n}\n\nfunction formatTimeSince(timestamp: number): string {\n const seconds = Math.floor((Date.now() - timestamp) / 1000);\n if (seconds < 60) return `${seconds}s ago`;\n const minutes = Math.floor(seconds / 60);\n if (minutes < 60) return `${minutes}m ago`;\n const hours = Math.floor(minutes / 60);\n return `${hours}h ${minutes % 60}m ago`;\n}\n\nexport function handleCommand(\n command: string,\n config: GatewayConfig,\n sessionManager: SessionManager,\n): string | null {\n const parts = command.trim().split(/\\s+/);\n const cmd = parts[0]?.toLowerCase();\n\n if (cmd === '!sessions') {\n const allSessions = sessionManager.listSessions();\n if (allSessions.length === 0) {\n return 'No active sessions.';\n }\n const lines = allSessions.map((s) => {\n const name = resolveProjectName(config, s.projectKey);\n const idle = formatTimeSince(s.lastActivity);\n const queue = s.queueLength > 0 ? ` | queue: ${s.queueLength}` : '';\n const sid = s.sessionId ? ` | \\`${s.sessionId.slice(0, 8)}…\\`` : '';\n return `- **${name}** — last active ${idle}${queue}${sid}`;\n });\n return `**Active sessions (${allSessions.length})**\\n${lines.join('\\n')}`;\n }\n\n if (cmd === '!session') {\n const name = parts.slice(1).join(' ');\n if (!name) return 'Usage: `!session <project name>`';\n const project = findProjectByName(config, name);\n if (!project) return `No project found matching \"${name}\".`;\n const info = sessionManager.getSession(project.channelId);\n if (!info) return `**${project.name}** — no active session.`;\n const idle = formatTimeSince(info.lastActivity);\n const sid = info.sessionId || 'none';\n return [\n `**${project.name}**`,\n `Session ID: \\`${sid}\\``,\n `Last active: ${idle}`,\n `Queue depth: ${info.queueLength}`,\n ].join('\\n');\n }\n\n if (cmd === '!kill') {\n const name = parts.slice(1).join(' ');\n if (!name) return 'Usage: `!kill <project name>`';\n const project = findProjectByName(config, name);\n if (!project) return `No project found matching \"${name}\".`;\n const cleared = sessionManager.clearSession(project.channelId);\n if (cleared) return `Session for **${project.name}** cleared.`;\n return `**${project.name}** — no active session to clear.`;\n }\n\n if (cmd === '!help') {\n return [\n '**Gateway commands**',\n '`!sessions` — list all active sessions',\n '`!session <name>` — inspect a specific project session',\n '`!kill <name>` — force-close a project session',\n '`!help` — show this message',\n ].join('\\n');\n }\n\n return null;\n}\n\nexport function createDiscordBot(router: Router, sessionManager: SessionManager, config: GatewayConfig): DiscordBot {\n const client = new Client({\n intents: [\n GatewayIntentBits.Guilds,\n GatewayIntentBits.GuildMessages,\n GatewayIntentBits.MessageContent,\n ],\n });\n\n client.on(Events.MessageCreate, async (message: Message) => {\n if (message.author.bot) return;\n\n if (!('send' in message.channel)) return;\n\n // Handle gateway commands from any mapped channel\n if (message.content.startsWith('!')) {\n const parentId = message.channel.isThread() ? message.channel.parentId ?? undefined : undefined;\n const resolved = router.resolve(message.channelId, parentId);\n if (resolved) {\n const response = handleCommand(message.content, config, sessionManager);\n if (response) {\n await message.channel.send(response);\n return;\n }\n }\n }\n\n const parentId = message.channel.isThread() ? message.channel.parentId ?? undefined : undefined;\n const resolved = router.resolve(message.channelId, parentId);\n if (!resolved) return;\n\n try {\n await message.react('👀');\n } catch {\n // Reaction may fail if permissions are missing — non-critical\n }\n\n // If the message is in a main channel, create a thread for the response.\n // If already in a thread, reply there directly.\n let replyChannel: TextChannel | ThreadChannel;\n if (message.channel.isThread()) {\n replyChannel = message.channel;\n } else {\n try {\n replyChannel = await message.startThread({\n name: message.content.slice(0, 100) || 'Claude response',\n autoArchiveDuration: 60,\n });\n } catch {\n // Thread creation may fail (permissions, channel type) — fall back to channel\n replyChannel = message.channel as TextChannel;\n }\n }\n\n try {\n const result = await sessionManager.send(\n resolved.channelId,\n resolved.directory,\n message.content,\n );\n\n const chunks = chunkMessage(result.text, 2000);\n for (const chunk of chunks) {\n await replyChannel.send(chunk);\n }\n } catch (err) {\n const errorMsg = err instanceof Error ? err.message : String(err);\n await replyChannel.send(\n `**Error** (${resolved.name}): ${errorMsg.slice(0, 1800)}`,\n );\n }\n });\n\n return {\n async start(token: string) {\n await client.login(token);\n console.log(`Gateway connected as ${client.user?.tag}`);\n },\n stop() {\n client.destroy();\n },\n };\n}\n","import { createInterface } from 'node:readline';\nimport { writeFileSync, existsSync } from 'node:fs';\nimport { resolve } from 'node:path';\nimport { execSync } from 'node:child_process';\n\nfunction createPrompt(): (question: string) => Promise<string> {\n const rl = createInterface({ input: process.stdin, output: process.stdout });\n return (question: string) =>\n new Promise((resolve) => rl.question(question, (answer) => resolve(answer.trim())));\n}\n\nexport async function runInit() {\n const ask = createPrompt();\n const configDir = process.cwd();\n\n console.log('\\nmpg init — set up multi-project gateway\\n');\n\n // Check for claude CLI\n try {\n execSync('claude --version', { stdio: 'pipe' });\n console.log('Claude CLI found.');\n } catch {\n console.warn('Warning: `claude` not found on PATH. Make sure it is installed before starting the gateway.');\n }\n\n // Discord bot token\n let token = process.env.DISCORD_BOT_TOKEN ?? '';\n const inputToken = await ask(`Discord bot token${token ? ' (press Enter to keep existing)' : ''}: `);\n if (inputToken) token = inputToken;\n if (!token) {\n console.error('A Discord bot token is required. Create one at https://discord.com/developers/applications');\n process.exit(1);\n }\n\n // Write .env\n const envPath = resolve(configDir, '.env');\n writeFileSync(envPath, `DISCORD_BOT_TOKEN=${token}\\n`);\n console.log(`Wrote ${envPath}`);\n\n // Collect projects\n interface ProjectEntry {\n name: string;\n directory: string;\n channelId: string;\n }\n\n const projects: ProjectEntry[] = [];\n\n // Load existing config if present\n const configPath = resolve(configDir, 'config.json');\n if (existsSync(configPath)) {\n try {\n const existing = JSON.parse(\n (await import('node:fs')).readFileSync(configPath, 'utf-8'),\n );\n if (existing.projects) {\n for (const [channelId, project] of Object.entries(existing.projects)) {\n const p = project as { name?: string; directory: string };\n projects.push({ name: p.name ?? channelId, directory: p.directory, channelId });\n }\n if (projects.length > 0) {\n console.log(`\\nExisting projects (${projects.length}):`);\n for (const p of projects) {\n console.log(` ${p.name} → ${p.directory} (${p.channelId})`);\n }\n }\n }\n } catch {\n // ignore parse errors\n }\n }\n\n console.log('\\nAdd projects (empty name to finish):\\n');\n\n while (true) {\n const name = await ask('Project name: ');\n if (!name) break;\n\n const directory = await ask('Project directory (absolute path): ');\n if (!directory) {\n console.log('Directory is required, skipping.');\n continue;\n }\n if (!existsSync(directory)) {\n console.warn(`Warning: ${directory} does not exist.`);\n }\n\n const channelId = await ask('Discord channel ID: ');\n if (!channelId) {\n console.log('Channel ID is required, skipping.');\n continue;\n }\n\n projects.push({ name, directory, channelId });\n console.log(`Added ${name}\\n`);\n }\n\n if (projects.length === 0) {\n console.log('No projects configured. You can edit config.json later.');\n }\n\n // Build config\n const config = {\n defaults: {\n idleTimeoutMs: 1800000,\n maxConcurrentSessions: 4,\n claudeArgs: ['--permission-mode', 'acceptEdits', '--output-format', 'json'],\n },\n projects: Object.fromEntries(\n projects.map((p) => [p.channelId, { name: p.name, directory: p.directory }]),\n ),\n };\n\n writeFileSync(configPath, JSON.stringify(config, null, 2) + '\\n');\n console.log(`Wrote ${configPath}`);\n\n console.log('\\nSetup complete! Run `mpg start` to launch the gateway.');\n}\n"],"mappings":"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","names":["resolve","existsSync","readFileSync","args","resolve","resolve","command","parentId","resolved","writeFileSync","resolve","readFileSync","resolve","existsSync"]}
|