openhands / src /api /hooks-service.ts
SaylorTwift's picture
SaylorTwift HF Staff
Add files using upload-large-folder tool
63522a5 verified
Raw
History Blame Contribute Delete
1.95 kB
import { HooksClient } from "@openhands/typescript-client/clients";
import type { HookConfig } from "@openhands/typescript-client";
import { getAgentServerWorkingDir } from "./agent-server-config";
import { getEffectiveLocalBackend } from "./backend-registry/active-store";
import { getAgentServerClientOptions } from "./agent-server-client-options";
// `loadHooks()` spends this twice (version probe, then the call) on the
// conversation-start critical path; the SDK's 60s default would stall a launch.
const HOOKS_LOAD_TIMEOUT_MS = 5000;
class HooksService {
/**
* Load workspace hooks from the agent-server by calling POST /api/hooks.
* Returns the HookConfig if the workspace has hooks, or null.
* Gracefully returns null when no usable local backend is available
* (cloud backend, unseeded registry, or older agent-server).
* `projectDir` must be the workspace root: the agent-server checks
* `<project_dir>/.openhands/hooks.json` literally and never walks parents.
*/
static async loadWorkspaceHooks(
projectDir?: string,
): Promise<HookConfig | null> {
// getEffectiveLocalBackend() returns null for cloud backends AND for the
// NO_BACKEND sentinel (unseeded/empty registry), avoiding the silent
// NoBackendAvailableError that getActiveBackend() would cause.
if (!getEffectiveLocalBackend()) {
return null;
}
try {
const response = await new HooksClient(
getAgentServerClientOptions({ timeout: HOOKS_LOAD_TIMEOUT_MS }),
).loadHooks({
project_dir: projectDir ?? getAgentServerWorkingDir(),
});
return response?.hook_config ?? null;
} catch (error) {
// Agent-server may not support the hooks endpoint or may be
// unreachable; gracefully fall back to null.
console.warn(
"Failed to load workspace hooks, continuing without:",
error,
);
return null;
}
}
}
export default HooksService;