Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ SLACK_HISTORY_API_MAX_LIMIT=50
SLACK_ACTIVE_TURN_RECONCILE_INTERVAL_MS=15000
SLACK_PROGRESS_REMINDER_AFTER_MS=120000
SLACK_PROGRESS_REMINDER_REPEAT_MS=120000
SESSION_INACTIVE_TTL_MS=86400000
SESSION_CLEANUP_INTERVAL_MS=3600000
SESSION_CLEANUP_MAX_PER_SWEEP=20

# Service storage
PORT=3000
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,9 @@ Copy `.env.example` to `.env` and fill in:
- optional `LOG_RAW_SLACK_EVENTS`
- optional `LOG_RAW_CODEX_RPC`
- optional `LOG_RAW_HTTP_REQUESTS`
- optional `SESSION_INACTIVE_TTL_MS`
- optional `SESSION_CLEANUP_INTERVAL_MS`
- optional `SESSION_CLEANUP_MAX_PER_SWEEP`
- one Codex auth mode
- optional host Codex home mount if you want the container to inherit your global `~/.codex` memory/instructions

Expand Down
6 changes: 6 additions & 0 deletions src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ export interface AppConfig {
readonly slackMissedThreadRecoveryIntervalMs: number;
readonly slackProgressReminderAfterMs: number;
readonly slackProgressReminderRepeatMs: number;
readonly sessionInactiveTtlMs: number;
readonly sessionCleanupIntervalMs: number;
readonly sessionCleanupMaxPerSweep: number;
readonly stateDir: string;
readonly jobsRoot: string;
readonly sessionsRoot: string;
Expand Down Expand Up @@ -171,6 +174,9 @@ export function loadConfig(env = process.env): AppConfig {
),
slackProgressReminderAfterMs: getNumber(env, "SLACK_PROGRESS_REMINDER_AFTER_MS", 120_000),
slackProgressReminderRepeatMs: getNumber(env, "SLACK_PROGRESS_REMINDER_REPEAT_MS", 120_000),
sessionInactiveTtlMs: getNumber(env, "SESSION_INACTIVE_TTL_MS", 24 * 60 * 60 * 1_000),
sessionCleanupIntervalMs: getNumber(env, "SESSION_CLEANUP_INTERVAL_MS", 60 * 60 * 1_000),
sessionCleanupMaxPerSweep: getNumber(env, "SESSION_CLEANUP_MAX_PER_SWEEP", 20),
stateDir,
jobsRoot,
sessionsRoot,
Expand Down
13 changes: 13 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { CodexRuntimeControl } from "./services/codex-runtime-control.js";
import { IsolatedMcpService } from "./services/codex/isolated-mcp-service.js";
import { GitHubAuthorMappingService } from "./services/github-author-mapping-service.js";
import { JobManager } from "./services/job-manager.js";
import { SessionJanitor } from "./services/session-janitor.js";
import { SessionManager } from "./services/session-manager.js";
import { SlackCodexBridge } from "./services/slack/slack-codex-bridge.js";
import { StateStore } from "./store/state-store.js";
Expand Down Expand Up @@ -71,6 +72,15 @@ export async function startService(): Promise<{
await bridge.acceptBackgroundJobEvent(event);
}
});
const sessionJanitor = new SessionJanitor({
sessions: sessionManager,
sessionsRoot: config.sessionsRoot,
jobsRoot: config.jobsRoot,
logDir: config.logDir,
inactivityTtlMs: config.sessionInactiveTtlMs,
cleanupIntervalMs: config.sessionCleanupIntervalMs,
cleanupMaxPerSweep: config.sessionCleanupMaxPerSweep
});
const authProfiles = new AuthProfileService({
config
});
Expand All @@ -95,11 +105,13 @@ export async function startService(): Promise<{
try {
await bridge.start();
await jobManager.start();
await sessionJanitor.start();
await new Promise<void>((resolve, reject) => {
server.listen(config.port, () => resolve());
server.once("error", reject);
});
} catch (error) {
await sessionJanitor.stop().catch(() => {});
await jobManager.stop().catch(() => {});
await bridge.stop().catch(() => {});
if (server.listening) {
Expand All @@ -118,6 +130,7 @@ export async function startService(): Promise<{

return {
stop: async () => {
await sessionJanitor.stop();
await bridge.stop();
await jobManager.stop();
await new Promise<void>((resolve, reject) => {
Expand Down
Loading
Loading