mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
feat: add directory autocomplete with validation for new session form
This commit is contained in:
@@ -133,16 +133,8 @@ export type RpcReadFileResponse = {
|
||||
error?: string
|
||||
}
|
||||
|
||||
export type SlashCommand = {
|
||||
name: string
|
||||
description?: string
|
||||
source: 'builtin' | 'user'
|
||||
}
|
||||
|
||||
export type RpcSlashCommandsResponse = {
|
||||
success: boolean
|
||||
commands?: SlashCommand[]
|
||||
error?: string
|
||||
export type RpcPathExistsResponse = {
|
||||
exists: Record<string, boolean>
|
||||
}
|
||||
|
||||
export type SyncEventType =
|
||||
@@ -697,6 +689,24 @@ export class SyncEngine {
|
||||
}
|
||||
}
|
||||
|
||||
async checkPathsExist(machineId: string, paths: string[]): Promise<Record<string, boolean>> {
|
||||
const result = await this.machineRpc(machineId, 'path-exists', { paths }) as RpcPathExistsResponse | unknown
|
||||
if (!result || typeof result !== 'object') {
|
||||
throw new Error('Unexpected path-exists result')
|
||||
}
|
||||
|
||||
const existsValue = (result as RpcPathExistsResponse).exists
|
||||
if (!existsValue || typeof existsValue !== 'object') {
|
||||
throw new Error('Unexpected path-exists result')
|
||||
}
|
||||
|
||||
const exists: Record<string, boolean> = {}
|
||||
for (const [key, value] of Object.entries(existsValue)) {
|
||||
exists[key] = value === true
|
||||
}
|
||||
return exists
|
||||
}
|
||||
|
||||
async getGitStatus(sessionId: string, cwd?: string): Promise<RpcCommandResponse> {
|
||||
return await this.sessionRpc(sessionId, 'git-status', { cwd }) as RpcCommandResponse
|
||||
}
|
||||
@@ -717,10 +727,6 @@ export class SyncEngine {
|
||||
return await this.sessionRpc(sessionId, 'ripgrep', { args, cwd }) as RpcCommandResponse
|
||||
}
|
||||
|
||||
async listSlashCommands(sessionId: string, agent: string): Promise<RpcSlashCommandsResponse> {
|
||||
return await this.sessionRpc(sessionId, 'listSlashCommands', { agent }) as RpcSlashCommandsResponse
|
||||
}
|
||||
|
||||
private async sessionRpc(sessionId: string, method: string, params: unknown): Promise<unknown> {
|
||||
return await this.rpcCall(`${sessionId}:${method}`, params)
|
||||
}
|
||||
|
||||
@@ -11,6 +11,10 @@ const spawnBodySchema = z.object({
|
||||
worktreeName: z.string().optional()
|
||||
})
|
||||
|
||||
const pathsExistsSchema = z.object({
|
||||
paths: z.array(z.string().min(1)).max(1000)
|
||||
})
|
||||
|
||||
export function createMachinesRoutes(getSyncEngine: () => SyncEngine | null): Hono<WebAppEnv> {
|
||||
const app = new Hono<WebAppEnv>()
|
||||
|
||||
@@ -53,5 +57,36 @@ export function createMachinesRoutes(getSyncEngine: () => SyncEngine | null): Ho
|
||||
return c.json(result)
|
||||
})
|
||||
|
||||
app.post('/machines/:id/paths/exists', async (c) => {
|
||||
const engine = getSyncEngine()
|
||||
if (!engine) {
|
||||
return c.json({ error: 'Not connected' }, 503)
|
||||
}
|
||||
|
||||
const machineId = c.req.param('id')
|
||||
const machine = engine.getMachine(machineId)
|
||||
if (!machine) {
|
||||
return c.json({ error: 'Machine not found' }, 404)
|
||||
}
|
||||
|
||||
const body = await c.req.json().catch(() => null)
|
||||
const parsed = pathsExistsSchema.safeParse(body)
|
||||
if (!parsed.success) {
|
||||
return c.json({ error: 'Invalid body' }, 400)
|
||||
}
|
||||
|
||||
const uniquePaths = Array.from(new Set(parsed.data.paths.map((path) => path.trim()).filter(Boolean)))
|
||||
if (uniquePaths.length === 0) {
|
||||
return c.json({ exists: {} })
|
||||
}
|
||||
|
||||
try {
|
||||
const exists = await engine.checkPathsExist(machineId, uniquePaths)
|
||||
return c.json({ exists })
|
||||
} catch (error) {
|
||||
return c.json({ error: error instanceof Error ? error.message : 'Failed to check paths' }, 500)
|
||||
}
|
||||
})
|
||||
|
||||
return app
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import { requireSessionFromParam, requireSyncEngine } from './guards'
|
||||
type SessionSummaryMetadata = {
|
||||
name?: string
|
||||
path: string
|
||||
machineId?: string
|
||||
summary?: { text: string }
|
||||
flavor?: string | null
|
||||
worktree?: {
|
||||
@@ -35,6 +36,7 @@ function toSessionSummary(session: Session): SessionSummary {
|
||||
const metadata: SessionSummaryMetadata | null = session.metadata ? {
|
||||
name: session.metadata.name,
|
||||
path: session.metadata.path,
|
||||
machineId: session.metadata.machineId ?? undefined,
|
||||
summary: session.metadata.summary ? { text: session.metadata.summary.text } : undefined,
|
||||
flavor: session.metadata.flavor ?? null,
|
||||
worktree: session.metadata.worktree
|
||||
|
||||
Reference in New Issue
Block a user