You've already forked agentic-coding-workflow
167 lines
4.9 KiB
TypeScript
167 lines
4.9 KiB
TypeScript
import { request } from './client'
|
|
|
|
// Admin DTO from `internal/acp/dto.go: AgentKindAdminDTO`.
|
|
export interface AgentKindAdmin {
|
|
id: string
|
|
name: string
|
|
display_name: string
|
|
description: string
|
|
binary_path: string
|
|
args: string[]
|
|
env_keys: string[]
|
|
enabled: boolean
|
|
tool_allowlist: string[]
|
|
created_by: string
|
|
created_at: string
|
|
updated_at: string
|
|
}
|
|
|
|
// Public DTO (non-admin view) from `internal/acp/dto.go: AgentKindPublicDTO`.
|
|
export interface AgentKindPublic {
|
|
id: string
|
|
name: string
|
|
display_name: string
|
|
enabled: boolean
|
|
}
|
|
|
|
// Type guard distinguishing admin vs public DTO based on presence of admin-only fields.
|
|
export function isAgentKindAdmin(k: AgentKindAdmin | AgentKindPublic): k is AgentKindAdmin {
|
|
return 'binary_path' in k
|
|
}
|
|
|
|
export interface CreateAgentKindReq {
|
|
name: string
|
|
display_name: string
|
|
description?: string
|
|
binary_path: string
|
|
args?: string[]
|
|
env?: Record<string, string>
|
|
enabled?: boolean
|
|
tool_allowlist?: string[]
|
|
}
|
|
|
|
export interface UpdateAgentKindReq {
|
|
display_name?: string
|
|
description?: string
|
|
binary_path?: string
|
|
args?: string[]
|
|
env?: Record<string, string>
|
|
enabled?: boolean
|
|
tool_allowlist?: string[]
|
|
}
|
|
|
|
// PermissionRequest mirrors backend PermissionRequestDTO.
|
|
export interface PermissionRequest {
|
|
id: string
|
|
session_id: string
|
|
tool_name: string
|
|
tool_call: unknown
|
|
options: { id: string; name?: string }[]
|
|
status: string
|
|
created_at: string
|
|
}
|
|
|
|
// AcpSession mirrors the backend session DTO returned by /api/v1/acp/sessions.
|
|
export interface AcpSession {
|
|
id: string
|
|
workspace_id: string
|
|
project_id: string
|
|
agent_kind_id: string
|
|
user_id: string
|
|
issue_id: string | null
|
|
requirement_id: string | null
|
|
agent_session_id: string | null
|
|
branch: string
|
|
cwd_path: string
|
|
is_main_worktree: boolean
|
|
status: 'starting' | 'running' | 'crashed' | 'exited'
|
|
pid: number | null
|
|
exit_code: number | null
|
|
last_error: string | null
|
|
started_at: string
|
|
ended_at: string | null
|
|
}
|
|
|
|
export interface CreateSessionReq {
|
|
workspace_id: string
|
|
agent_kind_id: string
|
|
branch?: string
|
|
issue_number?: number
|
|
requirement_number?: number
|
|
initial_prompt?: string
|
|
}
|
|
|
|
// AcpEvent is one frame from the session event stream (history REST + WS push).
|
|
export interface AcpEvent {
|
|
id: number
|
|
direction: 'in' | 'out'
|
|
kind:
|
|
| 'request'
|
|
| 'response'
|
|
| 'notification'
|
|
| 'error'
|
|
| 'state'
|
|
| 'session_terminated'
|
|
| 'slow_consumer_disconnect'
|
|
| 'client_error'
|
|
| 'permission_request'
|
|
| 'permission_resolved'
|
|
method?: string
|
|
payload: unknown
|
|
truncated: boolean
|
|
}
|
|
|
|
const enc = (v: string) => encodeURIComponent(v)
|
|
|
|
export const acpApi = {
|
|
agentKinds: {
|
|
list: () =>
|
|
request<(AgentKindAdmin | AgentKindPublic)[]>('/api/v1/acp/agent-kinds'),
|
|
get: (id: string) =>
|
|
request<AgentKindAdmin>(`/api/v1/acp/agent-kinds/${enc(id)}`),
|
|
create: (req: CreateAgentKindReq) =>
|
|
request<AgentKindAdmin>('/api/v1/acp/agent-kinds', { method: 'POST', body: req }),
|
|
update: (id: string, req: UpdateAgentKindReq) =>
|
|
request<AgentKindAdmin>(`/api/v1/acp/agent-kinds/${enc(id)}`, { method: 'PATCH', body: req }),
|
|
remove: (id: string) =>
|
|
request<void>(`/api/v1/acp/agent-kinds/${enc(id)}`, { method: 'DELETE' }),
|
|
},
|
|
sessions: {
|
|
list: (all?: boolean) =>
|
|
request<AcpSession[]>('/api/v1/acp/sessions' + (all ? '?all=true' : '')),
|
|
get: (id: string) =>
|
|
request<AcpSession>(`/api/v1/acp/sessions/${enc(id)}`),
|
|
create: (req: CreateSessionReq) =>
|
|
request<AcpSession>('/api/v1/acp/sessions', { method: 'POST', body: req }),
|
|
terminate: (id: string) =>
|
|
request<void>(`/api/v1/acp/sessions/${enc(id)}`, { method: 'DELETE' }),
|
|
events: (id: string, since: number) =>
|
|
request<AcpEvent[]>(`/api/v1/acp/sessions/${enc(id)}/events?since=${since}`),
|
|
permissions: (id: string) =>
|
|
request<PermissionRequest[]>(`/api/v1/acp/sessions/${enc(id)}/permissions`),
|
|
},
|
|
permissions: {
|
|
approve: (reqID: string, optionId?: string) =>
|
|
request<void>(`/api/v1/acp/permissions/${enc(reqID)}/approve`, {
|
|
method: 'POST',
|
|
body: optionId ? { option_id: optionId } : {},
|
|
}),
|
|
deny: (reqID: string) =>
|
|
request<void>(`/api/v1/acp/permissions/${enc(reqID)}/deny`, { method: 'POST' }),
|
|
},
|
|
}
|
|
|
|
// openSessionWS opens a native WebSocket to /sessions/{id}/ws.
|
|
//
|
|
// Auth: browser WebSocket cannot send custom headers, so the token is sent
|
|
// via the ?token= query parameter. The backend auth middleware accepts it
|
|
// as a fallback to Authorization. Keep token lifetimes short.
|
|
export function openSessionWS(sessionId: string, since: number, token: string): WebSocket {
|
|
const proto = window.location.protocol === 'https:' ? 'wss' : 'ws'
|
|
const host = window.location.host
|
|
const url =
|
|
`${proto}://${host}/api/v1/acp/sessions/${enc(sessionId)}` +
|
|
`/ws?since=${since}&token=${enc(token)}`
|
|
return new WebSocket(url)
|
|
}
|