refactor: share core REST payload types

This commit is contained in:
weishu
2026-05-21 14:35:47 +08:00
parent 7c26c1e749
commit e88a9075df
4 changed files with 121 additions and 99 deletions
+21 -53
View File
@@ -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<typeof CliMessagesResponseSchema>
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<typeof CreateSessionResponseSchema>
export const CreateMachineResponseSchema = z.object({
machine: MachineSchema
})
export type CreateMachineResponse = z.infer<typeof CreateMachineResponseSchema>
export const GetSessionResponseSchema = CreateSessionResponseSchema
export type GetSessionResponse = z.infer<typeof GetSessionResponseSchema>
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(),
+7 -18
View File
@@ -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<Cl
return c.json({ error: 'Not ready' }, 503)
}
const json = await c.req.json().catch(() => 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<Cl
return c.json({ error: 'Not ready' }, 503)
}
const json = await c.req.json().catch(() => null)
const parsed = createOrLoadMachineSchema.safeParse(json)
const parsed = CreateOrLoadMachineRequestSchema.safeParse(json)
if (!parsed.success) {
return c.json({ error: 'Invalid body' }, 400)
}
+87
View File
@@ -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<typeof CreateOrLoadSessionRequestSchema>
export const CreateOrLoadMachineRequestSchema = z.object({
id: z.string().min(1),
metadata: z.unknown(),
runnerState: z.unknown().nullable().optional()
})
export type CreateOrLoadMachineRequest = z.infer<typeof CreateOrLoadMachineRequestSchema>
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<typeof CliMessagesResponseSchema>
export const CreateSessionResponseSchema = z.object({
session: SessionSchema
})
export type CreateSessionResponse = z.infer<typeof CreateSessionResponseSchema>
export const CreateMachineResponseSchema = z.object({
machine: MachineSchema
})
export type CreateMachineResponse = z.infer<typeof CreateMachineResponseSchema>
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
+6 -28
View File
@@ -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