diff --git a/misc/Makefile b/misc/Makefile index ce54990634cd..8913eeae2c67 100644 --- a/misc/Makefile +++ b/misc/Makefile @@ -1,813 +1,814 @@ COMMENT = Miscellaneous utilities SUBDIR += R-cran-gapminder SUBDIR += R-cran-mime SUBDIR += R-cran-optimx SUBDIR += R-cran-xfun SUBDIR += aclgen SUBDIR += actiona SUBDIR += adiak SUBDIR += adios2 SUBDIR += aichat SUBDIR += air SUBDIR += alpaca SUBDIR += amanda-client SUBDIR += amanda-perl-wrapper SUBDIR += amanda-server SUBDIR += ansiweather SUBDIR += apparix SUBDIR += arrow-datafusion SUBDIR += artikulate SUBDIR += asbutton SUBDIR += asr-manpages SUBDIR += astc-encoder SUBDIR += bb SUBDIR += bbjd SUBDIR += bdelta SUBDIR += bedrust SUBDIR += bestfit SUBDIR += bible-kjv SUBDIR += biblesync SUBDIR += bibletime SUBDIR += biblical-curse SUBDIR += binbloom SUBDIR += binclock SUBDIR += binocle SUBDIR += biosfont-demos SUBDIR += birthday SUBDIR += blogwatcher SUBDIR += bogosort SUBDIR += bottlerocket SUBDIR += box2d SUBDIR += boxes SUBDIR += broot SUBDIR += bsod SUBDIR += bun-decompile SUBDIR += caffe SUBDIR += camsnap SUBDIR += chatblade SUBDIR += chatgpt-shell-cli SUBDIR += cheat SUBDIR += chef SUBDIR += chmlib SUBDIR += chrono SUBDIR += cinnamon-translations SUBDIR += claude-code SUBDIR += clawhub SUBDIR += clex SUBDIR += clifm SUBDIR += cloc SUBDIR += clpbar SUBDIR += code2prompt SUBDIR += codex SUBDIR += colortail SUBDIR += coloursum SUBDIR += colwide SUBDIR += comfy-cli SUBDIR += comfyui SUBDIR += compat.el SUBDIR += compat10x SUBDIR += compat11x SUBDIR += compat12x SUBDIR += compat13x SUBDIR += compat14x SUBDIR += compat15x SUBDIR += compat4x SUBDIR += compat5x SUBDIR += compat6x SUBDIR += compat7x SUBDIR += compat8x SUBDIR += compat9x SUBDIR += concourse SUBDIR += copperspice-examples SUBDIR += countrycodes SUBDIR += cpp-mcp SUBDIR += cpuid SUBDIR += crosti SUBDIR += crush SUBDIR += cryptoballot SUBDIR += cs SUBDIR += cstream SUBDIR += ctm SUBDIR += cuttlefish SUBDIR += dahdi SUBDIR += dahdi-kmod26 SUBDIR += darknet SUBDIR += dartsim SUBDIR += ddate SUBDIR += deark SUBDIR += deco SUBDIR += dejagnu SUBDIR += delay SUBDIR += diction SUBDIR += digitemp SUBDIR += dijo SUBDIR += dive SUBDIR += dlib-models SUBDIR += dlpack SUBDIR += dnetc SUBDIR += dotenv-linter SUBDIR += dtach SUBDIR += dvorak7min SUBDIR += dynomite SUBDIR += ecflow SUBDIR += edfbrowser SUBDIR += edflib SUBDIR += eightctl SUBDIR += elki SUBDIR += elscreen SUBDIR += emacs-libvterm SUBDIR += estic SUBDIR += exercism SUBDIR += explosions SUBDIR += ezc3d SUBDIR += fabric SUBDIR += far2l SUBDIR += fastscapelib SUBDIR += felis SUBDIR += figlet SUBDIR += figlet-fonts SUBDIR += findutils SUBDIR += flag SUBDIR += flag-icons SUBDIR += flashlight SUBDIR += fortune-mod-bible SUBDIR += fortune-mod-bofh SUBDIR += fortune-mod-epictetus SUBDIR += fortune-mod-freebsd-classic SUBDIR += fortune-mod-futurama SUBDIR += fortune-mod-mormon SUBDIR += fortune-mod-psalms SUBDIR += fortuneit SUBDIR += fq SUBDIR += free42 SUBDIR += freebsd-doc-all SUBDIR += freebsd-doc-bn SUBDIR += freebsd-doc-da SUBDIR += freebsd-doc-de SUBDIR += freebsd-doc-el SUBDIR += freebsd-doc-en SUBDIR += freebsd-doc-es SUBDIR += freebsd-doc-fr SUBDIR += freebsd-doc-hu SUBDIR += freebsd-doc-id SUBDIR += freebsd-doc-it SUBDIR += freebsd-doc-ja SUBDIR += freebsd-doc-ko SUBDIR += freebsd-doc-mn SUBDIR += freebsd-doc-nl SUBDIR += freebsd-doc-pl SUBDIR += freebsd-doc-pt SUBDIR += freebsd-doc-ru SUBDIR += freebsd-doc-tr SUBDIR += freebsd-doc-zh_cn SUBDIR += freebsd-doc-zh_tw SUBDIR += freebsd-release-manifests SUBDIR += freeguide SUBDIR += frugally-deep SUBDIR += ftdi-eeprom SUBDIR += fxload SUBDIR += g810-led SUBDIR += gedkeeper SUBDIR += geekcode SUBDIR += gemini-cli SUBDIR += getopt SUBDIR += ggml SUBDIR += gimp-help-en SUBDIR += github-copilot-cli SUBDIR += github-copilot-language-server SUBDIR += gitlogue SUBDIR += gkrellm-gamma SUBDIR += gkrellm-xkb SUBDIR += gkrellmbgchg2 SUBDIR += gkrellmlaunch2 SUBDIR += gkrellshoot2 SUBDIR += gkx86info2 SUBDIR += global-tz SUBDIR += gnome-devel-docs SUBDIR += gnome-getting-started-docs SUBDIR += gnome-icon-theme SUBDIR += gnome-icon-theme-extras SUBDIR += gnome-mime-data SUBDIR += gnome-user-docs SUBDIR += gnu-watch SUBDIR += gobuster SUBDIR += gollama SUBDIR += gone SUBDIR += goplaces SUBDIR += gplink SUBDIR += gpsim SUBDIR += granulate SUBDIR += grc SUBDIR += grizzly SUBDIR += grok-cli SUBDIR += gwhich SUBDIR += hashcards SUBDIR += hashdb SUBDIR += hello SUBDIR += help2man + SUBDIR += hermes-agent SUBDIR += heyu2 SUBDIR += hicolor-icon-theme SUBDIR += histring SUBDIR += hotkeys SUBDIR += hs-hascard SUBDIR += hulgalugha SUBDIR += hwdata SUBDIR += hxtools SUBDIR += hyprqt6engine SUBDIR += ignition-fuel-tools SUBDIR += iio-oscilloscope SUBDIR += imerge SUBDIR += img2xterm SUBDIR += ini_file_manager SUBDIR += inplace SUBDIR += ipa_conv SUBDIR += ipbt SUBDIR += ironscanner SUBDIR += iselect SUBDIR += iso-codes SUBDIR += jargon SUBDIR += jojodiff SUBDIR += json-tui SUBDIR += kbdscan SUBDIR += kcd SUBDIR += kde-thumbnailer-chm SUBDIR += kde-thumbnailer-epub SUBDIR += kde-thumbnailer-fb2 SUBDIR += kdeedu SUBDIR += kdeedu-data SUBDIR += kdeutils SUBDIR += kenny SUBDIR += kf5-purpose SUBDIR += kf6-purpose SUBDIR += kgeography SUBDIR += klettres SUBDIR += koboldcpp SUBDIR += kookbook SUBDIR += ktouch SUBDIR += kwordquiz SUBDIR += lastools SUBDIR += latex-mk SUBDIR += lazyread SUBDIR += lbann SUBDIR += lc SUBDIR += lean-ctx SUBDIR += lesspipe SUBDIR += lf SUBDIR += libad9361-iio SUBDIR += libartnet SUBDIR += libcomps SUBDIR += libeatmydata SUBDIR += libemos SUBDIR += libiio SUBDIR += libkeduvocdocument SUBDIR += liblxi SUBDIR += libmetalink SUBDIR += libmodulemd SUBDIR += libpostal SUBDIR += libpredict SUBDIR += libpri SUBDIR += librepo SUBDIR += libsolv SUBDIR += libspnav SUBDIR += libsupertone SUBDIR += libsweep-lidar SUBDIR += libutf SUBDIR += libuuid SUBDIR += libxdf SUBDIR += lifelines SUBDIR += lightgbm SUBDIR += linux-c7-sdl12-extralibs SUBDIR += linux-rl9-sdl12-extralibs SUBDIR += linux-rl9-sdl20-extralibs SUBDIR += llama-cpp SUBDIR += locale-en_DK SUBDIR += localedata SUBDIR += logsurfer SUBDIR += ls-lint SUBDIR += lscolors SUBDIR += lsix SUBDIR += lv SUBDIR += lxde-common SUBDIR += lxi-tools SUBDIR += magicpoint SUBDIR += man.el SUBDIR += mate-user-guide SUBDIR += mbuffer SUBDIR += mc SUBDIR += mcp-inspector SUBDIR += mcp-probe SUBDIR += mdp SUBDIR += memo SUBDIR += meshoptimizer SUBDIR += metalink-checker SUBDIR += metalink-tools SUBDIR += mime-support SUBDIR += minidnn SUBDIR += minuet SUBDIR += mirmon SUBDIR += mmdnn SUBDIR += mmv SUBDIR += mnn SUBDIR += molequeue SUBDIR += mtx SUBDIR += najitool SUBDIR += nanocoder SUBDIR += navi SUBDIR += ncnn SUBDIR += netron SUBDIR += nms SUBDIR += nn-insight SUBDIR += nnn SUBDIR += nsf SUBDIR += numchar SUBDIR += nut SUBDIR += ola SUBDIR += ollama SUBDIR += ompl SUBDIR += ondir SUBDIR += onnx SUBDIR += onnxruntime SUBDIR += ooo SUBDIR += openclaw SUBDIR += opencyc SUBDIR += openhab SUBDIR += openhab-addons SUBDIR += openhantek SUBDIR += openhmd SUBDIR += openmvg SUBDIR += opennn SUBDIR += openr2 SUBDIR += openvdb SUBDIR += orville-write SUBDIR += osinfo-db SUBDIR += ossp-uuid SUBDIR += otter SUBDIR += outside SUBDIR += owrep SUBDIR += p5-Acme-ButFirst SUBDIR += p5-Array-Compare SUBDIR += p5-Array-Diff SUBDIR += p5-Array-FileReader SUBDIR += p5-Array-IntSpan SUBDIR += p5-Array-LineReader SUBDIR += p5-Array-PrintCols SUBDIR += p5-Array-RefElem SUBDIR += p5-Array-Utils SUBDIR += p5-Asterisk-FastAGI SUBDIR += p5-Bot-Training SUBDIR += p5-Bot-Training-MegaHAL SUBDIR += p5-Bot-Training-StarCraft SUBDIR += p5-Business-EDI SUBDIR += p5-Business-Hours SUBDIR += p5-Business-ISBN SUBDIR += p5-Business-ISBN-Data SUBDIR += p5-Business-ISIN SUBDIR += p5-Business-ISMN SUBDIR += p5-Business-ISSN SUBDIR += p5-Business-UPS SUBDIR += p5-Chatbot-Eliza SUBDIR += p5-Data-Buffer SUBDIR += p5-File-CounterFile SUBDIR += p5-File-Sort SUBDIR += p5-Geo-Address-Formatter SUBDIR += p5-Geo-Cache SUBDIR += p5-Geo-Coder-Bing SUBDIR += p5-Geo-Coder-Google SUBDIR += p5-Geo-Coder-Googlev3 SUBDIR += p5-Geo-Coder-Mapquest SUBDIR += p5-Geo-Coder-Multimap SUBDIR += p5-Geo-Coder-Multiple SUBDIR += p5-Geo-Coder-Navteq SUBDIR += p5-Geo-Coder-OSM SUBDIR += p5-Geo-Coder-OpenCage SUBDIR += p5-Geo-Coder-Ovi SUBDIR += p5-Geo-Coder-PlaceFinder SUBDIR += p5-Geo-Coder-TomTom SUBDIR += p5-Geo-GeoNames SUBDIR += p5-Geo-Postcodes SUBDIR += p5-Geo-Postcodes-DK SUBDIR += p5-Geo-Postcodes-NO SUBDIR += p5-Geo-Weather SUBDIR += p5-Geography-Countries SUBDIR += p5-I18N-Charset SUBDIR += p5-I18N-LangTags SUBDIR += p5-LEGO-RCX SUBDIR += p5-Library-CallNumber-LC SUBDIR += p5-List-Compare SUBDIR += p5-List-Util-WeightedRoundRobin SUBDIR += p5-Locale-Codes SUBDIR += p5-Locale-Geocode SUBDIR += p5-Locale-Msgcat SUBDIR += p5-Locale-SubCountry SUBDIR += p5-Locale-US SUBDIR += p5-Net-vCard SUBDIR += p5-OSSP-uuid SUBDIR += p5-OpenAI-API SUBDIR += p5-Software-License SUBDIR += p5-Software-License-CCpack SUBDIR += p5-Test-Dir SUBDIR += p5-Text-FIGlet SUBDIR += p5-Tie-DxHash SUBDIR += p5-Zen-Koans SUBDIR += p5-asterisk-perl SUBDIR += parley SUBDIR += pastebinit SUBDIR += patchutils SUBDIR += pauker SUBDIR += pciids SUBDIR += pdmenu SUBDIR += pear-Services_Weather SUBDIR += pear-Text_Figlet SUBDIR += pecl-timezonedb SUBDIR += perkeep SUBDIR += pfm SUBDIR += php82-calendar SUBDIR += php83-calendar SUBDIR += php84-calendar SUBDIR += php85-calendar SUBDIR += pinfo SUBDIR += pipe SUBDIR += posixtestsuite SUBDIR += pqrs SUBDIR += proxyper SUBDIR += pspresent SUBDIR += ptsort SUBDIR += pubs SUBDIR += py-NATTEN SUBDIR += py-PyUserInput SUBDIR += py-PyWavefront SUBDIR += py-QSpectrumAnalyzer SUBDIR += py-SQCommon SUBDIR += py-SQImFil SUBDIR += py-SQSnobFit SUBDIR += py-SimpleSoapy SUBDIR += py-SoapySDR SUBDIR += py-accelerate SUBDIR += py-alive-progress SUBDIR += py-anthropic SUBDIR += py-apache-beam SUBDIR += py-ascii-magic SUBDIR += py-ast-comments SUBDIR += py-bitsandbytes SUBDIR += py-boost-histogram SUBDIR += py-censys SUBDIR += py-cmap SUBDIR += py-codecarbon SUBDIR += py-colorbrewer SUBDIR += py-colored SUBDIR += py-colorzero SUBDIR += py-comfy-aimdo SUBDIR += py-comfy-kitchen SUBDIR += py-comfyui-embedded-docs SUBDIR += py-comfyui-frontend-package SUBDIR += py-comfyui-workflow-templates SUBDIR += py-comfyui-workflow-templates-core SUBDIR += py-comfyui-workflow-templates-media-api SUBDIR += py-comfyui-workflow-templates-media-image SUBDIR += py-comfyui-workflow-templates-media-other SUBDIR += py-comfyui-workflow-templates-media-video SUBDIR += py-compressed-tensors SUBDIR += py-cppman SUBDIR += py-crudini SUBDIR += py-csvw SUBDIR += py-datasets SUBDIR += py-detecta SUBDIR += py-diffusers SUBDIR += py-eemeter SUBDIR += py-einops SUBDIR += py-emoji SUBDIR += py-equinox SUBDIR += py-evaluate SUBDIR += py-ezc3d SUBDIR += py-facenet-pytorch SUBDIR += py-fal-client SUBDIR += py-fast-histogram SUBDIR += py-fastmcp SUBDIR += py-fastscapelib SUBDIR += py-files-to-prompt SUBDIR += py-fleep SUBDIR += py-fuzzy SUBDIR += py-gguf SUBDIR += py-google-ai-generativelanguage SUBDIR += py-google-genai SUBDIR += py-gpiozero SUBDIR += py-gymnasium SUBDIR += py-halo SUBDIR += py-haystack_ai SUBDIR += py-haystack_experimental SUBDIR += py-hf-doc-builder SUBDIR += py-hf-xet SUBDIR += py-hist SUBDIR += py-histoprint SUBDIR += py-huepy SUBDIR += py-huggingface-hub SUBDIR += py-icoextract SUBDIR += py-instructor SUBDIR += py-ipyfastscape SUBDIR += py-jiwer SUBDIR += py-kartograph SUBDIR += py-kokoro SUBDIR += py-langchain SUBDIR += py-langchain-anthropic SUBDIR += py-langchain-core SUBDIR += py-langchain-google-genai SUBDIR += py-langchain-openai SUBDIR += py-langchain-tests SUBDIR += py-langchain-text-splitters SUBDIR += py-langgraph SUBDIR += py-langgraph-checkpoint SUBDIR += py-langgraph-prebuilt SUBDIR += py-langgraph-sdk SUBDIR += py-langsmith SUBDIR += py-laspy SUBDIR += py-lazrs SUBDIR += py-lightgbm SUBDIR += py-lightning-utilities SUBDIR += py-lion-pytorch SUBDIR += py-litellm SUBDIR += py-litellm-proxy-extras SUBDIR += py-llama-cpp-python SUBDIR += py-llm SUBDIR += py-llm-claude-3 SUBDIR += py-lm-format-enforcer SUBDIR += py-log_symbols SUBDIR += py-mcp SUBDIR += py-mem0ai SUBDIR += py-mffpy SUBDIR += py-misaki SUBDIR += py-mistral-common SUBDIR += py-mixpanel SUBDIR += py-ml-collections SUBDIR += py-mmcv SUBDIR += py-mmdet SUBDIR += py-mmengine SUBDIR += py-mmsegmentation SUBDIR += py-model-hosting-container-standards SUBDIR += py-molequeue SUBDIR += py-napari SUBDIR += py-napari-svg SUBDIR += py-npTDMS SUBDIR += py-npe2 SUBDIR += py-numcodecs SUBDIR += py-ollama SUBDIR += py-onnx SUBDIR += py-onnx-tf SUBDIR += py-onnxconverter-common SUBDIR += py-openai SUBDIR += py-openai-harmony SUBDIR += py-openai-whisper SUBDIR += py-openpaperwork-core SUBDIR += py-openpaperwork-gtk SUBDIR += py-openvdb SUBDIR += py-optuna SUBDIR += py-oterm SUBDIR += py-palettable SUBDIR += py-papermill SUBDIR += py-pdd SUBDIR += py-peft SUBDIR += py-pexpect SUBDIR += py-pkbar SUBDIR += py-placekey SUBDIR += py-polars SUBDIR += py-polars-runtime SUBDIR += py-posthog SUBDIR += py-powerline-status SUBDIR += py-progressbar SUBDIR += py-progressbar2 SUBDIR += py-pyEDFlib SUBDIR += py-pyct SUBDIR += py-pyfiglet SUBDIR += py-pykml SUBDIR += py-pymatreader SUBDIR += py-pyowm SUBDIR += py-pypatent SUBDIR += py-pyprind SUBDIR += py-pyqir SUBDIR += py-python-crc SUBDIR += py-python-geohash SUBDIR += py-python-pygtrie SUBDIR += py-python-utils SUBDIR += py-pytorch SUBDIR += py-pytorch-lightning SUBDIR += py-pyviz-comms SUBDIR += py-runloop-api-client SUBDIR += py-safetensors SUBDIR += py-sagemaker SUBDIR += py-sagemaker-core SUBDIR += py-sagemaker-mlops SUBDIR += py-sagemaker-serve SUBDIR += py-sagemaker-shim SUBDIR += py-sagemaker-train SUBDIR += py-schedulefree SUBDIR += py-scikit-fusion SUBDIR += py-scikit-quant SUBDIR += py-sentence-transformers SUBDIR += py-serverfiles SUBDIR += py-shap2 SUBDIR += py-shell-gpt SUBDIR += py-sigopt SUBDIR += py-smdebug-rulesconfig SUBDIR += py-snakemake-interface-common SUBDIR += py-snakemake-interface-executor-plugins SUBDIR += py-snakemake-interface-logger-plugins SUBDIR += py-snakemake-interface-report-plugins SUBDIR += py-snakemake-interface-scheduler-plugins SUBDIR += py-snakemake-interface-storage-plugins SUBDIR += py-soapy_power SUBDIR += py-spandrel SUBDIR += py-spdx-tools SUBDIR += py-spinners SUBDIR += py-synced-collections SUBDIR += py-tes SUBDIR += py-tflearn SUBDIR += py-tflite SUBDIR += py-tflite-support SUBDIR += py-thermocouples_reference SUBDIR += py-timm SUBDIR += py-timm-vis SUBDIR += py-toil SUBDIR += py-torch-geometric SUBDIR += py-torchao SUBDIR += py-torchdata SUBDIR += py-torchmetrics SUBDIR += py-torchsummary SUBDIR += py-torchvision SUBDIR += py-tqdm SUBDIR += py-transformers SUBDIR += py-tvm SUBDIR += py-uhi SUBDIR += py-uuid-utils SUBDIR += py-vaderSentiment SUBDIR += py-vllm SUBDIR += py-wandb SUBDIR += py-wurlitzer SUBDIR += py-xformers SUBDIR += py-xgboost SUBDIR += py-xgrammar SUBDIR += pyobd SUBDIR += pytorch SUBDIR += qbrew SUBDIR += qlas SUBDIR += qlcplus SUBDIR += qman SUBDIR += qt5-doc SUBDIR += qt5-examples SUBDIR += qt5-l10n SUBDIR += qt5ct SUBDIR += qt6-doc SUBDIR += qt6-examples SUBDIR += qt6ct SUBDIR += quantum++ SUBDIR += quary SUBDIR += qwen-code SUBDIR += radio_datetime_analyzer SUBDIR += raja SUBDIR += randomx SUBDIR += rash SUBDIR += raspberrypi-gpioshutdown SUBDIR += raspbsd-led SUBDIR += ree SUBDIR += retry SUBDIR += rfc SUBDIR += rgb-tui SUBDIR += ringtonetools SUBDIR += rname SUBDIR += rpl SUBDIR += rsclock SUBDIR += rtfm SUBDIR += rubygem-dotenv SUBDIR += rubygem-dotenv-rails SUBDIR += rubygem-dotenv-rails-rails4 SUBDIR += rubygem-dotenv-rails-rails5 SUBDIR += rubygem-dotenv-rails-rails50 SUBDIR += rubygem-dotenv2 SUBDIR += rubygem-haversine_distance SUBDIR += rubygem-iesd SUBDIR += rubygem-mime-types SUBDIR += rubygem-mime-types-data SUBDIR += rubygem-mime-types-gitlab SUBDIR += rubygem-mimemagic SUBDIR += rubygem-octicons SUBDIR += rubygem-rabbit SUBDIR += rubygem-vpim SUBDIR += rump SUBDIR += rustlings SUBDIR += ruut SUBDIR += schilytools SUBDIR += sdformat SUBDIR += seabios SUBDIR += sent SUBDIR += seq2 SUBDIR += sff SUBDIR += sfm SUBDIR += shared-mime-info SUBDIR += shc SUBDIR += shimmy SUBDIR += shuffle SUBDIR += shuffle-db SUBDIR += silicon SUBDIR += since SUBDIR += slides SUBDIR += sloccount SUBDIR += sls SUBDIR += snakemake SUBDIR += soapysdr SUBDIR += solarized SUBDIR += sonoscli SUBDIR += spacenavd-devel SUBDIR += spnavcfg-devel SUBDIR += sshbuddy SUBDIR += sssnips SUBDIR += star-history SUBDIR += stormy SUBDIR += susv2 SUBDIR += susv3 SUBDIR += susv4 SUBDIR += swissfileknife SUBDIR += sword SUBDIR += table.el SUBDIR += talkfilters SUBDIR += tdfiglet SUBDIR += tdrop SUBDIR += team SUBDIR += tellico SUBDIR += tempcontrol SUBDIR += tenere SUBDIR += termatrix SUBDIR += terminfo-db SUBDIR += teseq SUBDIR += thefuck SUBDIR += thrill SUBDIR += timetrace SUBDIR += timezonemap SUBDIR += timr-tui SUBDIR += tkcron SUBDIR += tkinfo SUBDIR += tkregexp SUBDIR += tkrunit SUBDIR += tlci SUBDIR += tlm SUBDIR += toilet SUBDIR += translate SUBDIR += trurl SUBDIR += ttyrec SUBDIR += tudu SUBDIR += tvbrowser SUBDIR += tvm SUBDIR += tzdiff SUBDIR += umodunpack SUBDIR += unclutter SUBDIR += unclutter-xfixes SUBDIR += unicode-emoji SUBDIR += units SUBDIR += upclient SUBDIR += urh SUBDIR += usbids SUBDIR += usbrh-libusb SUBDIR += utftools SUBDIR += utouch-kmod SUBDIR += valentina SUBDIR += vdmfec SUBDIR += veles SUBDIR += vera SUBDIR += vfc SUBDIR += vifm SUBDIR += vifm-colors SUBDIR += visp SUBDIR += viz SUBDIR += vote SUBDIR += vxl SUBDIR += wacli SUBDIR += wavedrom SUBDIR += wego SUBDIR += whichman SUBDIR += window SUBDIR += with SUBDIR += wmScoreBoard SUBDIR += wmcalendar SUBDIR += wmforecast SUBDIR += wmjulia SUBDIR += wmpal SUBDIR += wmtunlo SUBDIR += wmweather+ SUBDIR += wmwork SUBDIR += wthrr SUBDIR += xd-rust SUBDIR += xdelta SUBDIR += xdelta3 SUBDIR += xfce4-appfinder SUBDIR += xfce4-weather-plugin SUBDIR += xfce4-wm-themes SUBDIR += xgboost SUBDIR += xiphos SUBDIR += xless SUBDIR += xosd SUBDIR += xplr SUBDIR += xtail SUBDIR += xtar SUBDIR += ytree SUBDIR += zine SUBDIR += zoneinfo .include diff --git a/misc/hermes-agent/Makefile b/misc/hermes-agent/Makefile new file mode 100644 index 000000000000..68032e3a5ce6 --- /dev/null +++ b/misc/hermes-agent/Makefile @@ -0,0 +1,183 @@ +PORTNAME= hermes-agent +PORTVERSION= 0.12.0 +CATEGORIES= misc python +MASTER_SITES+= LOCAL/olivier:webcache +DISTFILES+= ${PORTNAME}-web-offline-cache-${PORTVERSION}${EXTRACT_SUFX}:webcache + +MAINTAINER= olivier@FreeBSD.org +COMMENT= AI agent with built-in learning loop +WWW= https://github.com/NousResearch/hermes-agent + +LICENSE= MIT +LICENSE_FILE= ${WRKSRC}/LICENSE + +BUILD_DEPENDS= npm:www/npm +RUN_DEPENDS= ${PYTHON_PKGNAMEPREFIX}anthropic>=0.39.0:misc/py-anthropic@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}croniter>=6.0.0:sysutils/py-croniter@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}edge-tts>=7.2.7:audio/py-edge-tts@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}exa-py>=2.9.0:www/py-exa-py@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}fal-client>=0.13.1:misc/py-fal-client@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}fastapi>=0.104.0:www/py-fastapi@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}fire>=0.7.0:devel/py-fire@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}firecrawl-py>=4.16.0:www/py-firecrawl-py@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}httpx>=0.28.1:www/py-httpx@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}Jinja2>=3.1.5:devel/py-Jinja2@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}openai>=2.21.0:misc/py-openai@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}parallel-web>=0.4.2:www/py-parallel-web@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}prompt-toolkit>=3.0.52:devel/py-prompt-toolkit@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}pydantic2>=2.12.5:devel/py-pydantic2@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}pyjwt>=2.12.0:www/py-pyjwt@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}pysocks>0:net/py-pysocks@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}python-dotenv>=1.2.1:www/py-python-dotenv@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}pyyaml>=6.0.2:devel/py-pyyaml@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}requests>=2.33.0:www/py-requests@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}rich>=14.3.3:textproc/py-rich@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}socksio>0:net/py-socksio@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}tenacity>=9.1.4:devel/py-tenacity@${PY_FLAVOR} \ + ${PYTHON_PKGNAMEPREFIX}uvicorn>=0.24.0:www/py-uvicorn@${PY_FLAVOR} + +USES= python:3.11+,run shebangfix nodejs:lts,build +USE_GITHUB= yes +GH_ACCOUNT= NousResearch +GH_PROJECT= hermes-agent +GH_TAGNAME= v2026.4.30 + +USE_RC_SUBR= hermes_dashboard hermes_gateway + +SUB_FILES= pkg-message + +NO_ARCH= yes + +# Hermes is an application, not a Python library. Upstream's Dockerfile, +# Nix flake, and Homebrew formula all install it into a private directory +# (/opt/hermes, the Nix store, libexec/ respectively) rather than into +# site-packages, because the project ships top-level packages with generic +# names (tools, agent, gateway, plugins, ...) and bare modules (cli.py, +# utils.py, ...) that would collide with other Python packages. We follow +# the same convention: install the source tree under HERMES_LIBDIR and +# create thin wrapper scripts in ${PREFIX}/bin that inject HERMES_LIBDIR +# into sys.path before calling each entry point. +HERMES_LIBDIR= ${PREFIX}/lib/${PORTNAME} + +PLIST_SUB+= HERMES_LIBDIR=${HERMES_LIBDIR:S,^${PREFIX}/,,} + +# Web dashboard SPA (Vite/React) — upstream's release tarball does NOT ship +# a prebuilt web_dist/, only the source under web/. We bring our own npm +# offline mirror as a second distfile (LOCAL/:webcache) and run +# `npm ci --offline && npm run build` in do-build to produce +# hermes_cli/web_dist/, which the dashboard serves at runtime +# (web_server.py defaults to ${HERMES_LIBDIR}/hermes_cli/web_dist). +# +# How to (re)generate hermes-agent-web-offline-cache-${PORTVERSION}.tar.gz +# on every PORTVERSION bump (run on a connected host with npm 10+ installed): +# +# 1. Extract the upstream source tarball: +# tar xzf ${DISTDIR}/NousResearch-hermes-agent-${PORTVERSION}-${GH_TAGNAME}_GH0.tar.gz +# cd hermes-agent-*/web +# 2. Populate a fresh npm cache from web/package-lock.json: +# rm -rf /tmp/hermes-cache && mkdir -p /tmp/hermes-cache +# HOME=/tmp npm_config_cache=/tmp/hermes-cache \ +# npm ci --no-audit --no-fund --prefer-offline +# 3. Strip non-deterministic bits (logs, last-checked stamps): +# rm -rf /tmp/hermes-cache/_logs /tmp/hermes-cache/_update-notifier-last-checked +# 4. Repackage with a top-level dir whose name matches the distfile: +# mv /tmp/hermes-cache /tmp/${PORTNAME}-web-offline-cache-${PORTVERSION} +# cd /tmp && tar --no-acls --no-xattrs --no-fflags --uid=0 --gid=0 \ +# -czf ${PORTNAME}-web-offline-cache-${PORTVERSION}.tar.gz \ +# ${PORTNAME}-web-offline-cache-${PORTVERSION} +# 5. Upload to LOCAL/'s distcache directory and drop a copy +# into ${DISTDIR} so `make makesum` picks it up locally. +# 6. cd ${.CURDIR} && make makesum +# +# `npm ci --offline` in do-build refuses any registry call, so a missing +# entry in the mirror fails fast instead of silently going to the network. +# npm reads cacache content from ${npm_config_cache}/_cacache. Point +# npm_config_cache at the *parent* of the _cacache/ tree we shipped — if +# you point it at _cacache/ directly, npm appends _cacache/ a second time +# and silently sees an empty cache (Index entries: 0), then fails every +# install with ENOTCACHED. +WEB_CACHE_DIR= ${WRKDIR}/${PORTNAME}-web-offline-cache-${PORTVERSION} +WEB_NPM_ENV= HOME=${WRKDIR} \ + npm_config_cache=${WEB_CACHE_DIR} \ + npm_config_update_notifier=false \ + npm_config_audit=false \ + npm_config_fund=false + +# Python packages and bare modules that constitute the runtime app. +HERMES_PKGS= acp_adapter agent cron gateway hermes_cli plugins tools tui_gateway +HERMES_MODS= batch_runner.py cli.py hermes_constants.py hermes_logging.py \ + hermes_state.py hermes_time.py model_tools.py rl_cli.py \ + run_agent.py toolset_distributions.py toolsets.py \ + trajectory_compressor.py utils.py + +SHEBANG_FILES= ${HERMES_MODS} + +PORTDOCS= README.md SECURITY.md CONTRIBUTING.md AGENTS.md +OPTIONS_DEFINE= DOCS + +PLIST_FILES= "@(,,0755) bin/hermes" \ + "@(,,0755) bin/hermes-agent" \ + "@(,,0755) bin/hermes-acp" + +# Build the web dashboard SPA from the offline npm mirror. npm reads its +# package cache from npm_config_cache; --offline forbids any network call +# so a missing dep fails fast instead of silently going to the registry. +# The vite config writes the bundle to ../hermes_cli/web_dist (relative to +# web/), which is then picked up by do-install below. +do-build: + cd ${WRKSRC}/web && \ + ${SETENV} ${WEB_NPM_ENV} \ + npm ci --offline --no-audit --no-fund + cd ${WRKSRC}/web && \ + ${SETENV} ${WEB_NPM_ENV} \ + npm run build + +do-install: + ${MKDIR} ${STAGEDIR}${HERMES_LIBDIR} +.for d in ${HERMES_PKGS} + cd ${WRKSRC} && ${COPYTREE_SHARE} ${d} ${STAGEDIR}${HERMES_LIBDIR} \ + "! -name __pycache__ ! -name *.pyc" +.endfor +.for f in ${HERMES_MODS} + ${INSTALL_DATA} ${WRKSRC}/${f} ${STAGEDIR}${HERMES_LIBDIR} +.endfor + ${MKDIR} ${STAGEDIR}${PREFIX}/bin + ${SED} -e 's|%%HERMES_LIBDIR%%|${HERMES_LIBDIR}|g' \ + -e 's|%%PYTHON_CMD%%|${PYTHON_CMD}|g' \ + -e 's|%%ENTRY_MODULE%%|hermes_cli.main|g' \ + -e 's|%%ENTRY_FUNC%%|main|g' \ + ${FILESDIR}/wrapper.in > ${STAGEDIR}${PREFIX}/bin/hermes + ${SED} -e 's|%%HERMES_LIBDIR%%|${HERMES_LIBDIR}|g' \ + -e 's|%%PYTHON_CMD%%|${PYTHON_CMD}|g' \ + -e 's|%%ENTRY_MODULE%%|run_agent|g' \ + -e 's|%%ENTRY_FUNC%%|main|g' \ + ${FILESDIR}/wrapper.in > ${STAGEDIR}${PREFIX}/bin/hermes-agent + ${SED} -e 's|%%HERMES_LIBDIR%%|${HERMES_LIBDIR}|g' \ + -e 's|%%PYTHON_CMD%%|${PYTHON_CMD}|g' \ + -e 's|%%ENTRY_MODULE%%|acp_adapter.entry|g' \ + -e 's|%%ENTRY_FUNC%%|main|g' \ + ${FILESDIR}/wrapper.in > ${STAGEDIR}${PREFIX}/bin/hermes-acp + ${MKDIR} ${STAGEDIR}${DATADIR} + cd ${WRKSRC} && ${COPYTREE_SHARE} skills ${STAGEDIR}${DATADIR} + cd ${WRKSRC} && ${COPYTREE_SHARE} optional-skills ${STAGEDIR}${DATADIR} + +# Walk the staged HERMES_LIBDIR and DATADIR trees and append every file +# (and every directory we created) to the plist. This avoids hand- +# maintaining a 500-line pkg-plist for skill templates that change every +# release. +post-install: + @cd ${STAGEDIR}${PREFIX} && \ + ${FIND} ${HERMES_LIBDIR:S,^${PREFIX}/,,} ${DATADIR:S,^${PREFIX}/,,} \ + -type f >> ${TMPPLIST} + @cd ${STAGEDIR}${PREFIX} && \ + ${FIND} ${HERMES_LIBDIR:S,^${PREFIX}/,,} ${DATADIR:S,^${PREFIX}/,,} \ + -type d -mindepth 1 | ${SORT} -r | \ + ${SED} 's|^|@dir |' >> ${TMPPLIST} + +post-install-DOCS-on: + ${MKDIR} ${STAGEDIR}${DOCSDIR} +.for f in ${PORTDOCS} + ${INSTALL_DATA} ${WRKSRC}/${f} ${STAGEDIR}${DOCSDIR} +.endfor + +.include diff --git a/misc/hermes-agent/distinfo b/misc/hermes-agent/distinfo new file mode 100644 index 000000000000..9238c06174f8 --- /dev/null +++ b/misc/hermes-agent/distinfo @@ -0,0 +1,5 @@ +TIMESTAMP = 1778071003 +SHA256 (hermes-agent-web-offline-cache-0.12.0.tar.gz) = b176b6ce7de35e7720d6c3363911944cfb9eca2c8e4bd1af621207d45f0ad225 +SIZE (hermes-agent-web-offline-cache-0.12.0.tar.gz) = 50966122 +SHA256 (NousResearch-hermes-agent-0.12.0-v2026.4.30_GH0.tar.gz) = 3743db721cf6c93631f8446bdc8b77fd53e0c439ee8c42ec821ebfd6874c3949 +SIZE (NousResearch-hermes-agent-0.12.0-v2026.4.30_GH0.tar.gz) = 18882013 diff --git a/misc/hermes-agent/files/hermes_dashboard.in b/misc/hermes-agent/files/hermes_dashboard.in new file mode 100644 index 000000000000..bdf958b0b889 --- /dev/null +++ b/misc/hermes-agent/files/hermes_dashboard.in @@ -0,0 +1,89 @@ +#!/bin/sh + +# PROVIDE: hermes_dashboard +# REQUIRE: LOGIN NETWORKING +# KEYWORD: shutdown +# +# Add the following lines to /etc/rc.conf to enable hermes_dashboard: +# +# hermes_dashboard_enable="YES" +# hermes_dashboard_user="hermes" # REQUIRED: account whose ~/.hermes is used +# hermes_dashboard_host="127.0.0.1" # OPTIONAL: bind host (default 127.0.0.1) +# hermes_dashboard_port="9119" # OPTIONAL: bind port (default 9119) +# hermes_dashboard_profile="" # OPTIONAL: -p +# hermes_dashboard_args="" # OPTIONAL: extra args passed to `hermes dashboard` +# # e.g. "--insecure" (DANGEROUS — exposes API keys) +# # or "--tui" (in-browser chat) +# +# NOTE: do NOT use ${name}_flags for extra args. rc.subr reserves *_flags +# for the *command* (i.e. daemon(8)) and will inject them ahead of daemon's +# own options, which causes daemon to fail with "unrecognized option". +# Use hermes_dashboard_args instead — it is forwarded to `hermes dashboard`. +# +# WARNING: --insecure binds the dashboard to all interfaces and exposes +# the configured provider API keys on the network. Only use behind a +# trusted reverse proxy with auth. Default binding is 127.0.0.1 only. + +. /etc/rc.subr + +name="hermes_dashboard" +rcvar="hermes_dashboard_enable" + +load_rc_config $name + +: ${hermes_dashboard_enable:="NO"} +: ${hermes_dashboard_user:=""} +: ${hermes_dashboard_host:="127.0.0.1"} +: ${hermes_dashboard_port:="9119"} +: ${hermes_dashboard_profile:=""} +: ${hermes_dashboard_args:=""} + +# Back-compat: if someone set the legacy *_flags var, honor it but warn. +if [ -n "${hermes_dashboard_flags}" ] && [ -z "${hermes_dashboard_args}" ]; then + warn "hermes_dashboard_flags is deprecated (it collides with rc.subr); use hermes_dashboard_args" + hermes_dashboard_args="${hermes_dashboard_flags}" +fi +# Suppress rc.subr's automatic *_flags injection — we route extras via +# hermes_dashboard_args into the inner command instead. +hermes_dashboard_flags="" + +if [ -n "${hermes_dashboard_user}" ]; then + hermes_dashboard_home=$(getent passwd "${hermes_dashboard_user}" 2>/dev/null | cut -d: -f6) + if [ -z "${hermes_dashboard_home}" ]; then + hermes_dashboard_home=$(eval echo "~${hermes_dashboard_user}") + fi +else + hermes_dashboard_home="" +fi + +piddir="/var/run/${name}" +pidfile="${piddir}/${name}.pid" + +command="/usr/sbin/daemon" +# Note: do NOT pass `-u ${hermes_dashboard_user}` to daemon. rc.subr already +# drops privileges to ${name}_user via su(1) before exec, so adding -u here +# causes daemon to call initgroups() as a non-root user → EPERM and a tight +# restart loop with `-r`. +command_args="-f -r -P ${pidfile} -S -T ${name} \ + /usr/bin/env HOME=${hermes_dashboard_home} \ + %%PREFIX%%/bin/hermes ${hermes_dashboard_profile:+-p ${hermes_dashboard_profile}} \ + dashboard --no-open --host ${hermes_dashboard_host} --port ${hermes_dashboard_port} \ + ${hermes_dashboard_args}" + +required_files="%%PREFIX%%/bin/hermes" + +start_precmd="hermes_dashboard_prestart" +hermes_dashboard_prestart() +{ + if [ -z "${hermes_dashboard_user}" ]; then + err 1 "hermes_dashboard_user is not set in rc.conf — refusing to start" + fi + if [ -z "${hermes_dashboard_home}" ] || [ ! -d "${hermes_dashboard_home}" ]; then + err 1 "home directory for user '${hermes_dashboard_user}' not found" + fi + # piddir must be writable by the unprivileged user since daemon(8) + # drops privileges (-u) before writing the supervisor pidfile (-P). + install -d -m 0755 -o "${hermes_dashboard_user}" "${piddir}" +} + +run_rc_command "$1" diff --git a/misc/hermes-agent/files/hermes_gateway.in b/misc/hermes-agent/files/hermes_gateway.in new file mode 100644 index 000000000000..06d23d772091 --- /dev/null +++ b/misc/hermes-agent/files/hermes_gateway.in @@ -0,0 +1,101 @@ +#!/bin/sh + +# PROVIDE: hermes_gateway +# REQUIRE: LOGIN NETWORKING +# KEYWORD: shutdown +# +# Add the following lines to /etc/rc.conf to enable hermes_gateway: +# +# hermes_gateway_enable="YES" +# hermes_gateway_user="hermes" # REQUIRED: account whose ~/.hermes is used +# hermes_gateway_profile="" # OPTIONAL: -p for multi-profile setups +# hermes_gateway_args="" # OPTIONAL: extra args for `hermes gateway run` +# # e.g. "-v" for INFO logs, "-vv" for DEBUG +# +# NOTE: do NOT use ${name}_flags for extra args. rc.subr reserves *_flags +# for the *command* (i.e. daemon(8)) and will inject them ahead of daemon's +# own options, which causes daemon to fail with "invalid option". +# Use hermes_gateway_args instead — it is forwarded to `hermes gateway run`. +# +# Notes: +# * Hermes stores all state under $HOME/.hermes (config, sessions, logs, +# credentials). This script runs the gateway as ${hermes_gateway_user} +# so ~/.hermes resolves to that user's home directory. There is no +# sane "root" default — running as root would target /root/.hermes +# which is almost never what the operator wants. +# * The gateway logs to ${HOME}/.hermes/agent.log via Hermes' own logging. +# daemon(8)'s syslog redirection captures any stray stderr to +# /var/log/messages with tag "hermes_gateway". +# * Crash recovery: daemon(8) -r restarts on non-zero exit. Hermes' own +# drain-restart (exit code 75) is handled the same way. + +. /etc/rc.subr + +name="hermes_gateway" +rcvar="hermes_gateway_enable" + +load_rc_config $name + +: ${hermes_gateway_enable:="NO"} +: ${hermes_gateway_user:=""} +: ${hermes_gateway_profile:=""} +: ${hermes_gateway_args:=""} + +# Back-compat: if someone set the legacy *_flags var, honor it but warn. +if [ -n "${hermes_gateway_flags}" ] && [ -z "${hermes_gateway_args}" ]; then + warn "hermes_gateway_flags is deprecated (it collides with rc.subr); use hermes_gateway_args" + hermes_gateway_args="${hermes_gateway_flags}" +fi +# Suppress rc.subr's automatic *_flags injection — we route extras via +# hermes_gateway_args into the inner command instead. +hermes_gateway_flags="" + +# Resolve home directory of the configured user so ~/.hermes works. +if [ -n "${hermes_gateway_user}" ]; then + hermes_gateway_home=$(getent passwd "${hermes_gateway_user}" 2>/dev/null | cut -d: -f6) + if [ -z "${hermes_gateway_home}" ]; then + hermes_gateway_home=$(eval echo "~${hermes_gateway_user}") + fi +else + hermes_gateway_home="" +fi + +piddir="/var/run/${name}" +pidfile="${piddir}/${name}.pid" + +# We launch via daemon(8): +# -f fully detach +# -r restart on non-zero exit (handles Hermes' SIGUSR1 drain-restart, code 75) +# -P pid pidfile owned by daemon(8) for proper PID tracking + signaling +# -S send stray stderr to syslog +# -T tag syslog tag +# HOME=... so Hermes finds ~/.hermes for the right user +# +# Note: do NOT pass `-u ${hermes_gateway_user}` to daemon. rc.subr already +# drops privileges to ${name}_user via su(1) before exec, so adding -u here +# would cause daemon to call initgroups() as a non-root user → EPERM and a +# tight restart loop with `-r`. +command="/usr/sbin/daemon" +command_args="-f -r -P ${pidfile} -S -T ${name} \ + /usr/bin/env HOME=${hermes_gateway_home} \ + %%PREFIX%%/bin/hermes ${hermes_gateway_profile:+-p ${hermes_gateway_profile}} \ + gateway run --replace ${hermes_gateway_args}" + +required_files="%%PREFIX%%/bin/hermes" + +start_precmd="hermes_gateway_prestart" +hermes_gateway_prestart() +{ + if [ -z "${hermes_gateway_user}" ]; then + err 1 "hermes_gateway_user is not set in rc.conf — refusing to start" + fi + if [ -z "${hermes_gateway_home}" ] || [ ! -d "${hermes_gateway_home}" ]; then + err 1 "home directory for user '${hermes_gateway_user}' not found" + fi + # piddir must be writable by the unprivileged user since rc.subr drops + # privileges (via ${name}_user) before daemon(8) writes the supervisor + # pidfile (-P). Always (re-)assert ownership in case of stale state. + install -d -m 0755 -o "${hermes_gateway_user}" "${piddir}" +} + +run_rc_command "$1" diff --git a/misc/hermes-agent/files/patch-hermes__cli_gateway.py b/misc/hermes-agent/files/patch-hermes__cli_gateway.py new file mode 100644 index 000000000000..cc8791634d1a --- /dev/null +++ b/misc/hermes-agent/files/patch-hermes__cli_gateway.py @@ -0,0 +1,284 @@ +--- hermes_cli/gateway.py.orig 2026-05-06 08:29:55 UTC ++++ hermes_cli/gateway.py +@@ -733,7 +733,175 @@ def is_windows() -> bool: + return sys.platform == 'win32' + + ++def is_freebsd() -> bool: ++ return sys.platform.startswith('freebsd') ++ ++ + # ============================================================================= ++# FreeBSD rc.d service helpers ++# ============================================================================= ++# ++# On FreeBSD the rc.d scripts (hermes_gateway, hermes_dashboard) are shipped by ++# the FreeBSD port at /usr/local/etc/rc.d/. Hermes does NOT generate or write ++# them at runtime — there is no per-user scope on FreeBSD, only the system one, ++# and editing rc.d / rc.conf requires root. These helpers therefore: ++# * detect whether the rc script is present (port installed) ++# * shell out to service(8) for start/stop/restart/status ++# * shell out to sysrc(8) to flip hermes_gateway_enable=YES / -x in rc.conf ++# * print exact sudo commands when running as a non-root user ++# This mirrors the systemd_*/launchd_* command shape so the dispatcher in ++# gateway_command() can branch on platform without duplicating logic. ++ ++FREEBSD_RC_SCRIPT_NAME = "hermes_gateway" ++FREEBSD_RC_SCRIPT_PATH = Path("/usr/local/etc/rc.d") / FREEBSD_RC_SCRIPT_NAME ++FREEBSD_RC_VAR = "hermes_gateway_enable" ++ ++ ++def supports_freebsd_rc() -> bool: ++ """Return True when running on FreeBSD with the hermes_gateway rc.d script ++ installed by the port. We do NOT try to install the rc script ourselves — ++ that's the package manager's job.""" ++ if not is_freebsd(): ++ return False ++ if shutil.which("service") is None: ++ return False ++ return FREEBSD_RC_SCRIPT_PATH.exists() ++ ++ ++def _freebsd_is_root() -> bool: ++ try: ++ return os.geteuid() == 0 ++ except AttributeError: ++ return False ++ ++ ++def _freebsd_sudo_prefix() -> list[str]: ++ """Return [] if already root, ['sudo'] otherwise.""" ++ return [] if _freebsd_is_root() else ["sudo"] ++ ++ ++def _freebsd_run_or_print(cmd: list[str], *, action: str) -> bool: ++ """Run *cmd* (prepending sudo when needed). If sudo is required and ++ unavailable in batch mode, print the exact command for the user to run. ++ Returns True on success, False otherwise. *action* is a short verb used ++ for log messages (e.g. 'start', 'enable').""" ++ if _freebsd_is_root(): ++ try: ++ subprocess.run(cmd, check=True) ++ return True ++ except subprocess.CalledProcessError as e: ++ print(f"✗ Failed to {action} hermes_gateway: exit {e.returncode}") ++ return False ++ ++ if shutil.which("sudo") is None: ++ print(f" Run as root: {' '.join(cmd)}") ++ return False ++ ++ sudo_cmd = ["sudo"] + cmd ++ try: ++ subprocess.run(sudo_cmd, check=True) ++ return True ++ except subprocess.CalledProcessError as e: ++ print(f"✗ Failed to {action} hermes_gateway: exit {e.returncode}") ++ return False ++ ++ ++def freebsd_rc_install(force: bool = False, system: bool = False, run_as_user: str | None = None): ++ """Enable hermes_gateway in /etc/rc.conf and start it. ++ ++ The rc.d script itself is provided by the FreeBSD port; this function only ++ flips the rcvar and (re)starts the service. --system is accepted for ++ parity with the systemd dispatcher but is a no-op on FreeBSD (rc.d only ++ has system scope). --run-as-user is also a no-op: the rc script reads ++ hermes_gateway_user from rc.conf, set it there with sysrc. ++ """ ++ del force, system # noqa: F841 — accepted for dispatcher parity ++ ++ if not FREEBSD_RC_SCRIPT_PATH.exists(): ++ print(f"✗ {FREEBSD_RC_SCRIPT_PATH} not found.") ++ print(" The hermes_gateway rc.d script is shipped by the FreeBSD port.") ++ print(" Install with: sudo pkg install hermes-agent") ++ sys.exit(1) ++ ++ import getpass ++ target_user = run_as_user or getpass.getuser() ++ ++ print(f"Enabling {FREEBSD_RC_VAR}=YES in /etc/rc.conf...") ++ if not _freebsd_run_or_print( ++ ["sysrc", f"{FREEBSD_RC_VAR}=YES", f"hermes_gateway_user={target_user}"], ++ action="enable", ++ ): ++ return ++ print(f"Starting {FREEBSD_RC_SCRIPT_NAME}...") ++ _freebsd_run_or_print( ++ ["service", FREEBSD_RC_SCRIPT_NAME, "start"], ++ action="start", ++ ) ++ ++ ++def freebsd_rc_uninstall(system: bool = False): ++ """Stop hermes_gateway and remove its rcvar from /etc/rc.conf. Does NOT ++ delete the rc.d script itself — that belongs to the FreeBSD package.""" ++ del system # noqa: F841 ++ print(f"Stopping {FREEBSD_RC_SCRIPT_NAME}...") ++ _freebsd_run_or_print( ++ ["service", FREEBSD_RC_SCRIPT_NAME, "stop"], ++ action="stop", ++ ) ++ print(f"Removing {FREEBSD_RC_VAR} from /etc/rc.conf...") ++ _freebsd_run_or_print( ++ ["sysrc", "-x", FREEBSD_RC_VAR], ++ action="disable", ++ ) ++ print(f" (The rc.d script {FREEBSD_RC_SCRIPT_PATH} is owned by the package") ++ print(" manager — use 'pkg delete hermes-agent' to remove it.)") ++ ++ ++def freebsd_rc_start(system: bool = False): ++ del system ++ _freebsd_run_or_print( ++ ["service", FREEBSD_RC_SCRIPT_NAME, "start"], ++ action="start", ++ ) ++ ++ ++def freebsd_rc_stop(system: bool = False): ++ del system ++ _freebsd_run_or_print( ++ ["service", FREEBSD_RC_SCRIPT_NAME, "stop"], ++ action="stop", ++ ) ++ ++ ++def freebsd_rc_restart(system: bool = False): ++ del system ++ _freebsd_run_or_print( ++ ["service", FREEBSD_RC_SCRIPT_NAME, "restart"], ++ action="restart", ++ ) ++ ++ ++def freebsd_rc_status(deep: bool = False, system: bool = False, full: bool = False): ++ del deep, system, full ++ # `service X status` doesn't need root; run directly without sudo. ++ try: ++ result = subprocess.run( ++ ["service", FREEBSD_RC_SCRIPT_NAME, "status"], ++ check=False, ++ ) ++ if result.returncode != 0: ++ print() ++ print("To start the gateway:") ++ if _freebsd_is_root(): ++ print(" hermes gateway start") ++ else: ++ print(f" sudo service {FREEBSD_RC_SCRIPT_NAME} start") ++ print(f" sudo sysrc {FREEBSD_RC_VAR}=YES # start at boot") ++ except FileNotFoundError: ++ print("✗ service(8) not found — is this really FreeBSD?") ++ ++ ++# ============================================================================= + # Service Configuration + # ============================================================================= + +@@ -4083,6 +4251,8 @@ def _gateway_command_inner(args): + print_info(" Or use tmux/screen for persistence: tmux new -s hermes 'hermes gateway run'") + print() + systemd_install(force=force, system=system, run_as_user=run_as_user) ++ elif supports_freebsd_rc(): ++ freebsd_rc_install(force=force, system=system, run_as_user=run_as_user) + elif is_macos(): + launchd_install(force) + elif is_wsl(): +@@ -4119,6 +4289,8 @@ def _gateway_command_inner(args): + sys.exit(1) + if supports_systemd_services(): + systemd_uninstall(system=system) ++ elif supports_freebsd_rc(): ++ freebsd_rc_uninstall(system=system) + elif is_macos(): + launchd_uninstall() + elif is_container(): +@@ -4149,6 +4321,8 @@ def _gateway_command_inner(args): + sys.exit(1) + if supports_systemd_services(): + systemd_start(system=system) ++ elif supports_freebsd_rc(): ++ freebsd_rc_start(system=system) + elif is_macos(): + launchd_start() + elif is_wsl(): +@@ -4187,6 +4361,12 @@ def _gateway_command_inner(args): + service_available = True + except subprocess.CalledProcessError: + pass ++ elif supports_freebsd_rc(): ++ try: ++ freebsd_rc_stop(system=system) ++ service_available = True ++ except subprocess.CalledProcessError: ++ pass + elif is_macos() and get_launchd_plist_path().exists(): + try: + launchd_stop() +@@ -4208,6 +4388,12 @@ def _gateway_command_inner(args): + service_available = True + except subprocess.CalledProcessError: + pass ++ elif supports_freebsd_rc(): ++ try: ++ freebsd_rc_stop(system=system) ++ service_available = True ++ except subprocess.CalledProcessError: ++ pass + elif is_macos() and get_launchd_plist_path().exists(): + try: + launchd_stop() +@@ -4240,6 +4426,12 @@ def _gateway_command_inner(args): + service_stopped = True + except subprocess.CalledProcessError: + pass ++ elif supports_freebsd_rc(): ++ try: ++ freebsd_rc_stop(system=system) ++ service_stopped = True ++ except subprocess.CalledProcessError: ++ pass + elif is_macos() and get_launchd_plist_path().exists(): + try: + launchd_stop() +@@ -4256,12 +4448,14 @@ def _gateway_command_inner(args): + print("Starting gateway...") + if supports_systemd_services() and (get_systemd_unit_path(system=False).exists() or get_systemd_unit_path(system=True).exists()): + systemd_start(system=system) ++ elif supports_freebsd_rc(): ++ freebsd_rc_start(system=system) + elif is_macos() and get_launchd_plist_path().exists(): + launchd_start() + else: + run_gateway(verbose=0) + return +- ++ + if supports_systemd_services() and (get_systemd_unit_path(system=False).exists() or get_systemd_unit_path(system=True).exists()): + service_configured = True + try: +@@ -4269,6 +4463,13 @@ def _gateway_command_inner(args): + service_available = True + except subprocess.CalledProcessError: + pass ++ elif supports_freebsd_rc(): ++ service_configured = True ++ try: ++ freebsd_rc_restart(system=system) ++ service_available = True ++ except subprocess.CalledProcessError: ++ pass + elif is_macos() and get_launchd_plist_path().exists(): + service_configured = True + try: +@@ -4320,6 +4521,9 @@ def _gateway_command_inner(args): + # Check for service first + if supports_systemd_services() and (get_systemd_unit_path(system=False).exists() or get_systemd_unit_path(system=True).exists()): + systemd_status(deep, system=system, full=full) ++ _print_gateway_process_mismatch(snapshot) ++ elif supports_freebsd_rc(): ++ freebsd_rc_status(deep=deep, system=system, full=full) + _print_gateway_process_mismatch(snapshot) + elif is_macos() and get_launchd_plist_path().exists(): + launchd_status(deep) diff --git a/misc/hermes-agent/files/patch-hermes__cli_main.py b/misc/hermes-agent/files/patch-hermes__cli_main.py new file mode 100644 index 000000000000..557c100f3aff --- /dev/null +++ b/misc/hermes-agent/files/patch-hermes__cli_main.py @@ -0,0 +1,66 @@ +--- hermes_cli/main.py.orig 2026-05-06 08:29:55 UTC ++++ hermes_cli/main.py +@@ -8118,7 +8118,7 @@ def main(): + gateway_start.add_argument( + "--system", + action="store_true", +- help="Target the Linux system-level gateway service", ++ help="Target the system-level gateway service (Linux only; ignored on FreeBSD/macOS)", + ) + gateway_start.add_argument( + "--all", +@@ -8131,7 +8131,7 @@ def main(): + gateway_stop.add_argument( + "--system", + action="store_true", +- help="Target the Linux system-level gateway service", ++ help="Target the system-level gateway service (Linux only; ignored on FreeBSD/macOS)", + ) + gateway_stop.add_argument( + "--all", +@@ -8146,7 +8146,7 @@ def main(): + gateway_restart.add_argument( + "--system", + action="store_true", +- help="Target the Linux system-level gateway service", ++ help="Target the system-level gateway service (Linux only; ignored on FreeBSD/macOS)", + ) + gateway_restart.add_argument( + "--all", +@@ -8166,23 +8166,23 @@ def main(): + gateway_status.add_argument( + "--system", + action="store_true", +- help="Target the Linux system-level gateway service", ++ help="Target the system-level gateway service (Linux only; ignored on FreeBSD/macOS)", + ) + + # gateway install + gateway_install = gateway_subparsers.add_parser( +- "install", help="Install gateway as a systemd/launchd background service" ++ "install", help="Install gateway as a systemd/launchd/rc.d background service" + ) + gateway_install.add_argument("--force", action="store_true", help="Force reinstall") + gateway_install.add_argument( + "--system", + action="store_true", +- help="Install as a Linux system-level service (starts at boot)", ++ help="Install as a system-level service (Linux only; FreeBSD rc.d is always system-scoped)", + ) + gateway_install.add_argument( + "--run-as-user", + dest="run_as_user", +- help="User account the Linux system service should run as", ++ help="User account the service should run as (Linux systemd / FreeBSD rc.d)", + ) + + # gateway uninstall +@@ -8192,7 +8192,7 @@ def main(): + gateway_uninstall.add_argument( + "--system", + action="store_true", +- help="Target the Linux system-level gateway service", ++ help="Target the system-level gateway service (Linux only; ignored on FreeBSD/macOS)", + ) + + # gateway setup diff --git a/misc/hermes-agent/files/patch-hermes__cli_setup.py b/misc/hermes-agent/files/patch-hermes__cli_setup.py new file mode 100644 index 000000000000..b3543dae6ca5 --- /dev/null +++ b/misc/hermes-agent/files/patch-hermes__cli_setup.py @@ -0,0 +1,95 @@ +--- hermes_cli/setup.py.orig 2026-05-06 08:29:55 UTC ++++ hermes_cli/setup.py +@@ -2409,11 +2409,13 @@ def setup_gateway(config: dict): + + _is_linux = _platform.system() == "Linux" + _is_macos = _platform.system() == "Darwin" ++ _is_freebsd = _platform.system() == "FreeBSD" + + from hermes_cli.gateway import ( + _is_service_installed, + _is_service_running, + supports_systemd_services, ++ supports_freebsd_rc, + has_conflicting_systemd_units, + has_legacy_hermes_units, + install_linux_gateway_from_setup, +@@ -2424,13 +2426,17 @@ def setup_gateway(config: dict): + launchd_install, + launchd_start, + launchd_restart, ++ freebsd_rc_install, ++ freebsd_rc_start, ++ freebsd_rc_restart, + UserSystemdUnavailableError, + ) + + service_installed = _is_service_installed() + service_running = _is_service_running() + supports_systemd = supports_systemd_services() +- supports_service_manager = supports_systemd or _is_macos ++ supports_freebsd = supports_freebsd_rc() ++ supports_service_manager = supports_systemd or _is_macos or supports_freebsd + + print() + if supports_systemd and has_conflicting_systemd_units(): +@@ -2446,6 +2452,8 @@ def setup_gateway(config: dict): + try: + if supports_systemd: + systemd_restart() ++ elif supports_freebsd: ++ freebsd_rc_restart() + elif _is_macos: + launchd_restart() + except UserSystemdUnavailableError as e: +@@ -2459,6 +2467,8 @@ def setup_gateway(config: dict): + try: + if supports_systemd: + systemd_start() ++ elif supports_freebsd: ++ freebsd_rc_start() + elif _is_macos: + launchd_start() + except UserSystemdUnavailableError as e: +@@ -2468,7 +2478,12 @@ def setup_gateway(config: dict): + except Exception as e: + print_error(f" Start failed: {e}") + elif supports_service_manager: +- svc_name = "systemd" if supports_systemd else "launchd" ++ if supports_systemd: ++ svc_name = "systemd" ++ elif supports_freebsd: ++ svc_name = "FreeBSD rc.d" ++ else: ++ svc_name = "launchd" + if prompt_yes_no( + f" Install the gateway as a {svc_name} service? (runs in background, starts on boot)", + True, +@@ -2478,6 +2493,9 @@ def setup_gateway(config: dict): + did_install = False + if supports_systemd: + installed_scope, did_install = install_linux_gateway_from_setup(force=False) ++ elif supports_freebsd: ++ freebsd_rc_install(force=False) ++ did_install = True + else: + launchd_install(force=False) + did_install = True +@@ -2486,6 +2504,8 @@ def setup_gateway(config: dict): + try: + if supports_systemd: + systemd_start(system=installed_scope == "system") ++ elif supports_freebsd: ++ freebsd_rc_start() + elif _is_macos: + launchd_start() + except UserSystemdUnavailableError as e: +@@ -2501,6 +2521,8 @@ def setup_gateway(config: dict): + print_info(" You can install later: hermes gateway install") + if supports_systemd: + print_info(" Or as a boot-time service: sudo hermes gateway install --system") ++ elif supports_freebsd: ++ print_info(" (FreeBSD rc.d install runs sysrc + service start with sudo.)") + print_info(" Or run in foreground: hermes gateway") + else: + from hermes_constants import is_container diff --git a/misc/hermes-agent/files/patch-hermes__cli_uninstall.py b/misc/hermes-agent/files/patch-hermes__cli_uninstall.py new file mode 100644 index 000000000000..c971198c7ae1 --- /dev/null +++ b/misc/hermes-agent/files/patch-hermes__cli_uninstall.py @@ -0,0 +1,35 @@ +--- hermes_cli/uninstall.py.orig 2026-05-06 08:29:55 UTC ++++ hermes_cli/uninstall.py +@@ -201,6 +201,32 @@ def uninstall_gateway_service(): + except Exception as e: + log_warn(f"Could not remove launchd gateway service: {e}") + ++ # 4. FreeBSD: stop the rc.d service and remove its rcvar from rc.conf. ++ # The rc script itself (/usr/local/etc/rc.d/hermes_gateway) is owned ++ # by the FreeBSD package — `pkg delete hermes-agent` removes it. ++ elif system == "FreeBSD": ++ try: ++ from hermes_cli.gateway import ( ++ supports_freebsd_rc, ++ FREEBSD_RC_SCRIPT_NAME, ++ FREEBSD_RC_VAR, ++ ) ++ if supports_freebsd_rc(): ++ sudo = [] if os.geteuid() == 0 else (["sudo"] if shutil.which("sudo") else None) ++ if sudo is None: ++ log_warn(f" Need root to disable {FREEBSD_RC_SCRIPT_NAME}. " ++ f"Run: sudo service {FREEBSD_RC_SCRIPT_NAME} stop && " ++ f"sudo sysrc -x {FREEBSD_RC_VAR}") ++ else: ++ subprocess.run(sudo + ["service", FREEBSD_RC_SCRIPT_NAME, "stop"], ++ capture_output=True, check=False) ++ subprocess.run(sudo + ["sysrc", "-x", FREEBSD_RC_VAR], ++ capture_output=True, check=False) ++ log_success(f"Stopped and disabled {FREEBSD_RC_SCRIPT_NAME}") ++ stopped_something = True ++ except Exception as e: ++ log_warn(f"Could not disable FreeBSD gateway service: {e}") ++ + return stopped_something + + diff --git a/misc/hermes-agent/files/pkg-message.in b/misc/hermes-agent/files/pkg-message.in new file mode 100644 index 000000000000..2ccb9172ae10 --- /dev/null +++ b/misc/hermes-agent/files/pkg-message.in @@ -0,0 +1,25 @@ +[ +{ type: install + message: <