diff --git a/lib/higgsfield-skills.sh b/lib/higgsfield-skills.sh new file mode 100644 index 0000000..51de21c --- /dev/null +++ b/lib/higgsfield-skills.sh @@ -0,0 +1,79 @@ +#!/usr/bin/env bash +# ============================================================ +# lib/higgsfield-skills.sh — Higgsfield skill pack sync + CLI probes +# +# Sourced by install-plugins.sh (Step 8.6), update-all.sh (7.3b) and +# doctor.sh. The pack is machine-owned: cloned from upstream and moved +# into skills-external/higgsfield-* (gitignored), then linked on demand by +# lib/toggle-external.sh. It is listed in neither link.sh nor any profile: +# either would re-enable a parked pack on every run (BDR-093). +# ============================================================ + +# Upstream skills repo, single source for both installers. An env value +# wins so the hermetic suite can point it at a local fixture repo. +HIGGSFIELD_SKILLS_URL="${HIGGSFIELD_SKILLS_URL:-\ +https://github.com/higgsfield-ai/skills.git}" + +# _higgsfield_adopt +# Move every real higgsfield-*/ directory of the clone that holds a SKILL.md +# over its copy in ; prints how many landed. A symlinked entry is +# skipped: only upstream's own directories are adopted. A skill counts only +# once its move succeeded. +_higgsfield_adopt() { + local clone="$1" dest="$2" dir name count=0 + for dir in "$clone"/higgsfield-*/; do + dir="${dir%/}" + { [ -f "$dir/SKILL.md" ] && [ ! -L "$dir" ]; } || continue + name="$(basename "$dir")" + rm -rf "${dest:?}/${name:?}" && mv "$dir" "$dest/$name" \ + && count=$((count + 1)) + done + echo "$count" +} + +# higgsfield_sync_skills +# Clone upstream into a stage and replace each +# /skills-external/higgsfield-* with the fresh copy; upstream's own +# machinery (setup, scripts/, plugin manifests, .git) stays in the stage. +# The stage sits next to the destination, on the same filesystem, so each +# replacement is a rename. Prints the number of skills synced. Returns 1, +# existing copies untouched, when the clone fails or upstream holds no +# higgsfield-*/SKILL.md. A parked skill (skills-disabled/, a symlink +# to the source path) stays parked. Known limit: a skill that upstream +# removes or renames keeps its last local copy. +higgsfield_sync_skills() { + local dest="$1/skills-external" stage count=0 + mkdir -p "$dest" || return 1 + stage="$(mktemp -d "$dest/.higgsfield-stage.XXXXXX")" || return 1 + # No credential prompt of any kind: a private or deleted upstream must + # fail at once, not wait on a terminal, an askpass program (an editor's + # terminal exports one) or a credential helper. + if GIT_TERMINAL_PROMPT=0 GIT_ASKPASS='' SSH_ASKPASS='' \ + git -c credential.helper= -c core.askPass= clone --quiet --depth 1 \ + "$HIGGSFIELD_SKILLS_URL" "$stage/src" /dev/null 2>&1; then + count="$(_higgsfield_adopt "$stage/src" "$dest")" + fi + rm -rf "${stage:?}" + echo "$count" + [ "$count" -gt 0 ] +} + +# _higgsfield_probe +# Run `higgsfield ` silently, 15 s at most when `timeout` exists. The +# CLI is closed source: a probe must never hang an installer, and what it +# prints (a token, for `auth token`) must never reach a terminal or a log. +_higgsfield_probe() { + if command -v timeout >/dev/null 2>&1; then + timeout 15 higgsfield "$@" /dev/null 2>&1 + else + higgsfield "$@" /dev/null 2>&1 + fi +} + +# higgsfield_cli_ok — 0 when the binary answers. `command -v` alone only +# proves the npm shim: the binary is vendored by a postinstall script that +# npm may hold back, on a first install or on any later update. +higgsfield_cli_ok() { _higgsfield_probe version; } + +# higgsfield_signed_in — 0 when the CLI holds a session. +higgsfield_signed_in() { _higgsfield_probe auth token; } diff --git a/lib/tests/higgsfield.test.sh b/lib/tests/higgsfield.test.sh new file mode 100644 index 0000000..813e660 --- /dev/null +++ b/lib/tests/higgsfield.test.sh @@ -0,0 +1,168 @@ +#!/usr/bin/env bash +# lib/tests/higgsfield.test.sh — hermetic suite for the Higgsfield pack. +# sync lib/higgsfield-skills.sh against a local git repo shaped like +# upstream (no network), and its CLI probes against a fake CLI +# toggle lib/toggle-external.sh `higgsfield` / `higgsfield-websites` +# against a fixture tree, fake CLIs first on PATH +# wiring static locks on the installers (order, off by default) +# Each named case prints one `PASS ` or `FAIL :
` line. +set -u +ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +pass=0; fail=0; errs="" + +# expect