| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- import { readdir, readFile } from 'node:fs/promises';
- import { resolve } from 'node:path';
- import { resolvePilotHome, createProjectId } from '../utils/pilotPaths.js';
- import { getPilotDeckGateway } from '../pilotdeck-bridge.js';
- /**
- * Read phase events from a single project's events.jsonl.
- *
- * @param {string} projectDir Absolute path to the project's always-on dir
- * (e.g. `~/.pilotdeck/always-on/projects/<id>`)
- * @returns {Array<object>}
- */
- async function readProjectEvents(projectDir) {
- const eventsFile = resolve(projectDir, 'events.jsonl');
- let raw;
- try {
- raw = await readFile(eventsFile, 'utf-8');
- } catch {
- return [];
- }
- const events = [];
- for (const line of raw.trim().split('\n')) {
- if (!line) continue;
- try {
- events.push(JSON.parse(line));
- } catch {
- // skip malformed
- }
- }
- return events;
- }
- /**
- * Build a lookup from projectKey -> { projectName, projectDisplayName }.
- */
- async function buildProjectLookup() {
- const gateway = await getPilotDeckGateway();
- const { projects } = await gateway.listProjects();
- const lookup = new Map();
- for (const project of projects) {
- const key = resolve(project.projectKey ?? project.fullPath ?? '');
- if (!key) continue;
- const name = createProjectId(key);
- const displayName = project.displayName || key.split(/[\\/]/).pop() || name;
- lookup.set(key, { projectName: name, projectDisplayName: displayName });
- }
- return lookup;
- }
- /**
- * Aggregate Always-On phase events across all projects.
- *
- * @param {{ limit?: number; since?: string }} [opts]
- * @returns {Promise<{ events: Array<object> }>}
- */
- export async function getAlwaysOnDashboardEvents(opts = {}) {
- const { limit = 200, since } = opts;
- const pilotHome = resolvePilotHome();
- const projectsDir = resolve(pilotHome, 'always-on', 'projects');
- let projectDirs;
- try {
- projectDirs = await readdir(projectsDir, { withFileTypes: true });
- } catch {
- return { events: [] };
- }
- const lookup = await buildProjectLookup().catch(() => new Map());
- const allEvents = [];
- for (const entry of projectDirs) {
- if (!entry.isDirectory()) continue;
- const dir = resolve(projectsDir, entry.name);
- const events = await readProjectEvents(dir);
- allEvents.push(...events);
- }
- let filtered = allEvents;
- if (since) {
- const sinceMs = Date.parse(since);
- if (Number.isFinite(sinceMs)) {
- filtered = filtered.filter((e) => Date.parse(e.timestamp) >= sinceMs);
- }
- }
- filtered.sort((a, b) => (b.timestamp || '').localeCompare(a.timestamp || ''));
- if (limit > 0) {
- filtered = filtered.slice(0, limit);
- }
- const events = filtered.map((event) => {
- const key = resolve(event.projectKey || '');
- const info = lookup.get(key) || {
- projectName: createProjectId(key || 'unknown'),
- projectDisplayName: key.split(/[\\/]/).pop() || 'Unknown',
- };
- return {
- ...event,
- projectName: info.projectName,
- projectDisplayName: info.projectDisplayName,
- };
- });
- return { events };
- }
|