Files
stack/apps/gateway/src/coord/coord.service.ts
Jason Woltje 4da255bf04
Some checks failed
ci/woodpecker/push/ci Pipeline failed
feat(cli): command architecture — agents, missions, gateway-aware prdy (#158)
Co-authored-by: Jason Woltje <jason@diversecanvas.com>
Co-committed-by: Jason Woltje <jason@diversecanvas.com>
2026-03-15 23:10:23 +00:00

78 lines
2.1 KiB
TypeScript

import { Injectable, Logger } from '@nestjs/common';
import {
loadMission,
getMissionStatus,
getTaskStatus,
parseTasksFile,
type Mission,
type MissionStatusSummary,
type MissionTask,
type TaskDetail,
} from '@mosaic/coord';
import { promises as fs } from 'node:fs';
import path from 'node:path';
/**
* File-based coord operations for agent tool consumption.
* DB-backed mission CRUD is handled directly by MissionsController via Brain repos.
*/
@Injectable()
export class CoordService {
private readonly logger = new Logger(CoordService.name);
async loadMission(projectPath: string): Promise<Mission | null> {
try {
return await loadMission(projectPath);
} catch (err) {
this.logger.debug(
`No coord mission at ${projectPath}: ${err instanceof Error ? err.message : String(err)}`,
);
return null;
}
}
async getMissionStatus(projectPath: string): Promise<MissionStatusSummary | null> {
const mission = await this.loadMission(projectPath);
if (!mission) return null;
try {
return await getMissionStatus(mission);
} catch (err) {
this.logger.error(
`Failed to get mission status: ${err instanceof Error ? err.message : String(err)}`,
);
return null;
}
}
async getTaskStatus(projectPath: string, taskId: string): Promise<TaskDetail | null> {
const mission = await this.loadMission(projectPath);
if (!mission) return null;
try {
return await getTaskStatus(mission, taskId);
} catch (err) {
this.logger.error(
`Failed to get task status for ${taskId}: ${err instanceof Error ? err.message : String(err)}`,
);
return null;
}
}
async listTasks(projectPath: string): Promise<MissionTask[]> {
const mission = await this.loadMission(projectPath);
if (!mission) return [];
const tasksFile = path.isAbsolute(mission.tasksFile)
? mission.tasksFile
: path.join(mission.projectPath, mission.tasksFile);
try {
const content = await fs.readFile(tasksFile, 'utf8');
return parseTasksFile(content);
} catch {
return [];
}
}
}