Files
Codewalkers/apps/server/trpc/routers/system.ts
Lukas May 34578d39c6 refactor: Restructure monorepo to apps/server/ and apps/web/ layout
Move src/ → apps/server/ and packages/web/ → apps/web/ to adopt
standard monorepo conventions (apps/ for runnable apps, packages/
for reusable libraries). Update all config files, shared package
imports, test fixtures, and documentation to reflect new paths.

Key fixes:
- Update workspace config to ["apps/*", "packages/*"]
- Update tsconfig.json rootDir/include for apps/server/
- Add apps/web/** to vitest exclude list
- Update drizzle.config.ts schema path
- Fix ensure-schema.ts migration path detection (3 levels up in dev,
  2 levels up in dist)
- Fix tests/integration/cli-server.test.ts import paths
- Update packages/shared imports to apps/server/ paths
- Update all docs/ files with new paths
2026-03-03 11:22:53 +01:00

111 lines
3.4 KiB
TypeScript

/**
* System Router — health, status, systemHealthCheck
*/
import { z } from 'zod';
import { join } from 'node:path';
import { access } from 'node:fs/promises';
import type { ProcedureBuilder } from '../trpc.js';
import { requireAccountRepository, requireProjectRepository } from './_helpers.js';
import { checkAccountHealth } from '../../agent/accounts/usage.js';
import { getProjectCloneDir } from '../../git/project-clones.js';
export const healthResponseSchema = z.object({
status: z.literal('ok'),
uptime: z.number().int().nonnegative(),
processCount: z.number().int().nonnegative(),
});
export type HealthResponse = z.infer<typeof healthResponseSchema>;
export const processInfoSchema = z.object({
id: z.string(),
pid: z.number().int().positive(),
command: z.string(),
status: z.string(),
startedAt: z.string(),
});
export type ProcessInfo = z.infer<typeof processInfoSchema>;
export const statusResponseSchema = z.object({
server: z.object({
startedAt: z.string(),
uptime: z.number().int().nonnegative(),
pid: z.number().int().positive(),
}),
processes: z.array(processInfoSchema),
});
export type StatusResponse = z.infer<typeof statusResponseSchema>;
export function systemProcedures(publicProcedure: ProcedureBuilder) {
return {
health: publicProcedure
.output(healthResponseSchema)
.query(({ ctx }): HealthResponse => {
const uptime = ctx.serverStartedAt
? Math.floor((Date.now() - ctx.serverStartedAt.getTime()) / 1000)
: 0;
return {
status: 'ok',
uptime,
processCount: ctx.processCount,
};
}),
status: publicProcedure
.output(statusResponseSchema)
.query(({ ctx }): StatusResponse => {
const uptime = ctx.serverStartedAt
? Math.floor((Date.now() - ctx.serverStartedAt.getTime()) / 1000)
: 0;
return {
server: {
startedAt: ctx.serverStartedAt?.toISOString() ?? '',
uptime,
pid: process.pid,
},
processes: [],
};
}),
systemHealthCheck: publicProcedure
.query(async ({ ctx }) => {
const uptime = ctx.serverStartedAt
? Math.floor((Date.now() - ctx.serverStartedAt.getTime()) / 1000)
: 0;
const accountRepo = requireAccountRepository(ctx);
const allAccounts = await accountRepo.findAll();
const allAgents = ctx.agentManager ? await ctx.agentManager.list() : [];
const accounts = await Promise.all(
allAccounts.map(account => checkAccountHealth(account, allAgents, ctx.credentialManager, ctx.workspaceRoot ?? undefined))
);
const projectRepo = requireProjectRepository(ctx);
const allProjects = await projectRepo.findAll();
const projects = await Promise.all(
allProjects.map(async project => {
let repoExists = false;
if (ctx.workspaceRoot) {
const clonePath = join(ctx.workspaceRoot, getProjectCloneDir(project.name, project.id));
try { await access(clonePath); repoExists = true; } catch { repoExists = false; }
}
return { id: project.id, name: project.name, url: project.url, repoExists };
})
);
return {
server: { status: 'ok' as const, uptime, startedAt: ctx.serverStartedAt?.toISOString() ?? null },
accounts,
projects,
};
}),
};
}