File size: 1,954 Bytes
63522a5
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
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;