Repository Public
1/**2 * Auth / notification / system wire shapes. The auth DTOs mirror the previous console's shapes3 * exactly (the server keeps them identical); they live here because `@aos/shared` only carries4 * the Agent OS vocabulary. System status is parsed leniently: every field the server does not5 * send renders as "—".6 */7import '@/lib/zod-config';8import { z } from 'zod';9import { Branding } from '@aos/shared';1011const iso = z.string();12const Id = z.string().uuid();1314export const AuthStatus = z.object({15 setupRequired: z.boolean(), // no administrator exists yet -> setup flow with one-time token16 authenticated: z.boolean(),17 user: z.object({ id: Id, username: z.string() }).nullable(),18 methods: z.object({ password: z.boolean(), passkeys: z.number(), totp: z.boolean() }),19 csrfToken: z.string().nullable(), // required as X-CSRF-Token on every mutating request20 sessionExpiresAt: iso.nullable(),21 branding: Branding.optional(),22});23export type AuthStatus = z.infer<typeof AuthStatus>;2425/** Password OK but second factor still needed. */26export const LoginStep = z.object({27 ok: z.boolean(),28 next: z.enum(['done', 'totp', 'passkey', 'enroll_second_factor']),29 retryAfterSec: z.number().optional(),30});31export type LoginStep = z.infer<typeof LoginStep>;32export const TotpEnrollStart = z.object({ secret: z.string(), otpauthUrl: z.string() });33export type TotpEnrollStart = z.infer<typeof TotpEnrollStart>;3435export const Device = z.object({36 id: Id,37 kind: z.enum(['passkey', 'session']),38 label: z.string(),39 createdAt: iso,40 lastUsedAt: iso.nullable(),41 current: z.boolean(),42 userAgent: z.string().nullable(),43 ip: z.string().nullable(),44});45export type Device = z.infer<typeof Device>;4647export const ApiError = z.object({48 error: z.object({ code: z.string(), message: z.string(), details: z.unknown().optional() }),49});50export type ApiError = z.infer<typeof ApiError>;5152export const Notification = z.object({53 id: z.string(),54 kind: z.string(),55 title: z.string(),56 body: z.string(),57 link: z.string().nullable(),58 readAt: iso.nullable(),59 createdAt: iso,60});61export type Notification = z.infer<typeof Notification>;6263export const AuditEvent = z.object({64 id: z.string(),65 at: iso,66 actor: z.string(),67 action: z.string(),68 target: z.string().nullable(),69 ip: z.string().nullable(),70 ok: z.boolean(),71 detail: z.record(z.string(), z.unknown()),72});73export type AuditEvent = z.infer<typeof AuditEvent>;7475/** GET /system/status — the server's shape (`apps/server/src/dto.ts`). Unknown ⇒ null. */76export const SystemStatus = z.looseObject({77 collectedAt: iso,78 version: z.string(),79 db: z.looseObject({ ok: z.boolean(), latencyMs: z.number().nullable() }),80 worker: z.looseObject({81 /** null = cannot tell (no heartbeat row and no leased run). */82 alive: z.boolean().nullable(),83 lastSeenAt: iso.nullable(),84 source: z.enum(['workers', 'leases', 'none']),85 workers: z.array(z.looseObject({ id: z.string(), lastSeenAt: iso, version: z.string().nullable() })),86 }),87 providers: z.looseObject({ anthropic: z.boolean(), openai: z.boolean() }),88 counts: z.record(z.string(), z.number()),89});90export type SystemStatus = z.infer<typeof SystemStatus>;9192/** GET /runs/:id/artifacts — assumed item shape. */93export interface ArtifactDto {94 sha256: string;95 name: string;96 size: number | null;97 mime?: string | null;98 stepSeq?: number | null;99 createdAt?: string | null;100}101