Share REST and RPC response types

This commit is contained in:
weishu
2026-05-21 10:31:50 +08:00
parent 9698aa9f4c
commit d6f97065c1
13 changed files with 166 additions and 250 deletions
+3 -20
View File
@@ -9,6 +9,7 @@ import { basename, dirname, isAbsolute, join, relative, resolve as resolvePath }
import { logger } from '@/ui/logger'
import { configuration } from '@/configuration'
import type { Update, UpdateMachineBody } from '@hapi/protocol'
import type { MachineDirectoryEntry, MachineListDirectoryResponse, PathExistsResponse } from '@hapi/protocol/apiTypes'
import type { RunnerState, Machine, MachineMetadata } from './types'
import { RunnerStateSchema, MachineMetadataSchema } from './types'
import { backoff } from '@/utils/time'
@@ -68,28 +69,10 @@ interface PathExistsRequest {
paths: string[]
}
interface PathExistsResponse {
exists: Record<string, boolean>
}
interface ListMachineDirectoryRequest {
path: string
}
interface ListMachineDirectoryEntry {
name: string
type: 'file' | 'directory' | 'other'
size?: number
modified?: number
isGitRepo?: boolean
}
interface ListMachineDirectoryResponse {
success: boolean
entries?: ListMachineDirectoryEntry[]
error?: string
}
function normalizeWorkspaceRoots(paths?: string[]): string[] | undefined {
if (!paths?.length) {
return undefined
@@ -163,7 +146,7 @@ export class ApiMachineClient {
return { exists }
})
this.rpcHandlerManager.registerHandler<ListMachineDirectoryRequest, ListMachineDirectoryResponse>('list-directory', async (params) => {
this.rpcHandlerManager.registerHandler<ListMachineDirectoryRequest, MachineListDirectoryResponse>('list-directory', async (params) => {
if (!this.normalizedWorkspaceRoots?.length) {
return { success: false, error: 'Workspace browsing is not enabled for this machine' }
}
@@ -185,7 +168,7 @@ export class ApiMachineClient {
}
const dirEntries = await readdir(targetPath, { withFileTypes: true })
const entries: ListMachineDirectoryEntry[] = []
const entries: MachineDirectoryEntry[] = []
await Promise.all(dirEntries.map(async (entry) => {
if (entry.name.startsWith('.')) return
+2 -13
View File
@@ -1,23 +1,12 @@
import type { CodexModelsResponse, CodexModelSummary } from '@hapi/protocol/apiTypes';
import { CodexAppServerClient } from '@/codex/codexAppServerClient';
import { getErrorMessage } from './rpcResponses';
export interface CodexModelSummary {
id: string;
displayName: string;
isDefault: boolean;
defaultReasoningEffort?: string | null;
supportedReasoningEfforts?: string[];
}
export interface ListCodexModelsRequest {
includeHidden?: boolean;
}
export interface ListCodexModelsResponse {
success: boolean;
models?: CodexModelSummary[];
error?: string;
}
export type ListCodexModelsResponse = CodexModelsResponse;
function asNonEmptyString(value: unknown): string | null {
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : null;
+1 -13
View File
@@ -1,6 +1,7 @@
import { logger } from '@/ui/logger'
import { readdir, stat } from 'fs/promises'
import { basename, join, resolve } from 'path'
import type { DirectoryEntry, ListDirectoryResponse } from '@hapi/protocol/apiTypes'
import type { RpcHandlerManager } from '@/api/rpc/RpcHandlerManager'
import { validatePath } from '../pathSecurity'
import { getErrorMessage, rpcError } from '../rpcResponses'
@@ -9,19 +10,6 @@ interface ListDirectoryRequest {
path: string
}
interface DirectoryEntry {
name: string
type: 'file' | 'directory' | 'other'
size?: number
modified?: number
}
interface ListDirectoryResponse {
success: boolean
entries?: DirectoryEntry[]
error?: string
}
interface GetDirectoryTreeRequest {
path: string
maxDepth: number
+3 -12
View File
@@ -2,6 +2,7 @@ import { logger } from '@/ui/logger'
import { readFile, stat, writeFile } from 'fs/promises'
import { createHash } from 'crypto'
import { resolve } from 'path'
import type { FileReadResponse, GeneratedImageResponse } from '@hapi/protocol/apiTypes'
import type { RpcHandlerManager } from '@/api/rpc/RpcHandlerManager'
import { validatePath } from '../pathSecurity'
import { getGeneratedImage } from '../generatedImages'
@@ -11,23 +12,13 @@ interface ReadFileRequest {
path: string
}
interface ReadFileResponse {
success: boolean
content?: string
error?: string
}
type ReadFileResponse = FileReadResponse
interface ReadGeneratedImageRequest {
id: string
}
interface ReadGeneratedImageResponse {
success: boolean
content?: string
mimeType?: string
fileName?: string
error?: string
}
type ReadGeneratedImageResponse = GeneratedImageResponse
interface WriteFileRequest {
path: string
+2 -7
View File
@@ -1,5 +1,6 @@
import { execFile, type ExecFileOptions } from 'child_process'
import { promisify } from 'util'
import type { CommandResponse } from '@hapi/protocol/apiTypes'
import type { RpcHandlerManager } from '@/api/rpc/RpcHandlerManager'
import { validatePath } from '../pathSecurity'
import { rpcError } from '../rpcResponses'
@@ -24,13 +25,7 @@ interface GitDiffFileRequest {
timeout?: number
}
interface GitCommandResponse {
success: boolean
stdout?: string
stderr?: string
exitCode?: number
error?: string
}
type GitCommandResponse = CommandResponse
function resolveCwd(requestedCwd: string | undefined, workingDirectory: string): { cwd: string; error?: string } {
const cwd = requestedCwd ?? workingDirectory
+1 -11
View File
@@ -2,6 +2,7 @@ import { logger } from '@/ui/logger'
import { mkdir, mkdtemp, rm, writeFile } from 'fs/promises'
import { join, resolve, sep } from 'path'
import { rmSync } from 'node:fs'
import type { DeleteUploadResponse, UploadFileResponse } from '@hapi/protocol/apiTypes'
import type { RpcHandlerManager } from '@/api/rpc/RpcHandlerManager'
import { getErrorMessage, rpcError } from '../rpcResponses'
import { getHapiBlobsDir } from '@/constants/uploadPaths'
@@ -13,22 +14,11 @@ interface UploadFileRequest {
mimeType: string
}
interface UploadFileResponse {
success: boolean
path?: string
error?: string
}
interface DeleteUploadRequest {
sessionId?: string
path: string
}
interface DeleteUploadResponse {
success: boolean
error?: string
}
const uploadDirs = new Map<string, string>()
const uploadDirPromises = new Map<string, Promise<string>>()
const uploadDirCleanupRequested = new Set<string>()
+2 -11
View File
@@ -1,23 +1,14 @@
import { asString, isObject } from '@hapi/protocol';
import type { OpencodeModelsResponse, OpencodeModelSummary } from '@hapi/protocol/apiTypes';
import { AcpStdioTransport } from '@/agent/backends/acp/AcpStdioTransport';
import packageJson from '../../../package.json';
import { getErrorMessage } from './rpcResponses';
export interface OpencodeModelSummary {
modelId: string;
name?: string;
}
export interface ListOpencodeModelsForCwdRequest {
cwd?: string;
}
export interface ListOpencodeModelsForCwdResponse {
success: boolean;
availableModels?: OpencodeModelSummary[];
currentModelId?: string | null;
error?: string;
}
export type ListOpencodeModelsForCwdResponse = OpencodeModelsResponse;
interface CacheEntry {
expiresAt: number;
+26 -75
View File
@@ -1,85 +1,36 @@
import type { AgentFlavor, CodexCollaborationMode, PermissionMode } from '@hapi/protocol/types'
import type {
CodexModelSummary,
CodexModelsResponse,
CommandResponse,
DeleteUploadResponse,
DirectoryEntry,
FileReadResponse,
GeneratedImageResponse,
ListDirectoryResponse,
OpencodeModelsResponse,
OpencodeModelSummary,
PathExistsResponse,
UploadFileResponse
} from '@hapi/protocol/apiTypes'
import type { Server } from 'socket.io'
import type { RpcRegistry } from '../socket/rpcRegistry'
const DEFAULT_RPC_TIMEOUT_MS = 30_000
const MODEL_LIST_RPC_TIMEOUT_MS = 120_000
export type RpcCommandResponse = {
success: boolean
stdout?: string
stderr?: string
exitCode?: number
error?: string
}
export type RpcReadFileResponse = {
success: boolean
content?: string
error?: string
}
export type RpcGeneratedImageResponse = {
success: boolean
content?: string
mimeType?: string
fileName?: string
error?: string
}
export type RpcUploadFileResponse = {
success: boolean
path?: string
error?: string
}
export type RpcDeleteUploadResponse = {
success: boolean
error?: string
}
export type RpcDirectoryEntry = {
name: string
type: 'file' | 'directory' | 'other'
size?: number
modified?: number
}
export type RpcListDirectoryResponse = {
success: boolean
entries?: RpcDirectoryEntry[]
error?: string
}
export type RpcPathExistsResponse = {
exists: Record<string, boolean>
}
export type RpcCodexModel = {
id: string
displayName: string
isDefault: boolean
defaultReasoningEffort?: string | null
supportedReasoningEfforts?: string[]
}
export type RpcListCodexModelsResponse = {
success: boolean
models?: RpcCodexModel[]
error?: string
}
export type RpcOpencodeModel = {
modelId: string
name?: string
}
export type RpcListOpencodeModelsResponse = {
success: boolean
availableModels?: RpcOpencodeModel[]
currentModelId?: string | null
error?: string
}
export type RpcCommandResponse = CommandResponse
export type RpcReadFileResponse = FileReadResponse
export type RpcGeneratedImageResponse = GeneratedImageResponse
export type RpcUploadFileResponse = UploadFileResponse
export type RpcDeleteUploadResponse = DeleteUploadResponse
export type RpcDirectoryEntry = DirectoryEntry
export type RpcListDirectoryResponse = ListDirectoryResponse
export type RpcPathExistsResponse = PathExistsResponse
export type RpcCodexModel = CodexModelSummary
export type RpcListCodexModelsResponse = CodexModelsResponse
export type RpcOpencodeModel = OpencodeModelSummary
export type RpcListOpencodeModelsResponse = OpencodeModelsResponse
export class RpcGateway {
constructor(
+1
View File
@@ -7,6 +7,7 @@
"types": "./src/index.ts",
"exports": {
".": "./src/index.ts",
"./apiTypes": "./src/apiTypes.ts",
"./messages": "./src/messages.ts",
"./buildInfo": "./src/buildInfo.ts",
"./modes": "./src/modes.ts",
+95
View File
@@ -0,0 +1,95 @@
export type CommandResponse = {
success: boolean
stdout?: string
stderr?: string
exitCode?: number
error?: string
}
export type GitCommandResponse = CommandResponse
export type FileReadResponse = {
success: boolean
content?: string
error?: string
}
export type GeneratedImageResponse = {
success: boolean
content?: string
mimeType?: string
fileName?: string
error?: string
}
export type UploadFileResponse = {
success: boolean
path?: string
error?: string
}
export type DeleteUploadResponse = {
success: boolean
error?: string
}
export type DirectoryEntry = {
name: string
type: 'file' | 'directory' | 'other'
size?: number
modified?: number
}
export type ListDirectoryResponse = {
success: boolean
entries?: DirectoryEntry[]
error?: string
}
export type RpcListDirectoryResponse = ListDirectoryResponse
export type MachineDirectoryEntry = DirectoryEntry & {
isGitRepo?: boolean
}
export type MachineListDirectoryResponse = {
success: boolean
entries?: MachineDirectoryEntry[]
error?: string
}
export type PathExistsResponse = {
exists: Record<string, boolean>
}
export type MachinePathsExistsResponse = PathExistsResponse
export type CodexModelSummary = {
id: string
displayName: string
isDefault: boolean
defaultReasoningEffort?: string | null
supportedReasoningEfforts?: string[]
}
export type CodexModelsResponse = {
success: boolean
models?: CodexModelSummary[]
error?: string
}
export type ListCodexModelsResponse = CodexModelsResponse
export type OpencodeModelSummary = {
modelId: string
name?: string
}
export type OpencodeModelsResponse = {
success: boolean
availableModels?: OpencodeModelSummary[]
currentModelId?: string | null
error?: string
}
export type ListOpencodeModelsResponse = OpencodeModelsResponse
+1
View File
@@ -1,3 +1,4 @@
export * from './apiTypes'
export * from './messages'
export * from './buildInfo'
export * from './flavors'
+11 -9
View File
@@ -2,17 +2,9 @@ import type {
AttachmentMetadata,
AuthResponse,
CodexCollaborationMode,
DeleteUploadResponse,
ListDirectoryResponse,
FileReadResponse,
FileSearchResponse,
GitCommandResponse,
MachineListDirectoryResponse,
MachinePathsExistsResponse,
MachinesResponse,
MessagesResponse,
CodexModelsResponse,
OpencodeModelsResponse,
PermissionMode,
PushSubscriptionPayload,
PushUnsubscribePayload,
@@ -20,11 +12,21 @@ import type {
SlashCommandsResponse,
SkillsResponse,
SpawnResponse,
UploadFileResponse,
VisibilityPayload,
SessionResponse,
SessionsResponse
} from '@/types/api'
import type {
CodexModelsResponse,
DeleteUploadResponse,
FileReadResponse,
GitCommandResponse,
ListDirectoryResponse,
MachineListDirectoryResponse,
MachinePathsExistsResponse,
OpencodeModelsResponse,
UploadFileResponse
} from '@hapi/protocol/apiTypes'
import type { AgentFlavor } from '@hapi/protocol'
import type { CancelMessageResponse } from '@hapi/protocol/schemas'
+18 -79
View File
@@ -6,6 +6,24 @@ import type {
WorktreeMetadata
} from '@hapi/protocol/types'
export type {
CodexModelsResponse,
CodexModelSummary,
CommandResponse,
DeleteUploadResponse,
DirectoryEntry,
FileReadResponse,
GitCommandResponse,
ListDirectoryResponse,
MachineDirectoryEntry,
MachineListDirectoryResponse,
MachinePathsExistsResponse,
OpencodeModelsResponse,
OpencodeModelSummary,
PathExistsResponse,
UploadFileResponse
} from '@hapi/protocol/apiTypes'
export type {
AgentState,
AttachmentMetadata,
@@ -100,34 +118,11 @@ export type MessagesResponse = {
}
export type MachinesResponse = { machines: Machine[] }
export type MachinePathsExistsResponse = { exists: Record<string, boolean> }
export type MachineDirectoryEntry = {
name: string
type: 'file' | 'directory' | 'other'
size?: number
modified?: number
isGitRepo?: boolean
}
export type MachineListDirectoryResponse = {
success: boolean
entries?: MachineDirectoryEntry[]
error?: string
}
export type SpawnResponse =
| { type: 'success'; sessionId: string }
| { type: 'error'; message: string }
export type GitCommandResponse = {
success: boolean
stdout?: string
stderr?: string
exitCode?: number
error?: string
}
export type FileSearchItem = {
fileName: string
filePath: string
@@ -141,36 +136,6 @@ export type FileSearchResponse = {
error?: string
}
export type DirectoryEntry = {
name: string
type: 'file' | 'directory' | 'other'
size?: number
modified?: number
}
export type ListDirectoryResponse = {
success: boolean
entries?: DirectoryEntry[]
error?: string
}
export type FileReadResponse = {
success: boolean
content?: string
error?: string
}
export type UploadFileResponse = {
success: boolean
path?: string
error?: string
}
export type DeleteUploadResponse = {
success: boolean
error?: string
}
export type GitFileStatus = {
fileName: string
filePath: string
@@ -215,32 +180,6 @@ export type SkillsResponse = {
error?: string
}
export type CodexModelSummary = {
id: string
displayName: string
isDefault: boolean
defaultReasoningEffort?: string | null
supportedReasoningEfforts?: string[]
}
export type CodexModelsResponse = {
success: boolean
models?: CodexModelSummary[]
error?: string
}
export type OpencodeModelSummary = {
modelId: string
name?: string
}
export type OpencodeModelsResponse = {
success: boolean
availableModels?: OpencodeModelSummary[]
currentModelId?: string | null
error?: string
}
export type PushSubscriptionKeys = {
p256dh: string
auth: string