spectro-kernel 0.6.1__tar.gz → 0.6.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/.gitignore +5 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/CHANGELOG.md +43 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/PKG-INFO +8 -4
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/README.md +2 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/concepts/algorithms.md +1 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/contributing.md +3 -3
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/getting-started.md +1 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/index.md +1 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/reference.md +1 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/discover-the-catalogue.md +1 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/first-spectrum.md +1 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/long-slit-reduction.md +1 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/usage/mcp.md +1 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/why.md +1 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/pyproject.toml +7 -4
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/version.py +1 -1
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/__main__.py +43 -2
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_http_entry.py +67 -2
- spectro_kernel-0.6.2/tools/docs/__init__.py +6 -0
- spectro_kernel-0.6.2/tools/docs/build.py +82 -0
- spectro_kernel-0.6.2/tools/docs/generate.py +25 -0
- spectro_kernel-0.6.2/tools/docs/generate_catalogue.py +482 -0
- spectro_kernel-0.6.2/tools/docs/generate_llms.py +134 -0
- spectro_kernel-0.6.2/tools/docs/netlify.toml +20 -0
- spectro_kernel-0.6.1/docs/gen_catalogue.py +0 -124
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/LICENSE +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/concepts/architecture.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/concepts/data-types.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/concepts/pipelines.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/cookbook/bess-dashboard.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/cookbook/multi-star-viewer.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/cookbook/web-playground.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_ew_timeseries.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_halpha_phase_stack.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_halpha_timeseries.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_overlay.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pc1_vs_phase.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pca_2d.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_pca_3d.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_periodogram.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_phase_folded.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_rv_timeseries.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_similarity.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_cyg_similarity_date.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_halpha_phase_stack.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pc1_vs_phase.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pca_2d.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_pca_3d.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_periodogram.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_phase_folded.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_rv_timeseries.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_similarity_date.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/images/notebooks/alpha_dra_similarity_phase.png +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/alpha-cyg-time-series.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/alpha-dra-binary-period.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/aurora-line-monitor.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/be-star-variability.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/claude-mcp-end-to-end.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/exoplanet-transit-rv.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/full-reduction-walkthrough.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/native-vs-easyspec-showdown.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/notebooks/your-first-sb2.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/add-an-algorithm.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/tutorials/analyse-a-spectrum.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/usage/cli.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/docs/usage/library.md +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/adapters/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/adapters/easyspec.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/_common.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/aperture_photometry.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/disentangle_sb2.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/advanced/doppler_tomogram.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/gaia.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/simbad.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/catalogs/vizier.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/classify_template_chi2.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/classification/pickles_atlas.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/compare_normalisations.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_edges.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_max.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_percentile.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_polynomial.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/normalize_region.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/continuum/subtract_continuum.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/air_vacuum.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/barycentric.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/doppler_shift.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/extinction_correct_easyspec.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/fit_telluric_scaling.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/flux_calibrate_easyspec.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/remove_telluric.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/response_from_standard.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/corrections/synth_telluric.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_band_power.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_continuum_subtracted.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_lick_indices.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_log_lambda.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_pretrained.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_remote.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_spectrum.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/embedding/embed_wavelets.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/_bess_contract.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_csv.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_fits.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_fits_bess.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_hdf5.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/exports/export_votable.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/boxcar.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/detect_trace.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/easyspec_extract.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/optimal.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/extraction/sky_lateral_bands.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_ascii.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_echelle.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_fits.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_sdss.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/io/read_votable.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/kinematics/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/kinematics/rotation_curve.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/_line_flux.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/_profiles.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/catalogs.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/compare_line_fits.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/detect.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/equivalent_width.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_gaussian.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_lorentzian.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/fit_voigt.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/lines/vr_ratio.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/bpt_line_ratios.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/oiii_electron_temperature.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/nebular/sii_electron_density.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/compare_snr_methods.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_der.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_edge.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/snr_linear_fit.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/quality/validate_bess_header.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/_easyspec_apply.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/_easyspec_helpers.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/bias_combine.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/clip_cosmic_rays.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/dark_combine.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/dark_subtract.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/denoise_2d.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_bias.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_cosmic_ray.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_dark.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_flat.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_flat_normalize.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_bias.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/easyspec_subtract_dark.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/extract_spectrum_sum.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/flat_combine.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/flat_normalize.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/geometry.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/subtract_sky_2d.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/reduction/wavelength_calibrate.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/cross_correlate.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/fit_keplerian_orbit.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/measure.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/precision_bouchy.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/rv/redshift_lines.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/_nan_bridge.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/compare_smoothings.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/smooth_gaussian.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/smoothing/smooth_savgol.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/merge_echelle_orders.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/stacking/stack.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/lomb_scargle.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/timeseries/phase_fold.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/clip_sigma.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/combine_arithmetic.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/extract_region.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/mask_range.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/resample.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/transforms/resample_flux_conserving.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_3d_surface.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_animation.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_dynamic_spectrum.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/viz/plot_plotly.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/arc_geometry.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/easyspec_wavelength.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/in_situ.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/lamp_atlas.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/match_lamp.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/reidentify.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/algorithms/wavelength_calibration/solar.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/base.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/cli.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/embeddings.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/errors.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/io/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/io/ascii.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/io/fits.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/io/votable.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/pipeline.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/balmer_quick.yaml +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/embed_quick.yaml +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/quality_report.yaml +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/rv_quick.yaml +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/snr_check.yaml +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/analysis/time_series_overview.yaml +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/catalog/reduction/full_reduction_easyspec.yaml +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/presets/loader.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/py.typed +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/registry.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/catalog.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/context.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/enums.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/history.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/image.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/line.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/spectrum.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/types/timeseries.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_kernel/url_safety.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/__init__.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/auth.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/auto_tools.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/observability.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/py.typed +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/server.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/session.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/src/spectro_mcp/url_safety.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/conftest.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/reference/conftest.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/reference/test_known_answers.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/reference/test_sun.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/reference/test_vega.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_aperture_photometry.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_barycentric_bjd.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_base.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_catalog_queries_mocked.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_classify_template_chi2.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_cli.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_combine.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_combine_arithmetic.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_continuum.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_corrections.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_cross_correlate_extension.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_dark_flat_combine.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_denoise_2d.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_detect_lines_blind.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_detect_trace.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_disentangle_sb2.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_doppler_tomogram.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_apply_staging.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_sessions.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_easyspec_wrappers.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_embedding.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_embedding_pretrained.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_embedding_remote_lick.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_embedding_tier1.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_export_fits_bess.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_export_hdf5.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_extract_boxcar.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_extract_optimal.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_extract_spectrum_sum.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_flux_calibrate_easyspec.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_geometry_corrections.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_io.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_lamp_atlas_match.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_line_profiles.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_lines.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_param_guard.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_production.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_security.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_mcp_tools_extra.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_measure_arc_geometry.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_misc_algorithms.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_nebular_diagnostics.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_no_circular_imports.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_normalize_to_region.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_output_produces_contract.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_pipeline.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_quality.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_read_sdss.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_redshift_lines.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_registry.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_reidentify.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_resample_flux_conserving.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_response_from_standard.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_rotation_curve.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_rv_precision_bouchy.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_sky_lateral_bands.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_smoothing.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_timeseries.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_transforms.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_types.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_url_safety_kernel.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_validate_bess_header.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_viz.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_vr_ratio.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_wavelength_calibration_in_situ.py +0 -0
- {spectro_kernel-0.6.1 → spectro_kernel-0.6.2}/tests/unit/test_wavelength_calibration_solar.py +0 -0
|
@@ -6,6 +6,49 @@ Until `1.0.0` the public API may change between minor versions.
|
|
|
6
6
|
|
|
7
7
|
## [Unreleased]
|
|
8
8
|
|
|
9
|
+
## [0.6.2] — 2026-09-19
|
|
10
|
+
|
|
11
|
+
A documentation and web release : the docs move to a generated,
|
|
12
|
+
agent-readable site at https://docs.spectrokernel.io/, the landing page is
|
|
13
|
+
rebuilt around the open MCP server, and the server gains CORS so the web
|
|
14
|
+
playground can use it. No algorithm changes.
|
|
15
|
+
|
|
16
|
+
### MCP server
|
|
17
|
+
|
|
18
|
+
- CORS for browser clients in HTTP mode : `SPECTRO_MCP_CORS_ORIGINS`
|
|
19
|
+
(default : the spectrokernel.io sites ; `*` or `none`), with the
|
|
20
|
+
`Mcp-Session-Id` header exposed, so the web playground on the landing
|
|
21
|
+
page can talk to the public server. Pre-flights are answered before the
|
|
22
|
+
API-key check.
|
|
23
|
+
|
|
24
|
+
### Documentation — new build pipeline and hosting (no package change)
|
|
25
|
+
|
|
26
|
+
- The site is now built with **Zensical** (the Material for MkDocs team's
|
|
27
|
+
successor engine, reading the unchanged `mkdocs.yml`) and published at
|
|
28
|
+
**https://docs.spectrokernel.io/** by Netlify, which builds it from the
|
|
29
|
+
repository (base directory `tools/docs`, see `tools/docs/netlify.toml`) ;
|
|
30
|
+
pull requests get a deploy preview and GitHub Actions keeps a strict build
|
|
31
|
+
check. `mkdocs build` keeps working as a fallback.
|
|
32
|
+
- The algorithm catalogue is generated *before* the build by
|
|
33
|
+
`tools/docs/generate_catalogue.py` (Zensical runs no MkDocs plugin such as
|
|
34
|
+
`gen-files`) : one page per algorithm under `/algorithms/<category>/<name>/`
|
|
35
|
+
(summary, backend, version, context reads/writes, parameter table, Python /
|
|
36
|
+
CLI / MCP call examples, references, related algorithms), a category index,
|
|
37
|
+
and a navigation tree consumed by `literate-nav`.
|
|
38
|
+
- Machine-readable layer for agents and tooling : `/ai/algorithms.json`
|
|
39
|
+
(validated at build time against `/ai/schemas/algorithms.schema.json`,
|
|
40
|
+
schema version 1, stamped with the package version), a curated `/llms.txt`
|
|
41
|
+
derived from the navigation, and the Markdown source of every page served
|
|
42
|
+
next to its HTML (`<page>/index.md`). Netlify headers set the content types
|
|
43
|
+
and CORS on `/ai/*`.
|
|
44
|
+
- `tools/docs/build.py` reproduces the CI build locally (generators + strict
|
|
45
|
+
build + post-processing) ; `tools/docs/generate.py && zensical serve` for
|
|
46
|
+
live preview. Generated files (`docs/algorithms/`, `docs/ai/`,
|
|
47
|
+
`docs/llms.txt`) are git-ignored. `.readthedocs.yaml` and
|
|
48
|
+
`docs/gen_catalogue.py` are removed ; the `docs` extra now installs
|
|
49
|
+
`zensical`, `mkdocstrings[python]`, `mkdocs-material`,
|
|
50
|
+
`mkdocs-literate-nav`, `jsonschema`.
|
|
51
|
+
|
|
9
52
|
## [0.6.1] — 2026-09-19
|
|
10
53
|
|
|
11
54
|
A correctness and hardening release driven by a full audit of the
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: spectro-kernel
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.2
|
|
4
4
|
Summary: A shared catalogue of astronomical spectroscopy algorithms, composable into reproducible pipelines, usable as a Python library, a CLI, or an MCP server.
|
|
5
5
|
Project-URL: Homepage, https://github.com/matthieulel/spectro-kernel
|
|
6
|
-
Project-URL: Documentation, https://
|
|
6
|
+
Project-URL: Documentation, https://docs.spectrokernel.io/
|
|
7
7
|
Project-URL: Source, https://github.com/matthieulel/spectro-kernel
|
|
8
8
|
Project-URL: Issues, https://github.com/matthieulel/spectro-kernel/issues
|
|
9
9
|
Project-URL: Changelog, https://github.com/matthieulel/spectro-kernel/blob/main/CHANGELOG.md
|
|
@@ -54,9 +54,12 @@ Requires-Dist: pytest-cov>=5.0; extra == 'dev'
|
|
|
54
54
|
Requires-Dist: pytest>=8.0; extra == 'dev'
|
|
55
55
|
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
56
56
|
Provides-Extra: docs
|
|
57
|
-
Requires-Dist:
|
|
57
|
+
Requires-Dist: jsonschema>=4.0; extra == 'docs'
|
|
58
|
+
Requires-Dist: mkdocs-literate-nav>=0.6; extra == 'docs'
|
|
58
59
|
Requires-Dist: mkdocs-material>=9.5; extra == 'docs'
|
|
59
60
|
Requires-Dist: mkdocstrings[python]>=0.26; extra == 'docs'
|
|
61
|
+
Requires-Dist: pyyaml>=6.0; extra == 'docs'
|
|
62
|
+
Requires-Dist: zensical>=0.0.62; extra == 'docs'
|
|
60
63
|
Provides-Extra: embedding-ml
|
|
61
64
|
Requires-Dist: torch>=2.2; extra == 'embedding-ml'
|
|
62
65
|
Provides-Extra: embedding-remote
|
|
@@ -197,6 +200,7 @@ by environment variable (none of these affect stdio mode) :
|
|
|
197
200
|
| `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
|
|
198
201
|
| `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window request limit per API key (per client IP when auth is off). |
|
|
199
202
|
| `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For` for the client IP. |
|
|
203
|
+
| `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
|
|
200
204
|
| `SPECTRO_MCP_MAX_SESSIONS` | `200` | Cap on live in-memory sessions; the least recently used one is evicted. |
|
|
201
205
|
| `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Refuse downloaded FITS files with more data elements than this (256 MB as float64); `0` disables. |
|
|
202
206
|
| `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget for one URL download (on top of the 256 MiB cap). |
|
|
@@ -234,7 +238,7 @@ from the registry. Build it locally with:
|
|
|
234
238
|
|
|
235
239
|
```bash
|
|
236
240
|
uv pip install -e ".[docs,all]"
|
|
237
|
-
|
|
241
|
+
python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
|
|
238
242
|
```
|
|
239
243
|
|
|
240
244
|
Every algorithm declares its **provenance** — a `backend` (the library it leans on) and
|
|
@@ -111,6 +111,7 @@ by environment variable (none of these affect stdio mode) :
|
|
|
111
111
|
| `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
|
|
112
112
|
| `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window request limit per API key (per client IP when auth is off). |
|
|
113
113
|
| `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For` for the client IP. |
|
|
114
|
+
| `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
|
|
114
115
|
| `SPECTRO_MCP_MAX_SESSIONS` | `200` | Cap on live in-memory sessions; the least recently used one is evicted. |
|
|
115
116
|
| `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Refuse downloaded FITS files with more data elements than this (256 MB as float64); `0` disables. |
|
|
116
117
|
| `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget for one URL download (on top of the 256 MiB cap). |
|
|
@@ -148,7 +149,7 @@ from the registry. Build it locally with:
|
|
|
148
149
|
|
|
149
150
|
```bash
|
|
150
151
|
uv pip install -e ".[docs,all]"
|
|
151
|
-
|
|
152
|
+
python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
|
|
152
153
|
```
|
|
153
154
|
|
|
154
155
|
Every algorithm declares its **provenance** — a `backend` (the library it leans on) and
|
|
@@ -103,7 +103,7 @@ principle](../why.md#the-principle-we-do-not-reinvent). Every algorithm declares
|
|
|
103
103
|
- its **`references`** - the literature for the method, or the wrapped library.
|
|
104
104
|
|
|
105
105
|
Both are visible in `spectro describe <name>`, in the
|
|
106
|
-
[catalogue](../
|
|
106
|
+
[catalogue](../algorithms/index.md), and in every MCP tool description. Nothing is a black box:
|
|
107
107
|
you can always see what an algorithm stands on and where the method comes from.
|
|
108
108
|
|
|
109
109
|
## Naming conventions
|
|
@@ -57,10 +57,10 @@ CI runs the same checks on every pull request; keep `main` green.
|
|
|
57
57
|
|
|
58
58
|
```bash
|
|
59
59
|
uv pip install -e ".[docs,all]"
|
|
60
|
-
|
|
61
|
-
|
|
60
|
+
python tools/docs/generate.py && zensical serve # live preview at http://127.0.0.1:8000
|
|
61
|
+
python tools/docs/build.py # what CI runs: generators + strict build into site/
|
|
62
62
|
```
|
|
63
63
|
|
|
64
|
-
The [algorithm catalogue](
|
|
64
|
+
The [algorithm catalogue](algorithms/index.md) page is *generated* from the registry at build
|
|
65
65
|
time - you never edit it by hand. Add a docstring and metadata to your algorithm and the
|
|
66
66
|
catalogue page documents it automatically.
|
|
@@ -114,4 +114,4 @@ The agent now sees every algorithm as a tool. See [As an MCP server](usage/mcp.m
|
|
|
114
114
|
catalogue](tutorials/discover-the-catalogue.md).
|
|
115
115
|
- Understand the moving parts → [Concepts: architecture](concepts/architecture.md).
|
|
116
116
|
- A guided, end-to-end analysis → [Tutorial: analyse a spectrum](tutorials/analyse-a-spectrum.md).
|
|
117
|
-
- Browse everything available → [Algorithm catalogue](
|
|
117
|
+
- Browse everything available → [Algorithm catalogue](algorithms/index.md).
|
|
@@ -49,7 +49,7 @@ through MCP, you can do from a terminal, and vice-versa.
|
|
|
49
49
|
your first analysis in five minutes.
|
|
50
50
|
- :material-sitemap: **[Concepts](concepts/architecture.md)** - the architecture,
|
|
51
51
|
the data types, the registry, the pipelines.
|
|
52
|
-
- :material-book-open-variant: **[Algorithm catalogue](
|
|
52
|
+
- :material-book-open-variant: **[Algorithm catalogue](algorithms/index.md)** - every
|
|
53
53
|
algorithm, auto-generated from the code.
|
|
54
54
|
- :material-school: **[Tutorials](tutorials/first-spectrum.md)** - from *"I captured a
|
|
55
55
|
spectrum of a star, now what?"* all the way to writing your own algorithm.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
This page is generated from the source docstrings by
|
|
4
4
|
[mkdocstrings](https://mkdocstrings.github.io/). It documents the public Python API; for
|
|
5
|
-
the *algorithms* themselves see the [catalogue](
|
|
5
|
+
the *algorithms* themselves see the [catalogue](algorithms/index.md).
|
|
6
6
|
|
|
7
7
|
## Data types
|
|
8
8
|
|
|
@@ -106,7 +106,7 @@ Over [MCP](../usage/mcp.md), the discovery tools are `list_algorithms` and
|
|
|
106
106
|
|
|
107
107
|
## The full reference
|
|
108
108
|
|
|
109
|
-
The [Algorithm catalogue](../
|
|
109
|
+
The [Algorithm catalogue](../algorithms/index.md) page lists **every** algorithm with its
|
|
110
110
|
parameters, provenance and references, grouped by category. It is generated directly
|
|
111
111
|
from the registry, so it is always in sync with the version you have installed.
|
|
112
112
|
|
|
@@ -149,4 +149,4 @@ checked, continuum normalised, lines identified. That is a genuine scientific re
|
|
|
149
149
|
operation available, and how to inspect each one.
|
|
150
150
|
- **A fuller, guided analysis** → [Analyse a spectrum](analyse-a-spectrum.md).
|
|
151
151
|
- **Periodic targets** (Be stars, eclipsing binaries) → the `lomb_scargle` and
|
|
152
|
-
`phase_fold` algorithms in the [catalogue](../
|
|
152
|
+
`phase_fold` algorithms in the [catalogue](../algorithms/index.md).
|
|
@@ -167,5 +167,5 @@ Every algorithm above follows the same kernel contract:
|
|
|
167
167
|
`ctx.image.meta` (for 2-D) or `ctx.spectrum.meta` (for 1-D), and the
|
|
168
168
|
per-step `metrics` are JSON-serialisable for later auditing.
|
|
169
169
|
|
|
170
|
-
See [Algorithm catalogue](../
|
|
170
|
+
See [Algorithm catalogue](../algorithms/index.md) for the full parameter and
|
|
171
171
|
reference list of each brick.
|
|
@@ -136,6 +136,7 @@ bricks are now fetched through the same guard as `load_spectrum`.
|
|
|
136
136
|
| `SPECTRO_MCP_ALLOW_KEY_IN_URL` | unset | `1` also accepts the key as `?api_key=…` in the URL (for claude.ai / Claude Desktop connectors, which cannot set headers); the parameter is scrubbed before access logging. `Authorization: Bearer <key>` is always accepted. |
|
|
137
137
|
| `SPECTRO_MCP_RATE_PER_MINUTE` | `0` (off) | Sliding-window limit per API key (per client IP when auth is off). |
|
|
138
138
|
| `SPECTRO_MCP_TRUSTED_PROXY` | unset | `1` behind a load balancer: trust `X-Forwarded-For`. |
|
|
139
|
+
| `SPECTRO_MCP_CORS_ORIGINS` | spectrokernel.io sites | Comma-separated browser origins allowed to call the server (`*` for any, `none` to disable). The `Mcp-Session-Id` header is exposed to browsers. |
|
|
139
140
|
| `SPECTRO_MCP_MAX_SESSIONS` | `200` | Live in-memory sessions before LRU eviction. |
|
|
140
141
|
| `SPECTRO_MCP_MAX_PIXELS` | `32000000` | Largest downloaded FITS accepted (data elements); `0` disables. |
|
|
141
142
|
| `SPECTRO_MCP_DOWNLOAD_BUDGET_S` | `120` | Wall-clock budget per URL download. |
|
|
@@ -89,7 +89,7 @@ Every algorithm makes this visible through its `backend` field:
|
|
|
89
89
|
| `plotly` | Renders figures with Plotly. |
|
|
90
90
|
|
|
91
91
|
…and through its `references` field, which cites the literature. You can see both in
|
|
92
|
-
`spectro describe <name>`, in the [catalogue](
|
|
92
|
+
`spectro describe <name>`, in the [catalogue](algorithms/index.md), and in every MCP tool
|
|
93
93
|
description.
|
|
94
94
|
|
|
95
95
|
### Examples
|
|
@@ -64,9 +64,12 @@ dev = [
|
|
|
64
64
|
"ruff>=0.5",
|
|
65
65
|
]
|
|
66
66
|
docs = [
|
|
67
|
-
"
|
|
68
|
-
"mkdocstrings[python]>=0.26",
|
|
69
|
-
"mkdocs-
|
|
67
|
+
"zensical>=0.0.62", # the site engine (Material for MkDocs successor)
|
|
68
|
+
"mkdocstrings[python]>=0.26", # Python API reference (Griffe), used natively by Zensical
|
|
69
|
+
"mkdocs-material>=9.5", # fallback engine for the same mkdocs.yml
|
|
70
|
+
"mkdocs-literate-nav>=0.6", # fallback engine's nav plugin (Zensical has it built in)
|
|
71
|
+
"jsonschema>=4.0", # validates docs/ai/algorithms.json at build time
|
|
72
|
+
"pyyaml>=6.0",
|
|
70
73
|
]
|
|
71
74
|
all = [
|
|
72
75
|
"spectro-kernel[catalogs,viz,storage,reduction,mcp,monitoring,embedding-wavelets,embedding-remote]",
|
|
@@ -81,7 +84,7 @@ spectro-mcp = "spectro_mcp.__main__:main"
|
|
|
81
84
|
|
|
82
85
|
[project.urls]
|
|
83
86
|
Homepage = "https://github.com/matthieulel/spectro-kernel"
|
|
84
|
-
Documentation = "https://
|
|
87
|
+
Documentation = "https://docs.spectrokernel.io/"
|
|
85
88
|
Source = "https://github.com/matthieulel/spectro-kernel"
|
|
86
89
|
Issues = "https://github.com/matthieulel/spectro-kernel/issues"
|
|
87
90
|
Changelog = "https://github.com/matthieulel/spectro-kernel/blob/main/CHANGELOG.md"
|
|
@@ -80,8 +80,10 @@ def _http_api_key() -> str | None:
|
|
|
80
80
|
return api_key
|
|
81
81
|
if _truthy("SPECTRO_MCP_ALLOW_ANONYMOUS"):
|
|
82
82
|
print(
|
|
83
|
-
"spectro-mcp:
|
|
84
|
-
"
|
|
83
|
+
"spectro-mcp: open mode (SPECTRO_MCP_ALLOW_ANONYMOUS=1): no API key "
|
|
84
|
+
"required. Local paths are refused, URLs go through the SSRF guard, "
|
|
85
|
+
"sessions / downloads / memory are capped and requests are rate-limited "
|
|
86
|
+
"per client IP. Set SPECTRO_MCP_API_KEY to close the server.",
|
|
85
87
|
file=sys.stderr,
|
|
86
88
|
)
|
|
87
89
|
return None
|
|
@@ -96,6 +98,28 @@ def _http_api_key() -> str | None:
|
|
|
96
98
|
raise SystemExit(2)
|
|
97
99
|
|
|
98
100
|
|
|
101
|
+
DEFAULT_CORS_ORIGINS = (
|
|
102
|
+
"https://spectrokernel.io",
|
|
103
|
+
"https://www.spectrokernel.io",
|
|
104
|
+
"https://docs.spectrokernel.io",
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _cors_origins() -> list[str]:
|
|
109
|
+
"""Origins allowed to call the HTTP server from a browser.
|
|
110
|
+
|
|
111
|
+
``SPECTRO_MCP_CORS_ORIGINS`` is a comma-separated list (``*`` for any
|
|
112
|
+
origin, ``none`` to disable) ; unset means the spectrokernel.io sites.
|
|
113
|
+
"""
|
|
114
|
+
raw = os.environ.get("SPECTRO_MCP_CORS_ORIGINS")
|
|
115
|
+
if raw is None:
|
|
116
|
+
return list(DEFAULT_CORS_ORIGINS)
|
|
117
|
+
items = [o.strip() for o in raw.split(",") if o.strip()]
|
|
118
|
+
if not items or items == ["none"]:
|
|
119
|
+
return []
|
|
120
|
+
return items
|
|
121
|
+
|
|
122
|
+
|
|
99
123
|
def _run_http(server, args, api_key: str | None) -> int:
|
|
100
124
|
"""Serve over HTTP, wiring API-key auth + optional rate-limit middleware.
|
|
101
125
|
|
|
@@ -124,6 +148,23 @@ def _run_http(server, args, api_key: str | None) -> int:
|
|
|
124
148
|
carriers += ", ?api_key= query parameter (scrubbed from access logs)"
|
|
125
149
|
print(f"spectro-mcp: API-key auth enabled ({carriers}).", file=sys.stderr)
|
|
126
150
|
|
|
151
|
+
# Browser clients (the playground on spectrokernel.io) need CORS ; added
|
|
152
|
+
# last so it is the outermost layer and OPTIONS pre-flights are answered
|
|
153
|
+
# before the API-key check (browsers send no custom header on a pre-flight).
|
|
154
|
+
origins = _cors_origins()
|
|
155
|
+
if origins:
|
|
156
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
157
|
+
|
|
158
|
+
app.add_middleware(
|
|
159
|
+
CORSMiddleware,
|
|
160
|
+
allow_origins=origins,
|
|
161
|
+
allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
|
|
162
|
+
allow_headers=["*"],
|
|
163
|
+
expose_headers=["Mcp-Session-Id"],
|
|
164
|
+
max_age=600,
|
|
165
|
+
)
|
|
166
|
+
print(f"spectro-mcp: CORS enabled for {', '.join(origins)}.", file=sys.stderr)
|
|
167
|
+
|
|
127
168
|
uvicorn_kwargs: dict = {"log_config": None}
|
|
128
169
|
if _truthy("SPECTRO_MCP_TRUSTED_PROXY"):
|
|
129
170
|
# Behind a load balancer (DigitalOcean App Platform, nginx, …) the
|
|
@@ -97,9 +97,12 @@ def test_http_with_key_puts_auth_outermost(fake_boot, clean_env):
|
|
|
97
97
|
clean_env.setenv("SPECTRO_MCP_API_KEY", "secret")
|
|
98
98
|
clean_env.setenv("SPECTRO_MCP_RATE_PER_MINUTE", "30")
|
|
99
99
|
assert entry.main(["--http"]) == 0
|
|
100
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
101
|
+
|
|
100
102
|
classes = [m.cls for m in fake_boot["app"].user_middleware]
|
|
101
|
-
# Starlette: index 0 is the outermost layer.
|
|
102
|
-
|
|
103
|
+
# Starlette: index 0 is the outermost layer. CORS sits outside auth so
|
|
104
|
+
# browser pre-flights are answered; auth stays outside the rate limiter.
|
|
105
|
+
assert classes == [CORSMiddleware, ApiKeyMiddleware, RateLimitMiddleware]
|
|
103
106
|
assert fake_boot["uvicorn_kwargs"].get("proxy_headers") is None
|
|
104
107
|
assert fake_boot["build_kwargs"]["mask_error_details"] is True
|
|
105
108
|
|
|
@@ -137,3 +140,65 @@ def test_stdio_mode_needs_no_key(clean_env, monkeypatch):
|
|
|
137
140
|
assert record["ran"] is True
|
|
138
141
|
assert "mask_error_details" not in record["build_kwargs"]
|
|
139
142
|
assert os.environ.get("SPECTRO_MCP_DENY_LOCAL_PATHS") is None
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
# --- CORS ------------------------------------------------------------------ #
|
|
146
|
+
def test_cors_origins_default_and_overrides(monkeypatch):
|
|
147
|
+
from spectro_mcp.__main__ import DEFAULT_CORS_ORIGINS, _cors_origins
|
|
148
|
+
|
|
149
|
+
monkeypatch.delenv("SPECTRO_MCP_CORS_ORIGINS", raising=False)
|
|
150
|
+
assert _cors_origins() == list(DEFAULT_CORS_ORIGINS)
|
|
151
|
+
monkeypatch.setenv("SPECTRO_MCP_CORS_ORIGINS", "https://a.example, https://b.example")
|
|
152
|
+
assert _cors_origins() == ["https://a.example", "https://b.example"]
|
|
153
|
+
monkeypatch.setenv("SPECTRO_MCP_CORS_ORIGINS", "*")
|
|
154
|
+
assert _cors_origins() == ["*"]
|
|
155
|
+
monkeypatch.setenv("SPECTRO_MCP_CORS_ORIGINS", "none")
|
|
156
|
+
assert _cors_origins() == []
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def test_cors_preflight_is_answered_before_auth():
|
|
160
|
+
"""A browser pre-flight carries no X-API-Key: CORS must sit outside auth."""
|
|
161
|
+
from starlette.applications import Starlette
|
|
162
|
+
from starlette.middleware.cors import CORSMiddleware
|
|
163
|
+
from starlette.responses import PlainTextResponse
|
|
164
|
+
from starlette.routing import Route
|
|
165
|
+
from starlette.testclient import TestClient
|
|
166
|
+
|
|
167
|
+
async def ok(_request):
|
|
168
|
+
return PlainTextResponse("ok")
|
|
169
|
+
|
|
170
|
+
app = Starlette(routes=[Route("/mcp", ok, methods=["POST"])])
|
|
171
|
+
# Same registration order as _run_http: auth first, CORS last (outermost).
|
|
172
|
+
app.add_middleware(ApiKeyMiddleware, expected="secret")
|
|
173
|
+
app.add_middleware(
|
|
174
|
+
CORSMiddleware,
|
|
175
|
+
allow_origins=["https://spectrokernel.io"],
|
|
176
|
+
allow_methods=["GET", "POST", "DELETE", "OPTIONS"],
|
|
177
|
+
allow_headers=["*"],
|
|
178
|
+
expose_headers=["Mcp-Session-Id"],
|
|
179
|
+
)
|
|
180
|
+
client = TestClient(app)
|
|
181
|
+
pre = client.options(
|
|
182
|
+
"/mcp",
|
|
183
|
+
headers={
|
|
184
|
+
"Origin": "https://spectrokernel.io",
|
|
185
|
+
"Access-Control-Request-Method": "POST",
|
|
186
|
+
"Access-Control-Request-Headers": "content-type,mcp-session-id",
|
|
187
|
+
},
|
|
188
|
+
)
|
|
189
|
+
assert pre.status_code == 200
|
|
190
|
+
assert pre.headers["access-control-allow-origin"] == "https://spectrokernel.io"
|
|
191
|
+
assert "mcp-session-id" in pre.headers["access-control-allow-headers"].lower()
|
|
192
|
+
# Real request from an allowed origin: auth still applies, headers exposed.
|
|
193
|
+
ok_headers = {"Origin": "https://spectrokernel.io", "X-API-Key": "secret"}
|
|
194
|
+
resp = client.post("/mcp", headers=ok_headers)
|
|
195
|
+
assert resp.status_code == 200
|
|
196
|
+
assert resp.headers["access-control-allow-origin"] == "https://spectrokernel.io"
|
|
197
|
+
assert "Mcp-Session-Id" in resp.headers["access-control-expose-headers"]
|
|
198
|
+
assert client.post("/mcp", headers={"Origin": "https://spectrokernel.io"}).status_code == 401
|
|
199
|
+
# Disallowed origin gets no CORS grant.
|
|
200
|
+
other = client.options(
|
|
201
|
+
"/mcp",
|
|
202
|
+
headers={"Origin": "https://evil.example", "Access-Control-Request-Method": "POST"},
|
|
203
|
+
)
|
|
204
|
+
assert "access-control-allow-origin" not in other.headers
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
"""Documentation tooling: generators that turn the live registry into pages and JSON.
|
|
2
|
+
|
|
3
|
+
Run ``python tools/docs/build.py`` for a full build, or ``python tools/docs/generate.py``
|
|
4
|
+
before ``zensical serve``. Everything written under ``docs/algorithms/``, ``docs/ai/``
|
|
5
|
+
and ``docs/llms.txt`` is generated and git-ignored.
|
|
6
|
+
"""
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"""Build the documentation site the way CI does.
|
|
2
|
+
|
|
3
|
+
python tools/docs/build.py # generate + zensical build --strict + post-process
|
|
4
|
+
python tools/docs/build.py --no-strict
|
|
5
|
+
|
|
6
|
+
Steps :
|
|
7
|
+
|
|
8
|
+
1. run the generators (``generate.py``) so every generated page exists before the
|
|
9
|
+
engine looks for it - Zensical does not run MkDocs plugins such as gen-files ;
|
|
10
|
+
2. ``zensical build --strict`` (warnings are errors, exactly like CI) ;
|
|
11
|
+
3. post-process ``site/`` for the hosting platform : publish the Markdown source of
|
|
12
|
+
every page next to its HTML (``<page>/index.md``) so agents can fetch it, and write
|
|
13
|
+
the Netlify ``_headers`` file (content types, CORS on the JSON artefacts).
|
|
14
|
+
"""
|
|
15
|
+
|
|
16
|
+
from __future__ import annotations
|
|
17
|
+
|
|
18
|
+
import shutil
|
|
19
|
+
import subprocess
|
|
20
|
+
import sys
|
|
21
|
+
from pathlib import Path
|
|
22
|
+
|
|
23
|
+
ROOT = Path(__file__).resolve().parents[2]
|
|
24
|
+
DOCS = ROOT / "docs"
|
|
25
|
+
SITE = ROOT / "site"
|
|
26
|
+
|
|
27
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
28
|
+
from generate import generate_all # noqa: E402
|
|
29
|
+
|
|
30
|
+
HEADERS = """\
|
|
31
|
+
# Generated by tools/docs/build.py - Netlify header rules for docs.spectrokernel.io
|
|
32
|
+
/*
|
|
33
|
+
X-Content-Type-Options: nosniff
|
|
34
|
+
Referrer-Policy: strict-origin-when-cross-origin
|
|
35
|
+
/*.md
|
|
36
|
+
Content-Type: text/markdown; charset=utf-8
|
|
37
|
+
/llms.txt
|
|
38
|
+
Content-Type: text/plain; charset=utf-8
|
|
39
|
+
/ai/*
|
|
40
|
+
Access-Control-Allow-Origin: *
|
|
41
|
+
Cache-Control: public, max-age=300
|
|
42
|
+
"""
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def _zensical() -> str:
|
|
46
|
+
candidate = Path(sys.executable).with_name("zensical")
|
|
47
|
+
return str(candidate) if candidate.exists() else (shutil.which("zensical") or "zensical")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def publish_markdown_sources() -> int:
|
|
51
|
+
"""Copy each ``docs/**/*.md`` page to ``site/<url>/index.md``."""
|
|
52
|
+
count = 0
|
|
53
|
+
for src in DOCS.rglob("*.md"):
|
|
54
|
+
rel = src.relative_to(DOCS)
|
|
55
|
+
if rel.name.startswith("."):
|
|
56
|
+
continue
|
|
57
|
+
if rel.name == "index.md":
|
|
58
|
+
target = SITE / rel.parent / "index.md"
|
|
59
|
+
else:
|
|
60
|
+
target = SITE / rel.with_suffix("") / "index.md"
|
|
61
|
+
target.parent.mkdir(parents=True, exist_ok=True)
|
|
62
|
+
shutil.copyfile(src, target)
|
|
63
|
+
count += 1
|
|
64
|
+
return count
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def main(argv: list[str]) -> int:
|
|
68
|
+
strict = "--no-strict" not in argv
|
|
69
|
+
generate_all()
|
|
70
|
+
cmd = [_zensical(), "build"] + (["--strict"] if strict else [])
|
|
71
|
+
print("build:", " ".join(cmd))
|
|
72
|
+
result = subprocess.run(cmd, cwd=ROOT)
|
|
73
|
+
if result.returncode != 0:
|
|
74
|
+
return result.returncode
|
|
75
|
+
n = publish_markdown_sources()
|
|
76
|
+
(SITE / "_headers").write_text(HEADERS, encoding="utf-8")
|
|
77
|
+
print(f"build: {n} Markdown sources published next to their pages, _headers written")
|
|
78
|
+
return 0
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
if __name__ == "__main__":
|
|
82
|
+
raise SystemExit(main(sys.argv[1:]))
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Run every documentation generator (catalogue pages + JSON, llms.txt).
|
|
2
|
+
|
|
3
|
+
python tools/docs/generate.py && zensical serve
|
|
4
|
+
|
|
5
|
+
The build script (``tools/docs/build.py``) calls this before building.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import sys
|
|
11
|
+
from pathlib import Path
|
|
12
|
+
|
|
13
|
+
sys.path.insert(0, str(Path(__file__).resolve().parent))
|
|
14
|
+
|
|
15
|
+
import generate_catalogue # noqa: E402
|
|
16
|
+
import generate_llms # noqa: E402
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def generate_all() -> None:
|
|
20
|
+
generate_catalogue.generate()
|
|
21
|
+
generate_llms.generate()
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
if __name__ == "__main__":
|
|
25
|
+
generate_all()
|