(input: {
readonly ctx: PluginCtx<OpenapiStore>;
readonly integration: IntegrationRecord;
readonly credential: ToolInvocationCredential;
readonly spec?: HealthCheckSpec;
readonly httpClientLayer: Layer.Layer<HttpClient.HttpClient, never, never>;
})
| 861 | * reasons: a rejected credential is a `HealthCheckResult` with |
| 862 | * `status: "expired"`, not an error. */ |
| 863 | export const checkHealthOpenApi = (input: { |
| 864 | readonly ctx: PluginCtx<OpenapiStore>; |
| 865 | readonly integration: IntegrationRecord; |
| 866 | readonly credential: ToolInvocationCredential; |
| 867 | readonly spec?: HealthCheckSpec; |
| 868 | readonly httpClientLayer: Layer.Layer<HttpClient.HttpClient, never, never>; |
| 869 | }): Effect.Effect<HealthCheckResult, StorageFailure> => |
| 870 | Effect.gen(function* () { |
| 871 | const checkedAt = Date.now(); |
| 872 | const config = decodeOpenApiIntegrationConfig(input.integration.config); |
| 873 | const spec = input.spec; |
| 874 | if (!spec) { |
| 875 | return { |
| 876 | status: "unknown", |
| 877 | checkedAt, |
| 878 | detail: "No health check configured.", |
| 879 | } satisfies HealthCheckResult; |
| 880 | } |
| 881 | |
| 882 | const integration = String(input.integration.slug); |
| 883 | const binding = yield* resolveHealthCheckBinding( |
| 884 | input.ctx, |
| 885 | integration, |
| 886 | spec.operation, |
| 887 | config, |
| 888 | ); |
| 889 | if (!binding) { |
| 890 | return { |
| 891 | status: "unknown", |
| 892 | checkedAt, |
| 893 | detail: `Health check operation "${spec.operation}" not found on "${integration}".`, |
| 894 | } satisfies HealthCheckResult; |
| 895 | } |
| 896 | |
| 897 | // HARD block, not just a ranking hint: a health check runs unattended and |
| 898 | // repeatedly, so a mutating operation must never execute through it. The |
| 899 | // normal tool path gates these behind approval, and this path has no |
| 900 | // approval step. The candidate list labels these "(writes)"; refusing here |
| 901 | // is the enforcement. |
| 902 | if (REQUIRE_APPROVAL.has(binding.method.toLowerCase())) { |
| 903 | return { |
| 904 | status: "unknown", |
| 905 | checkedAt, |
| 906 | detail: `Health check operation "${spec.operation}" is a ${binding.method.toUpperCase()} (mutating): pick a read-only operation.`, |
| 907 | } satisfies HealthCheckResult; |
| 908 | } |
| 909 | |
| 910 | const headers: Record<string, string> = { ...(config?.headers ?? {}) }; |
| 911 | const queryParams: Record<string, string> = { ...(config?.queryParams ?? {}) }; |
| 912 | |
| 913 | const template = (config?.authenticationTemplate ?? []).find( |
| 914 | (entry) => String(entry.slug) === String(input.credential.template), |
| 915 | ); |
| 916 | if (template) { |
| 917 | const missing = requiredTemplateVariables(template).filter((name) => { |
| 918 | const value = input.credential.values[name]; |
| 919 | return value == null || value === ""; |
| 920 | }); |
no test coverage detected