diff --git a/cli/src/api/types.ts b/cli/src/api/types.ts index da35643f..adfad4ac 100644 --- a/cli/src/api/types.ts +++ b/cli/src/api/types.ts @@ -1,18 +1,22 @@ import { AgentStateSchema, AttachmentMetadataSchema, - CodexCollaborationModeSchema, MachineMetadataSchema, - MachineSchema, MetadataSchema, - PermissionModeSchema, - RunnerStateSchema, - TodosSchema + RunnerStateSchema } from '@hapi/protocol/schemas' import { + CliMessagesResponseSchema, + CreateMachineResponseSchema, + CreateSessionResponseSchema, + GetSessionResponseSchema, LocalHandoffResponseSchema, LocalResumeTargetResponseSchema, - ResumableSessionsResponseSchema + ResumableSessionsResponseSchema, + type CliMessagesResponse, + type CreateMachineResponse, + type CreateSessionResponse, + type GetSessionResponse } from '@hapi/protocol' import type { CodexCollaborationMode, Machine, MachineMetadata, PermissionMode, RunnerState } from '@hapi/protocol/types' import { z } from 'zod' @@ -40,59 +44,23 @@ export type SessionEffort = string | null export { AgentStateSchema, AttachmentMetadataSchema, MachineMetadataSchema, MetadataSchema, RunnerStateSchema } -export const CliMessagesResponseSchema = z.object({ - messages: z.array(z.object({ - id: z.string(), - seq: z.number(), - createdAt: z.number(), - localId: z.string().nullable().optional(), - content: z.unknown() - })) -}) - -export type CliMessagesResponse = z.infer - -export const CreateSessionResponseSchema = z.object({ - session: z.object({ - id: z.string(), - namespace: z.string(), - seq: z.number(), - createdAt: z.number(), - updatedAt: z.number(), - active: z.boolean(), - activeAt: z.number(), - metadata: z.unknown().nullable(), - metadataVersion: z.number(), - agentState: z.unknown().nullable(), - agentStateVersion: z.number(), - thinking: z.boolean(), - thinkingAt: z.number(), - todos: TodosSchema.optional(), - model: z.string().nullable().optional().default(null), - modelReasoningEffort: z.string().nullable().optional().default(null), - effort: z.string().nullable().optional().default(null), - permissionMode: PermissionModeSchema.optional(), - collaborationMode: CodexCollaborationModeSchema.optional() - }) -}) - -export type CreateSessionResponse = z.infer - -export const CreateMachineResponseSchema = z.object({ - machine: MachineSchema -}) - -export type CreateMachineResponse = z.infer - -export const GetSessionResponseSchema = CreateSessionResponseSchema -export type GetSessionResponse = z.infer - export { + CliMessagesResponseSchema, + CreateMachineResponseSchema, + CreateSessionResponseSchema, + GetSessionResponseSchema, LocalHandoffResponseSchema, LocalResumeTargetResponseSchema, ResumableSessionsResponseSchema } +export type { + CliMessagesResponse, + CreateMachineResponse, + CreateSessionResponse, + GetSessionResponse +} + export const MessageMetaSchema = z.object({ sentFrom: z.string().optional(), fallbackModel: z.string().nullable().optional(), diff --git a/hub/src/web/routes/cli.ts b/hub/src/web/routes/cli.ts index 96752a91..90a4824d 100644 --- a/hub/src/web/routes/cli.ts +++ b/hub/src/web/routes/cli.ts @@ -1,6 +1,10 @@ import { Hono } from 'hono' import { z } from 'zod' -import { PROTOCOL_VERSION } from '@hapi/protocol' +import { + CreateOrLoadMachineRequestSchema, + CreateOrLoadSessionRequestSchema, + PROTOCOL_VERSION +} from '@hapi/protocol' import { getConfiguration } from '../../configuration' import { constantTimeEquals } from '../../utils/crypto' import { parseAccessToken } from '../../utils/accessToken' @@ -8,21 +12,6 @@ import type { Machine, Session, SyncEngine } from '../../sync/syncEngine' const bearerSchema = z.string().regex(/^Bearer\s+(.+)$/i) -const createOrLoadSessionSchema = z.object({ - tag: z.string().min(1), - metadata: z.unknown(), - agentState: z.unknown().nullable().optional(), - model: z.string().optional(), - modelReasoningEffort: z.string().optional(), - effort: z.string().optional() -}) - -const createOrLoadMachineSchema = z.object({ - id: z.string().min(1), - metadata: z.unknown(), - runnerState: z.unknown().nullable().optional() -}) - const getMessagesQuerySchema = z.object({ afterSeq: z.coerce.number().int().min(0), limit: z.coerce.number().int().min(1).max(200).optional() @@ -98,7 +87,7 @@ export function createCliRoutes(getSyncEngine: () => SyncEngine | null): Hono null) - const parsed = createOrLoadSessionSchema.safeParse(json) + const parsed = CreateOrLoadSessionRequestSchema.safeParse(json) if (!parsed.success) { return c.json({ error: 'Invalid body' }, 400) } @@ -215,7 +204,7 @@ export function createCliRoutes(getSyncEngine: () => SyncEngine | null): Hono null) - const parsed = createOrLoadMachineSchema.safeParse(json) + const parsed = CreateOrLoadMachineRequestSchema.safeParse(json) if (!parsed.success) { return c.json({ error: 'Invalid body' }, 400) } diff --git a/shared/src/apiTypes.ts b/shared/src/apiTypes.ts index 65587900..d02aa571 100644 --- a/shared/src/apiTypes.ts +++ b/shared/src/apiTypes.ts @@ -1,3 +1,90 @@ +import { z } from 'zod' +import { + DecryptedMessageSchema, + MachineSchema, + SessionSchema +} from './schemas' +import type { + DecryptedMessage, + Machine, + Session +} from './schemas' +import type { SessionSummary } from './sessionSummary' + +export const CreateOrLoadSessionRequestSchema = z.object({ + tag: z.string().min(1), + metadata: z.unknown(), + agentState: z.unknown().nullable().optional(), + model: z.string().optional(), + modelReasoningEffort: z.string().optional(), + effort: z.string().optional() +}) + +export type CreateOrLoadSessionRequest = z.infer + +export const CreateOrLoadMachineRequestSchema = z.object({ + id: z.string().min(1), + metadata: z.unknown(), + runnerState: z.unknown().nullable().optional() +}) + +export type CreateOrLoadMachineRequest = z.infer + +export const CliMessagesResponseSchema = z.object({ + messages: z.array(z.object({ + id: z.string(), + seq: z.number(), + createdAt: z.number(), + localId: z.string().nullable().optional(), + content: z.unknown() + })) +}) + +export type CliMessagesResponse = z.infer + +export const CreateSessionResponseSchema = z.object({ + session: SessionSchema +}) + +export type CreateSessionResponse = z.infer + +export const CreateMachineResponseSchema = z.object({ + machine: MachineSchema +}) + +export type CreateMachineResponse = z.infer + +export const GetSessionResponseSchema = CreateSessionResponseSchema +export type GetSessionResponse = CreateSessionResponse + +export type AuthResponse = { + token: string + user: { + id: number + username?: string + firstName?: string + lastName?: string + } +} + +export type SessionsResponse = { sessions: SessionSummary[] } +export type SessionResponse = { session: Session } +export type MessagesResponse = { + messages: DecryptedMessage[] + page: { + limit: number + nextBeforeSeq: number | null + nextBeforeAt: number | null + hasMore: boolean + } +} + +export type MachinesResponse = { machines: Machine[] } + +export type SpawnResponse = + | { type: 'success'; sessionId: string } + | { type: 'error'; message: string } + export type CommandResponse = { success: boolean stdout?: string diff --git a/web/src/types/api.ts b/web/src/types/api.ts index 0d940f9d..ac52a29f 100644 --- a/web/src/types/api.ts +++ b/web/src/types/api.ts @@ -20,11 +20,17 @@ export type { MachineDirectoryEntry, MachineListDirectoryResponse, MachinePathsExistsResponse, + AuthResponse, + MachinesResponse, + MessagesResponse, OpencodeModelsResponse, OpencodeModelSummary, PathExistsResponse, SlashCommand, SlashCommandsResponse, + SessionResponse, + SessionsResponse, + SpawnResponse, UploadFileResponse } from '@hapi/protocol/apiTypes' @@ -73,34 +79,6 @@ export type DecryptedMessage = ProtocolDecryptedMessage & { invokedAt?: number | null } -export type AuthResponse = { - token: string - user: { - id: number - username?: string - firstName?: string - lastName?: string - } -} - -export type SessionsResponse = { sessions: SessionSummary[] } -export type SessionResponse = { session: Session } -export type MessagesResponse = { - messages: DecryptedMessage[] - page: { - limit: number - nextBeforeSeq: number | null - nextBeforeAt: number | null - hasMore: boolean - } -} - -export type MachinesResponse = { machines: Machine[] } - -export type SpawnResponse = - | { type: 'success'; sessionId: string } - | { type: 'error'; message: string } - export type FileSearchItem = { fileName: string filePath: string