huskies: merge 950
This commit is contained in:
@@ -1,8 +1,7 @@
|
||||
/**
|
||||
* HTTP transport layer for the Huskies API client.
|
||||
* Provides the low-level `requestJson` helper, the `callMcpTool` function
|
||||
* for MCP JSON-RPC calls, the `resolveWsHost` utility, and the `api`
|
||||
* object exposing all REST endpoints.
|
||||
* Provides the `callMcpTool` function for MCP JSON-RPC calls, the
|
||||
* `resolveWsHost` utility, and the `api` object exposing all endpoints.
|
||||
*/
|
||||
|
||||
import { rpcCall } from "../rpc";
|
||||
@@ -15,18 +14,13 @@ import type {
|
||||
import type {
|
||||
AllTokenUsageResponse,
|
||||
AnthropicModelInfo,
|
||||
CommandOutput,
|
||||
FileEntry,
|
||||
OAuthStatus,
|
||||
SearchResult,
|
||||
TestResultsResponse,
|
||||
TokenCostResponse,
|
||||
WorkItemContent,
|
||||
} from "./types";
|
||||
|
||||
/** Base URL prefix for all REST API requests in production. */
|
||||
export const DEFAULT_API_BASE = "/api";
|
||||
|
||||
/**
|
||||
* Resolve the WebSocket host to connect to.
|
||||
* In development, uses the injected port (or 3001); in production, uses the
|
||||
@@ -40,31 +34,6 @@ export function resolveWsHost(
|
||||
return isDev ? `127.0.0.1:${envPort || "3001"}` : locationHost;
|
||||
}
|
||||
|
||||
function buildApiUrl(path: string, baseUrl = DEFAULT_API_BASE): string {
|
||||
return `${baseUrl}${path}`;
|
||||
}
|
||||
|
||||
async function requestJson<T>(
|
||||
path: string,
|
||||
options: RequestInit = {},
|
||||
baseUrl = DEFAULT_API_BASE,
|
||||
): Promise<T> {
|
||||
const res = await fetch(buildApiUrl(path, baseUrl), {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
...(options.headers ?? {}),
|
||||
},
|
||||
...options,
|
||||
});
|
||||
|
||||
if (!res.ok) {
|
||||
const text = await res.text();
|
||||
throw new Error(text || `Request failed (${res.status})`);
|
||||
}
|
||||
|
||||
return res.json() as Promise<T>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invoke an MCP tool via the server's JSON-RPC `/mcp` endpoint.
|
||||
* Returns the first text content block from the tool result, or an empty
|
||||
@@ -92,7 +61,7 @@ export async function callMcpTool(
|
||||
return text;
|
||||
}
|
||||
|
||||
/** Typed REST and MCP wrappers for all Huskies server endpoints. */
|
||||
/** Typed wrappers for all Huskies server endpoints. */
|
||||
export const api = {
|
||||
getCurrentProject(_baseUrl?: string) {
|
||||
return rpcCall<string | null>("project.current");
|
||||
@@ -137,40 +106,11 @@ export const api = {
|
||||
const r = await rpcCall<OkResult>("anthropic.set_api_key", params);
|
||||
return r.ok;
|
||||
},
|
||||
readFile(path: string, baseUrl?: string) {
|
||||
return requestJson<string>(
|
||||
"/fs/read",
|
||||
{ method: "POST", body: JSON.stringify({ path }) },
|
||||
baseUrl,
|
||||
);
|
||||
readFile(path: string) {
|
||||
return rpcCall<string>("io.read_file", { path });
|
||||
},
|
||||
writeFile(path: string, content: string, baseUrl?: string) {
|
||||
return requestJson<boolean>(
|
||||
"/fs/write",
|
||||
{ method: "POST", body: JSON.stringify({ path, content }) },
|
||||
baseUrl,
|
||||
);
|
||||
},
|
||||
listDirectory(path: string, baseUrl?: string) {
|
||||
return requestJson<FileEntry[]>(
|
||||
"/fs/list",
|
||||
{ method: "POST", body: JSON.stringify({ path }) },
|
||||
baseUrl,
|
||||
);
|
||||
},
|
||||
listDirectoryAbsolute(path: string, baseUrl?: string) {
|
||||
return requestJson<FileEntry[]>(
|
||||
"/io/fs/list/absolute",
|
||||
{ method: "POST", body: JSON.stringify({ path }) },
|
||||
baseUrl,
|
||||
);
|
||||
},
|
||||
createDirectoryAbsolute(path: string, baseUrl?: string) {
|
||||
return requestJson<boolean>(
|
||||
"/io/fs/create/absolute",
|
||||
{ method: "POST", body: JSON.stringify({ path }) },
|
||||
baseUrl,
|
||||
);
|
||||
listDirectoryAbsolute(path: string) {
|
||||
return rpcCall<FileEntry[]>("io.list_directory_absolute", { path });
|
||||
},
|
||||
getHomeDirectory(_baseUrl?: string) {
|
||||
return rpcCall<string>("io.home_directory");
|
||||
@@ -178,20 +118,6 @@ export const api = {
|
||||
listProjectFiles(_baseUrl?: string) {
|
||||
return rpcCall<string[]>("io.list_project_files");
|
||||
},
|
||||
searchFiles(query: string, baseUrl?: string) {
|
||||
return requestJson<SearchResult[]>(
|
||||
"/fs/search",
|
||||
{ method: "POST", body: JSON.stringify({ query }) },
|
||||
baseUrl,
|
||||
);
|
||||
},
|
||||
execShell(command: string, args: string[], baseUrl?: string) {
|
||||
return requestJson<CommandOutput>(
|
||||
"/shell/exec",
|
||||
{ method: "POST", body: JSON.stringify({ command, args }) },
|
||||
baseUrl,
|
||||
);
|
||||
},
|
||||
async cancelChat(_baseUrl?: string) {
|
||||
const r = await rpcCall<OkResult>("chat.cancel");
|
||||
return r.ok;
|
||||
@@ -237,11 +163,7 @@ export const api = {
|
||||
return rpcCall<OAuthStatus>("oauth.status");
|
||||
},
|
||||
/** Execute a bot slash command without LLM invocation. Returns markdown response text. */
|
||||
botCommand(command: string, args: string, baseUrl?: string) {
|
||||
return requestJson<{ response: string }>(
|
||||
"/bot/command",
|
||||
{ method: "POST", body: JSON.stringify({ command, args }) },
|
||||
baseUrl,
|
||||
);
|
||||
botCommand(command: string, args: string) {
|
||||
return rpcCall<{ response: string }>("bot.command", { command, args });
|
||||
},
|
||||
};
|
||||
|
||||
@@ -33,6 +33,6 @@ export type {
|
||||
WsResponse,
|
||||
} from "./types";
|
||||
|
||||
export { api, callMcpTool, DEFAULT_API_BASE, resolveWsHost } from "./http";
|
||||
export { api, callMcpTool, resolveWsHost } from "./http";
|
||||
|
||||
export { ChatWebSocket } from "./websocket";
|
||||
|
||||
Reference in New Issue
Block a user