Skip to content

Commit 9fe1ae8

Browse files
refactor: remove all config.json legacy code
- Daemon now takes dbPath directly instead of configPath - Removed watchConfig(), reloadConfig(), and the config.json auto-import - Removed loadConfig() from config.ts (no longer needed) - Removed importFromJson() from db.ts (no longer needed) - Removed --config CLI flag (config is managed via the dashboard) - All runtime data lives in ~/.relay/ Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 2ea643d commit 9fe1ae8

4 files changed

Lines changed: 9 additions & 170 deletions

File tree

src/cli.ts

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,6 @@ function dataDir(): string {
2626
return join(process.env.HOME ?? "~", ".relay");
2727
}
2828

29-
function resolveConfigPath(): string {
30-
return getFlag("config") ?? join(dataDir(), "config.json");
31-
}
32-
3329
function getCommand(): string {
3430
const args = process.argv.slice(2).filter((a) => !a.startsWith("--"));
3531
return args[0] ?? "help";
@@ -57,10 +53,10 @@ async function checkForUpdates() {
5753
}
5854

5955
async function cmdStart() {
60-
const configPath = resolveConfigPath();
6156
const dir = dataDir();
6257
await Bun.$`mkdir -p ${dir}`.quiet();
6358
const pidFile = join(dir, "relay.pid");
59+
const dbPath = join(dir, "sqlite.db");
6460
const logLevel = getFlag("log-level");
6561
if (logLevel) setLogLevel(logLevel as "debug" | "info" | "warn" | "error");
6662

@@ -81,7 +77,7 @@ async function cmdStart() {
8177
// Silent update check in background
8278
checkForUpdates().catch(() => {});
8379

84-
const daemon = new Daemon(configPath);
80+
const daemon = new Daemon(dbPath);
8581

8682
const cleanup = () => {
8783
try { unlinkSync(pidFile); } catch {}
@@ -236,7 +232,6 @@ ${BOLD}Usage:${RESET}
236232
relay help Show this help
237233
238234
${BOLD}Options:${RESET}
239-
--config <path> Path to config.json (default: ~/.relay/config.json)
240235
--log-level <level> Override log level (debug/info/warn/error)
241236
242237
${BOLD}Getting started:${RESET}

src/config.ts

Lines changed: 0 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -78,34 +78,6 @@ const DEFAULTS = {
7878
allowedTools: ["Read", "Write", "Edit", "Bash", "Glob", "Grep"],
7979
};
8080

81-
export async function loadConfig(configPath: string): Promise<Config> {
82-
const file = Bun.file(configPath);
83-
if (!(await file.exists())) {
84-
throw new Error(`Config not found at ${configPath}. Run 'relay init' to create one.`);
85-
}
86-
87-
let raw: Record<string, unknown>;
88-
try {
89-
raw = await file.json();
90-
} catch {
91-
throw new Error(`Config at ${configPath} is not valid JSON. Check for syntax errors.`);
92-
}
93-
94-
const config: Config = {
95-
workspaces: (raw.workspaces as WorkspaceConfig[]) ?? [],
96-
pollIntervalSeconds: (raw.pollIntervalSeconds as number) ?? DEFAULTS.pollIntervalSeconds,
97-
maxConcurrency: (raw.maxConcurrency as number) ?? DEFAULTS.maxConcurrency,
98-
claudeTimeout: (raw.claudeTimeout as number) ?? DEFAULTS.claudeTimeout,
99-
triageTimeout: (raw.triageTimeout as number) ?? DEFAULTS.triageTimeout,
100-
triage: (raw.triage as boolean) ?? DEFAULTS.triage,
101-
logLevel: (raw.logLevel as Config["logLevel"]) ?? DEFAULTS.logLevel,
102-
allowedTools: (raw.allowedTools as string[]) ?? DEFAULTS.allowedTools,
103-
};
104-
105-
validate(config);
106-
return config;
107-
}
108-
10981
export function findProjectConfig(config: Config, projectKey: string): { workspace: WorkspaceConfig; project: ProjectConfig } | null {
11082
for (const ws of config.workspaces) {
11183
const project = ws.projects.find((p) => p.key === projectKey);

src/daemon.ts

Lines changed: 7 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { loadConfig, allProjects, findProjectConfig, type Config, type ProjectConfig, type WorkspaceConfig } from "./config.ts";
1+
import { allProjects, findProjectConfig, type Config, type ProjectConfig, type WorkspaceConfig } from "./config.ts";
22
import { IssueDB, ConfigDB, type IssueRow, type IssueStatus } from "./db.ts";
33
import { PriorityQueue } from "./queue.ts";
44
import { SentryAdapter } from "./sources/sentry.ts";
@@ -44,33 +44,19 @@ export class Daemon {
4444
private queue!: PriorityQueue<PipelineItem>;
4545
private workspaces: Map<string, WorkspaceRuntime> = new Map();
4646
private pollTimer: ReturnType<typeof setInterval> | null = null;
47-
private configWatcher: ReturnType<typeof import("node:fs").watch> | null = null;
4847
private stopping = false;
49-
private configPath: string;
48+
private dbPath: string;
5049
private dashboard!: DashboardServer;
51-
private baseDir: string;
5250

53-
constructor(configPath: string) {
54-
this.configPath = configPath;
55-
this.baseDir = dirname(configPath);
51+
constructor(dbPath: string) {
52+
this.dbPath = dbPath;
5653
}
5754

5855
async start() {
5956
// Init DB
60-
const dbPath = join(this.baseDir, "sqlite.db");
61-
this.db = new IssueDB(dbPath);
57+
this.db = new IssueDB(this.dbPath);
6258
this.configDB = new ConfigDB(this.db.getDatabase());
6359

64-
// Auto-import config.json if DB has no config
65-
if (!this.configDB.hasConfig()) {
66-
const configFile = Bun.file(this.configPath);
67-
if (await configFile.exists()) {
68-
const jsonConfig = await loadConfig(this.configPath);
69-
this.configDB.importFromJson(jsonConfig);
70-
logger.info("Auto-imported config.json into database");
71-
}
72-
}
73-
7460
// Start dashboard (always — even without config for setup wizard)
7561
const isDev = process.env.DEV === "1";
7662
// Resolve dist/ relative to this source file so it works both in-repo and when
@@ -129,9 +115,6 @@ export class Daemon {
129115
// Start poll interval
130116
this.pollTimer = setInterval(() => this.poll(), this.config.pollIntervalSeconds * 1000);
131117

132-
// Watch config.json for hot reload (legacy support)
133-
this.watchConfig();
134-
135118
// Setup shutdown handlers
136119
this.setupShutdownHandlers();
137120

@@ -223,11 +206,6 @@ export class Daemon {
223206
this.stopping = true;
224207
logger.info("Shutting down...");
225208

226-
if (this.configWatcher) {
227-
this.configWatcher.close();
228-
this.configWatcher = null;
229-
}
230-
231209
if (this.pollTimer) {
232210
clearInterval(this.pollTimer);
233211
this.pollTimer = null;
@@ -330,58 +308,6 @@ export class Daemon {
330308
}
331309
}
332310

333-
private watchConfig() {
334-
const { watch } = require("node:fs") as typeof import("node:fs");
335-
let debounce: ReturnType<typeof setTimeout> | null = null;
336-
337-
this.configWatcher = watch(this.configPath, () => {
338-
if (debounce) clearTimeout(debounce);
339-
debounce = setTimeout(() => this.reloadConfig(), 500);
340-
});
341-
342-
logger.debug("Watching config for changes", { path: this.configPath });
343-
}
344-
345-
private async reloadConfig() {
346-
try {
347-
const newConfig = await loadConfig(this.configPath);
348-
349-
// Restart poll timer if interval changed
350-
if (newConfig.pollIntervalSeconds !== this.config.pollIntervalSeconds) {
351-
if (this.pollTimer) clearInterval(this.pollTimer);
352-
this.pollTimer = setInterval(() => this.poll(), newConfig.pollIntervalSeconds * 1000);
353-
logger.info("Poll interval updated", { seconds: newConfig.pollIntervalSeconds });
354-
}
355-
356-
if (newConfig.logLevel !== this.config.logLevel) {
357-
setLogLevel(newConfig.logLevel);
358-
logger.info("Log level updated", { level: newConfig.logLevel });
359-
}
360-
361-
// Reinit workspaces if workspace config changed
362-
const oldWsJson = JSON.stringify(this.config.workspaces);
363-
const newWsJson = JSON.stringify(newConfig.workspaces);
364-
if (oldWsJson !== newWsJson) {
365-
for (const ws of this.workspaces.values()) {
366-
ws.telegram?.stopPolling();
367-
}
368-
this.workspaces.clear();
369-
this.config = newConfig;
370-
this.initWorkspaces();
371-
logger.info("Workspaces reinitialized");
372-
} else {
373-
this.config = newConfig;
374-
}
375-
376-
logger.info("Config reloaded");
377-
378-
// Trigger immediate poll after reload
379-
await this.poll();
380-
} catch (err) {
381-
logger.error("Config reload failed, keeping current config", { error: String(err) });
382-
}
383-
}
384-
385311
private async poll() {
386312
if (this.stopping) return;
387313

@@ -1090,7 +1016,7 @@ function buildPRBody(row: IssueRow): string {
10901016

10911017
// When run directly
10921018
if (import.meta.main) {
1093-
const configPath = process.argv[2] || join(process.cwd(), "config.json");
1094-
const daemon = new Daemon(configPath);
1019+
const dbPath = process.argv[2] || join(process.env.HOME ?? "~", ".relay", "sqlite.db");
1020+
const daemon = new Daemon(dbPath);
10951021
await daemon.start();
10961022
}

src/db.ts

Lines changed: 0 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -615,60 +615,6 @@ export class ConfigDB {
615615
return row.count > 0;
616616
}
617617

618-
importFromJson(config: Config): void {
619-
this.db.exec("BEGIN TRANSACTION");
620-
try {
621-
// Import settings
622-
const settingsMap: Record<string, string> = {
623-
pollIntervalSeconds: String(config.pollIntervalSeconds),
624-
maxConcurrency: String(config.maxConcurrency),
625-
claudeTimeout: String(config.claudeTimeout),
626-
triageTimeout: String(config.triageTimeout),
627-
triage: String(config.triage),
628-
logLevel: config.logLevel,
629-
allowedTools: JSON.stringify(config.allowedTools),
630-
};
631-
this.updateSettings(settingsMap);
632-
633-
// Import workspaces
634-
for (const ws of config.workspaces) {
635-
const wsRow = this.createWorkspace(ws.key);
636-
637-
if (ws.telegram) {
638-
this.upsertTelegramConfig(wsRow.id, ws.telegram.botToken, ws.telegram.chatId);
639-
}
640-
641-
for (const proj of ws.projects) {
642-
const projRow = this.createProject(wsRow.id, {
643-
key: proj.key,
644-
repoPath: proj.repoPath,
645-
baseBranch: proj.baseBranch,
646-
testCommand: proj.testCommand,
647-
});
648-
649-
if (proj.sources.sentry) {
650-
this.upsertSourceConfig(projRow.id, "sentry", proj.sources.sentry as unknown as Record<string, unknown>);
651-
}
652-
if (proj.sources.asana) {
653-
this.upsertSourceConfig(projRow.id, "asana", proj.sources.asana as unknown as Record<string, unknown>);
654-
}
655-
if (proj.sources.linear) {
656-
this.upsertSourceConfig(projRow.id, "linear", proj.sources.linear as unknown as Record<string, unknown>);
657-
}
658-
if (proj.sources.jira) {
659-
this.upsertSourceConfig(projRow.id, "jira", proj.sources.jira as unknown as Record<string, unknown>);
660-
}
661-
}
662-
}
663-
664-
this.db.exec("COMMIT");
665-
logger.info("Imported config from JSON to DB", { workspaces: config.workspaces.length });
666-
} catch (err) {
667-
this.db.exec("ROLLBACK");
668-
throw err;
669-
}
670-
}
671-
672618
toConfig(): Config {
673619
const settings = this.getSettings();
674620
const workspaces: WorkspaceConfig[] = [];

0 commit comments

Comments
 (0)