@zerwiz/ymir 0.1.4 → 0.1.7
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.
- package/.agents/README.md +291 -0
- package/.agents/agents/bragi-marketer.md +111 -0
- package/.agents/agents/brokk.md +352 -0
- package/.agents/agents/forseti-reviewer.md +93 -0
- package/.agents/agents/frigg-consent.md +77 -0
- package/.agents/agents/groa-update.md +77 -0
- package/.agents/agents/hnoss-designer.md +69 -0
- package/.agents/agents/huginn-researcher.md +99 -0
- package/.agents/agents/jord-projects.md +78 -0
- package/.agents/agents/kvasir-scout.md +91 -0
- package/.agents/agents/mimir-planner.md +92 -0
- package/.agents/agents/muninn-stow.md +77 -0
- package/.agents/agents/saga-bearings.md +78 -0
- package/.agents/agents/sindri-developer.md +114 -0
- package/.agents/agents/snotra-documenter.md +105 -0
- package/.agents/agents/syn-recovery.md +78 -0
- package/.agents/agents/tyr-check.md +78 -0
- package/.agents/agents/urdh-hold.md +77 -0
- package/.agents/agents/volundr-smith.md +80 -0
- package/.agents/agents/vor-diagnostics.md +77 -0
- package/.agents/assets/agents/naming.md +50 -0
- package/.agents/assets/agents/registry.md +93 -0
- package/.agents/assets/agents/runtime.md +36 -0
- package/.agents/assets/agents/toon-tasks-cli.md +42 -0
- package/.agents/assets/templates/company_entity.template.md +37 -0
- package/.agents/backend/README.md +74 -0
- package/.agents/backend/backends/cmux.sh +649 -0
- package/.agents/backend/backends/herdr-eventwait.py +157 -0
- package/.agents/backend/backends/herdr-workspace-move.py +114 -0
- package/.agents/backend/backends/herdr.sh +3386 -0
- package/.agents/backend/backends/orca.sh +290 -0
- package/.agents/backend/backends/tmux.sh +357 -0
- package/.agents/backend/backends/zellij.sh +700 -0
- package/.agents/backend/fm-afk-launch.sh +654 -0
- package/.agents/backend/fm-afk-return.sh +241 -0
- package/.agents/backend/fm-afk-start.sh +172 -0
- package/.agents/backend/fm-arm-command-policy.mjs +960 -0
- package/.agents/backend/fm-arm-pretool-check.sh +200 -0
- package/.agents/backend/fm-backend-hometag-lib.sh +52 -0
- package/.agents/backend/fm-backend.sh +981 -0
- package/.agents/backend/fm-backlog-handoff.sh +909 -0
- package/.agents/backend/fm-backlog-receive.sh +187 -0
- package/.agents/backend/fm-backlog-transition-lib.sh +779 -0
- package/.agents/backend/fm-bearings-board.sh +204 -0
- package/.agents/backend/fm-bearings-snapshot.sh +546 -0
- package/.agents/backend/fm-bootstrap.sh +1568 -0
- package/.agents/backend/fm-branch-outcome.sh +203 -0
- package/.agents/backend/fm-branch-prompt.sh +101 -0
- package/.agents/backend/fm-brief.sh +452 -0
- package/.agents/backend/fm-busy-event.sh +234 -0
- package/.agents/backend/fm-busy-lib.sh +986 -0
- package/.agents/backend/fm-captain-hold.sh +1001 -0
- package/.agents/backend/fm-cd-command-policy.mjs +153 -0
- package/.agents/backend/fm-cd-pretool-check.sh +190 -0
- package/.agents/backend/fm-check-lib.sh +74 -0
- package/.agents/backend/fm-check-register.sh +42 -0
- package/.agents/backend/fm-check-unregister.sh +52 -0
- package/.agents/backend/fm-classify-lib.sh +1770 -0
- package/.agents/backend/fm-claude-stop-autoarm.sh +334 -0
- package/.agents/backend/fm-composer-lib.sh +1416 -0
- package/.agents/backend/fm-config-inherit-lib.sh +1202 -0
- package/.agents/backend/fm-config-push.sh +254 -0
- package/.agents/backend/fm-control-lib.sh +251 -0
- package/.agents/backend/fm-control.sh +878 -0
- package/.agents/backend/fm-crew-state.sh +625 -0
- package/.agents/backend/fm-cursor-lib.sh +243 -0
- package/.agents/backend/fm-decision-hold.sh +232 -0
- package/.agents/backend/fm-doc-audience-check.sh +269 -0
- package/.agents/backend/fm-dod-lib.sh +67 -0
- package/.agents/backend/fm-ensure-agents-md.sh +253 -0
- package/.agents/backend/fm-extension-launch-barrier.mjs +129 -0
- package/.agents/backend/fm-extension.mjs +2577 -0
- package/.agents/backend/fm-extension.sh +16 -0
- package/.agents/backend/fm-ff-lib.sh +420 -0
- package/.agents/backend/fm-fleet-snapshot.sh +1463 -0
- package/.agents/backend/fm-fleet-sync.sh +458 -0
- package/.agents/backend/fm-fleet-view.sh +96 -0
- package/.agents/backend/fm-gate-refuse-lib.sh +102 -0
- package/.agents/backend/fm-guard.sh +242 -0
- package/.agents/backend/fm-harness.sh +195 -0
- package/.agents/backend/fm-herdr-ci-cleanup.sh +115 -0
- package/.agents/backend/fm-herdr-lab.sh +346 -0
- package/.agents/backend/fm-herdr-session-cleanup.sh +337 -0
- package/.agents/backend/fm-home-seed.sh +984 -0
- package/.agents/backend/fm-home-summary-refresh.sh +255 -0
- package/.agents/backend/fm-hook-host-lib.sh +36 -0
- package/.agents/backend/fm-inactive-reconcile.sh +531 -0
- package/.agents/backend/fm-inbox.sh +399 -0
- package/.agents/backend/fm-install-actionlint.sh +84 -0
- package/.agents/backend/fm-install-herdr.sh +96 -0
- package/.agents/backend/fm-install-shellcheck.sh +84 -0
- package/.agents/backend/fm-install-treehouse.sh +95 -0
- package/.agents/backend/fm-kimi-turnend-hook.sh +276 -0
- package/.agents/backend/fm-lease-lib.sh +218 -0
- package/.agents/backend/fm-lease.sh +189 -0
- package/.agents/backend/fm-line-cap-lib.sh +51 -0
- package/.agents/backend/fm-lint-workflows.sh +137 -0
- package/.agents/backend/fm-lint.sh +591 -0
- package/.agents/backend/fm-lock-lib.sh +104 -0
- package/.agents/backend/fm-lock.sh +107 -0
- package/.agents/backend/fm-marker-lib.sh +12 -0
- package/.agents/backend/fm-merge-local.sh +74 -0
- package/.agents/backend/fm-merge-outcome-lib.sh +138 -0
- package/.agents/backend/fm-nm-run-lib.sh +124 -0
- package/.agents/backend/fm-on.sh +125 -0
- package/.agents/backend/fm-operational-input.sh +253 -0
- package/.agents/backend/fm-peek.sh +46 -0
- package/.agents/backend/fm-pending-reply-lib.sh +1409 -0
- package/.agents/backend/fm-pr-check.sh +135 -0
- package/.agents/backend/fm-pr-lib.sh +1016 -0
- package/.agents/backend/fm-pr-merge.sh +707 -0
- package/.agents/backend/fm-pr-poll.sh +110 -0
- package/.agents/backend/fm-primary-scope-lib.sh +33 -0
- package/.agents/backend/fm-procevent-extension-capture.pl +259 -0
- package/.agents/backend/fm-procevent-lavish.sh +628 -0
- package/.agents/backend/fm-procevent-lib.sh +892 -0
- package/.agents/backend/fm-procevent-quota.sh +290 -0
- package/.agents/backend/fm-procevent-remote-reply.sh +577 -0
- package/.agents/backend/fm-procevent-when.sh +504 -0
- package/.agents/backend/fm-procevent.sh +1466 -0
- package/.agents/backend/fm-project-mode.sh +92 -0
- package/.agents/backend/fm-project-origin-lib.sh +180 -0
- package/.agents/backend/fm-promote.sh +282 -0
- package/.agents/backend/fm-public-followup-emit.sh +260 -0
- package/.agents/backend/fm-public-followup-lib.sh +398 -0
- package/.agents/backend/fm-public-followup.sh +1330 -0
- package/.agents/backend/fm-push-transition-lib.sh +168 -0
- package/.agents/backend/fm-quota-axi-lib.sh +93 -0
- package/.agents/backend/fm-quota-choose.sh +384 -0
- package/.agents/backend/fm-remote-delta-read.sh +189 -0
- package/.agents/backend/fm-remote-doctor.sh +800 -0
- package/.agents/backend/fm-remote-entrypoint.sh +193 -0
- package/.agents/backend/fm-remote-file.sh +234 -0
- package/.agents/backend/fm-remote-home-provision.sh +259 -0
- package/.agents/backend/fm-remote-home-seed.sh +259 -0
- package/.agents/backend/fm-remote-inherit-push.sh +90 -0
- package/.agents/backend/fm-remote-inherit.sh +179 -0
- package/.agents/backend/fm-remote-job-lib.sh +1241 -0
- package/.agents/backend/fm-remote-job-reap-orphans.sh +137 -0
- package/.agents/backend/fm-remote-job-worker.sh +1124 -0
- package/.agents/backend/fm-remote-readiness-lib.sh +44 -0
- package/.agents/backend/fm-remote-secondmate-control.sh +340 -0
- package/.agents/backend/fm-review-diff.sh +158 -0
- package/.agents/backend/fm-secondmate-charter-lib.sh +44 -0
- package/.agents/backend/fm-secondmate-nudge-lib.sh +68 -0
- package/.agents/backend/fm-secondmate-parent-lib.sh +70 -0
- package/.agents/backend/fm-secondmate-reconcile.sh +371 -0
- package/.agents/backend/fm-secondmate-registry-lib.sh +311 -0
- package/.agents/backend/fm-secondmate-report.sh +88 -0
- package/.agents/backend/fm-send.sh +1059 -0
- package/.agents/backend/fm-session-lock-lib.sh +176 -0
- package/.agents/backend/fm-session-start.sh +970 -0
- package/.agents/backend/fm-sessionstart-cursor.sh +40 -0
- package/.agents/backend/fm-sessionstart-nudge.sh +45 -0
- package/.agents/backend/fm-sessionstart-run.sh +145 -0
- package/.agents/backend/fm-spawn.sh +3153 -0
- package/.agents/backend/fm-startup-memory-budget-lib.sh +224 -0
- package/.agents/backend/fm-startup-memory-budget.sh +94 -0
- package/.agents/backend/fm-startup-network.sh +642 -0
- package/.agents/backend/fm-stow-cascade.sh +251 -0
- package/.agents/backend/fm-subagent-pretool-check.sh +207 -0
- package/.agents/backend/fm-supervise-daemon.sh +1732 -0
- package/.agents/backend/fm-supervision-instructions.sh +215 -0
- package/.agents/backend/fm-supervision-lib.sh +90 -0
- package/.agents/backend/fm-supervisor-target-lib.sh +78 -0
- package/.agents/backend/fm-tangle-lib.sh +53 -0
- package/.agents/backend/fm-task-inbox-lib.sh +402 -0
- package/.agents/backend/fm-tasks-axi-lib.sh +123 -0
- package/.agents/backend/fm-teardown.sh +2922 -0
- package/.agents/backend/fm-test-isolation-proof.sh +588 -0
- package/.agents/backend/fm-test-run.sh +2191 -0
- package/.agents/backend/fm-timeout-lib.sh +141 -0
- package/.agents/backend/fm-timing-lib.sh +179 -0
- package/.agents/backend/fm-tmux-lib.sh +291 -0
- package/.agents/backend/fm-tool-update-check.sh +898 -0
- package/.agents/backend/fm-trace-context-lib.sh +227 -0
- package/.agents/backend/fm-transition-lib.sh +103 -0
- package/.agents/backend/fm-turnend-guard-cursor.sh +391 -0
- package/.agents/backend/fm-turnend-guard-grok.sh +90 -0
- package/.agents/backend/fm-turnend-guard.sh +432 -0
- package/.agents/backend/fm-update.sh +110 -0
- package/.agents/backend/fm-vendor-auth-probe.sh +190 -0
- package/.agents/backend/fm-voice-client.py +1373 -0
- package/.agents/backend/fm-voice-relay.py +1256 -0
- package/.agents/backend/fm-wake-drain.sh +592 -0
- package/.agents/backend/fm-wake-grant.sh +127 -0
- package/.agents/backend/fm-wake-lib.sh +1849 -0
- package/.agents/backend/fm-watch-arm.sh +602 -0
- package/.agents/backend/fm-watch-checkpoint.sh +109 -0
- package/.agents/backend/fm-watch.sh +1962 -0
- package/.agents/backend/fm-x-dismiss.sh +116 -0
- package/.agents/backend/fm-x-followup.sh +286 -0
- package/.agents/backend/fm-x-lib.sh +1000 -0
- package/.agents/backend/fm-x-link.sh +234 -0
- package/.agents/backend/fm-x-poll.sh +185 -0
- package/.agents/backend/fm-x-reply.sh +385 -0
- package/.agents/backend/fm_voice_frame.py +166 -0
- package/.agents/backend/fm_voice_records.py +574 -0
- package/.agents/backend/model-bridge.py +360 -0
- package/.agents/backend/opencode-go-bridge.py +30 -0
- package/.agents/bus/README.md +12 -0
- package/.agents/bus/messages.json +1 -0
- package/.agents/config/agents.machine.example.yaml +16 -0
- package/.agents/config/agents.yaml.example +72 -0
- package/.agents/config/crew-dispatch.json +22 -0
- package/.agents/config/cron.yaml +11 -0
- package/.agents/config/eindri-dispatch.json +67 -0
- package/.agents/config/eindri-harness +1 -0
- package/.agents/config/startup-memory-budget +1 -0
- package/.agents/filebrowser/.filebrowser.json +6 -0
- package/.agents/filebrowser/README.md +11 -0
- package/.agents/gateway/README.md +12 -0
- package/.agents/github/README.md +15 -0
- package/.agents/github/webhooks/.gitkeep +0 -0
- package/.agents/github/workflows/.gitkeep +0 -0
- package/.agents/harness/opencode/plugins/lib/rodd-operational-input.js +37 -0
- package/.agents/harness/opencode/plugins/package.json +4 -0
- package/.agents/harness/opencode/plugins/saga-sessionstart.js +77 -0
- package/.agents/harness/opencode/plugins/syn-cd-check.js +61 -0
- package/.agents/harness/opencode/plugins/syn-pretool-check.js +68 -0
- package/.agents/harness/opencode/plugins/syn-turnend-guard.js +105 -0
- package/.agents/harness/opencode/plugins/syn-watch-arm.js +521 -0
- package/.agents/memory/README.md +46 -0
- package/.agents/memory/runes_audit.md +5 -0
- package/.agents/migrations/0001-hodd-layout.sh +38 -0
- package/.agents/migrations/0002-a2a-mcp.sh +27 -0
- package/.agents/migrations/0003-private-data-separation.sh +121 -0
- package/.agents/migrations/0004-hoard-and-realms.sh +104 -0
- package/.agents/migrations/0005-roots-out-of-tree.sh +158 -0
- package/.agents/sandbox/Dockerfile.utgard +19 -0
- package/.agents/sandbox/README.md +14 -0
- package/.agents/sandbox/utgard.config.json +14 -0
- package/.agents/skills/README.md +59 -0
- package/.agents/skills/bragi-marketing/README.md +3 -0
- package/.agents/skills/bragi-marketing/SKILL.md +82 -0
- package/.agents/skills/eindri-homes/README.md +3 -0
- package/.agents/skills/eindri-homes/SKILL.md +291 -0
- package/.agents/skills/eindri-homes/assets/control-plane.md +122 -0
- package/.agents/skills/eindri-homes/assets/dispatch.md +80 -0
- package/.agents/skills/frigg-consent/README.md +3 -0
- package/.agents/skills/frigg-consent/SKILL.md +56 -0
- package/.agents/skills/galdr-ymirsystem/README.md +3 -0
- package/.agents/skills/galdr-ymirsystem/SKILL.md +159 -0
- package/.agents/skills/galdr-ymirsystem/assets/README.md +74 -0
- package/.agents/skills/galdr-ymirsystem/assets/brokk-distro-runtime.md +390 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-method.md +113 -0
- package/.agents/skills/galdr-ymirsystem/assets/build-tool-categories.md +178 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-orchestration.md +518 -0
- package/.agents/skills/galdr-ymirsystem/assets/eindri-profiles.md +136 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/README.md +736 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/claude-code.md +197 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/codex.md +191 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/cursor.md +193 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/opencode.md +262 -0
- package/.agents/skills/galdr-ymirsystem/assets/harness-integration/pi.md +295 -0
- package/.agents/skills/galdr-ymirsystem/assets/hlidskjalf-ui.md +539 -0
- package/.agents/skills/galdr-ymirsystem/assets/installation.md +763 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models/HOST-DATA.example.md +72 -0
- package/.agents/skills/galdr-ymirsystem/assets/local-models.md +274 -0
- package/.agents/skills/galdr-ymirsystem/assets/memory-well.md +163 -0
- package/.agents/skills/galdr-ymirsystem/assets/nornir-jobs.md +415 -0
- package/.agents/skills/galdr-ymirsystem/assets/norse-naming.md +325 -0
- package/.agents/skills/galdr-ymirsystem/assets/odrerir-hall.md +133 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/einherjar-spawn.schema.json +81 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/herdr-profile.toml +75 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/pi-profile.yml +101 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot/supervision-tree.yml +163 -0
- package/.agents/skills/galdr-ymirsystem/assets/pi-boot-guide.md +58 -0
- package/.agents/skills/galdr-ymirsystem/assets/porting-upstream-to-norse.md +301 -0
- package/.agents/skills/galdr-ymirsystem/assets/principles.md +385 -0
- package/.agents/skills/galdr-ymirsystem/assets/reference-adoption.md +99 -0
- package/.agents/skills/galdr-ymirsystem/assets/registry.md +379 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-compliance.md +463 -0
- package/.agents/skills/galdr-ymirsystem/assets/runtime-components.md +553 -0
- package/.agents/skills/galdr-ymirsystem/assets/smidja.md +180 -0
- package/.agents/skills/galdr-ymirsystem/schemas/toon-schemas.md +190 -0
- package/.agents/skills/galdr-ymirsystem/scripts/README.md +45 -0
- package/.agents/skills/galdr-ymirsystem/scripts/bench-one.sh +248 -0
- package/.agents/skills/galdr-ymirsystem/scripts/compliance-check.sh +503 -0
- package/.agents/skills/galdr-ymirsystem/scripts/gpu-sample.sh +43 -0
- package/.agents/skills/galdr-ymirsystem/scripts/stop-all.sh +34 -0
- package/.agents/skills/galdr-ymirsystem/scripts/toon-check.py +188 -0
- package/.agents/skills/gjallarhorn-relay/README.md +3 -0
- package/.agents/skills/gjallarhorn-relay/SKILL.md +283 -0
- package/.agents/skills/groa-update/SKILL.md +90 -0
- package/.agents/skills/hamr-adapters/README.md +3 -0
- package/.agents/skills/hamr-adapters/SKILL.md +123 -0
- package/.agents/skills/hamr-adapters/references/common/control-and-recovery.md +37 -0
- package/.agents/skills/hamr-adapters/references/common/dispatch.md +32 -0
- package/.agents/skills/hamr-adapters/references/common/model-and-effort.md +42 -0
- package/.agents/skills/hamr-adapters/references/common/primary-hooks.md +40 -0
- package/.agents/skills/hamr-adapters/references/harness/claude.md +55 -0
- package/.agents/skills/hamr-adapters/references/harness/codex.md +43 -0
- package/.agents/skills/hamr-adapters/references/harness/cursor.md +75 -0
- package/.agents/skills/hamr-adapters/references/harness/grok.md +69 -0
- package/.agents/skills/hamr-adapters/references/harness/kimi.md +51 -0
- package/.agents/skills/hamr-adapters/references/harness/muse.md +70 -0
- package/.agents/skills/hamr-adapters/references/harness/opencode.md +42 -0
- package/.agents/skills/hamr-adapters/references/harness/pi.md +56 -0
- package/.agents/skills/herdr-panes/README.md +3 -0
- package/.agents/skills/herdr-panes/SKILL.md +92 -0
- package/.agents/skills/herdr-panes/assets/herdr-backend.md +346 -0
- package/.agents/skills/herdr-panes/assets/tmux-backend.md +115 -0
- package/.agents/skills/hnoss-design/DESIGN.md +19 -0
- package/.agents/skills/hnoss-design/README.md +3 -0
- package/.agents/skills/hnoss-design/SKILL.md +83 -0
- package/.agents/skills/hvild-afk/README.md +3 -0
- package/.agents/skills/hvild-afk/SKILL.md +241 -0
- package/.agents/skills/jord-projects/README.md +3 -0
- package/.agents/skills/jord-projects/SKILL.md +92 -0
- package/.agents/skills/lifecycle/SKILL.md +56 -0
- package/.agents/skills/lifecycle/smoke_test.sh +57 -0
- package/.agents/skills/lifecycle/start.sh +21 -0
- package/.agents/skills/lifecycle/status.sh +35 -0
- package/.agents/skills/lifecycle/stop.sh +16 -0
- package/.agents/skills/muninn-stow/README.md +3 -0
- package/.agents/skills/muninn-stow/SKILL.md +309 -0
- package/.agents/skills/no-mistakes/SKILL.md +379 -0
- package/.agents/skills/nornir-schedule/README.md +3 -0
- package/.agents/skills/nornir-schedule/SKILL.md +25 -0
- package/.agents/skills/nornir-schedule/assets/events.md +137 -0
- package/.agents/skills/nornir-schedule/assets/quota.md +131 -0
- package/.agents/skills/nsr-compliance/README.md +3 -0
- package/.agents/skills/nsr-compliance/SKILL.md +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/README.md +40 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/git-workflow.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/security.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/RULES/tech-stack.md +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/NSRcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/README.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/README.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/features/SKILL-template.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/SKILL.md +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/create_branch.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/safe_commit.sh +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/git_ops/sync_upstream.sh +6 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/README.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/SKILL.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/smoke_test.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/start.sh +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/status.sh +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/agents_skills/lifecycle/stop.sh +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/config/core_four.yaml +13 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/compliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/README.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/api.md +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/compliance-wiring.md +155 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/BEST_PRACTICES/frontend.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/README.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/clients/template-README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/deploy.sh +49 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/development.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/production.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/envs/staging.env.example +17 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/CI_CD/deployment/tenants/template.env.example +8 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/README.md +26 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/agent.md +7 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/developers/dev-1.md +20 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/linux.md +9 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/macos.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/shared.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/DEVELOPER_SETUP/windows.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/README.md +25 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/_template.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/HOSTING/hosting-1.md +41 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/README.md +21 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/README.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/deployment.md +19 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/disaster-recovery.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/RUNBOOK/ops-tasks.md +14 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/features/README.md +16 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/project-skills.md +146 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/research/software-compliance-research.md +269 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/docs/working-with-agents.md +260 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/AGENTS.md +38 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/ARCHITECTURE.md +36 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/BEST_PRACTICES.md +27 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/CI_CD.md +23 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/FEATURES.md +15 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/README.md +22 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/RULES.md +37 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/STRUCTURE.md +45 -0
- package/.agents/skills/nsr-compliance/assets/nsr/assets/root/TECH_STACK.md +28 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scaffold-spec.md +63 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/scaffold.sh +103 -0
- package/.agents/skills/nsr-compliance/assets/nsr/scripts/validate.sh +52 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/SKILL.md +71 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/config/core_four.yaml +16 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_danger.sh +102 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/check_wiring.sh +69 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/validate_code.sh +29 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/gates/verify_docs.py +66 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/generate.sh +109 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/result_envelope.json +14 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/envelopes/task_envelope.json +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/harness/runner.py +303 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/telemetry/logger.py +112 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/README.md +18 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/config/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/README.md +12 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_env.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_paths.sh +20 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/gates/check_platform.sh +23 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/README.md +11 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/harness/envelopes/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/README.md +10 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/installer/install.sh +125 -0
- package/.agents/skills/nsr-compliance/assets/nsrcompliance/templates/compliance/telemetry/README.md +11 -0
- package/.agents/skills/pr-ops/README.md +3 -0
- package/.agents/skills/pr-ops/SKILL.md +109 -0
- package/.agents/skills/ratatoskr-a2a/README.md +3 -0
- package/.agents/skills/ratatoskr-a2a/SKILL.md +148 -0
- package/.agents/skills/rules-check-drift/SKILL.md +81 -0
- package/.agents/skills/saga-bearings/README.md +3 -0
- package/.agents/skills/saga-bearings/SKILL.md +26 -0
- package/.agents/skills/saga-bearings/assets/bearings.md +161 -0
- package/.agents/skills/saga-bearings/assets/board-template.html +731 -0
- package/.agents/skills/saga-bearings/assets/recap.md +56 -0
- package/.agents/skills/syn-recovery/README.md +3 -0
- package/.agents/skills/syn-recovery/SKILL.md +54 -0
- package/.agents/skills/tyr-check/README.md +3 -0
- package/.agents/skills/tyr-check/SKILL.md +218 -0
- package/.agents/skills/urdh-hold/README.md +3 -0
- package/.agents/skills/urdh-hold/SKILL.md +25 -0
- package/.agents/skills/urdh-hold/assets/decisions.md +15 -0
- package/.agents/skills/urdh-hold/assets/hold.md +54 -0
- package/.agents/skills/vor-diagnostics/README.md +3 -0
- package/.agents/skills/vor-diagnostics/SKILL.md +68 -0
- package/.agents/skills/ymir-host/README.md +3 -0
- package/.agents/skills/ymir-host/SKILL.md +35 -0
- package/.agents/skills/ymir-host/assets/desktop.md +36 -0
- package/.agents/skills/ymir-host/assets/install.md +69 -0
- package/.agents/skills/ymir-host/assets/omarchy.md +297 -0
- package/.agents/skills/ymir-host/assets/runbooks.md +15 -0
- package/.agents/skills/ymir-host/assets/thjazi.md +220 -0
- package/.agents/skills/ymir-host/assets/update.md +64 -0
- package/.agents/tests/assets/board-render-harness.mjs +117 -0
- package/.agents/tests/cmux-test-safety.sh +51 -0
- package/.agents/tests/fixtures.sh +291 -0
- package/.agents/tests/fm-afk-inject-e2e.test.sh +428 -0
- package/.agents/tests/fm-afk-inject-herdr-e2e.test.sh +536 -0
- package/.agents/tests/fm-afk-launch.test.sh +961 -0
- package/.agents/tests/fm-afk-pi-herdr-return-e2e.test.sh +297 -0
- package/.agents/tests/fm-afk-return.test.sh +283 -0
- package/.agents/tests/fm-arm-pretool-check.test.sh +482 -0
- package/.agents/tests/fm-ask-user-authority.test.sh +42 -0
- package/.agents/tests/fm-backend-autodetect-smoke.test.sh +176 -0
- package/.agents/tests/fm-backend-cmux-smoke.test.sh +188 -0
- package/.agents/tests/fm-backend-cmux.test.sh +1166 -0
- package/.agents/tests/fm-backend-herdr-eventwait-smoke.test.sh +136 -0
- package/.agents/tests/fm-backend-herdr-eventwait.test.py +106 -0
- package/.agents/tests/fm-backend-herdr-focus-flash-e2e.test.sh +409 -0
- package/.agents/tests/fm-backend-herdr-launcher-workspace-e2e.test.sh +437 -0
- package/.agents/tests/fm-backend-herdr-presentation-e2e.test.sh +1424 -0
- package/.agents/tests/fm-backend-herdr-prune-safety-e2e.test.sh +180 -0
- package/.agents/tests/fm-backend-herdr-respawn-idem-e2e.test.sh +182 -0
- package/.agents/tests/fm-backend-herdr-smoke.test.sh +355 -0
- package/.agents/tests/fm-backend-herdr-workspace-per-home-e2e.test.sh +251 -0
- package/.agents/tests/fm-backend-herdr.test.sh +4606 -0
- package/.agents/tests/fm-backend-orca.test.sh +1357 -0
- package/.agents/tests/fm-backend-tmux-smoke.test.sh +173 -0
- package/.agents/tests/fm-backend-zellij-smoke.test.sh +205 -0
- package/.agents/tests/fm-backend-zellij.test.sh +1360 -0
- package/.agents/tests/fm-backend.test.sh +1142 -0
- package/.agents/tests/fm-backlog-atomicity.test.sh +2302 -0
- package/.agents/tests/fm-backlog-handoff.test.sh +1360 -0
- package/.agents/tests/fm-bearings-board-render.test.sh +135 -0
- package/.agents/tests/fm-bearings-board.test.sh +414 -0
- package/.agents/tests/fm-bearings-snapshot.test.sh +1990 -0
- package/.agents/tests/fm-bootstrap-network-parallel.test.sh +316 -0
- package/.agents/tests/fm-bootstrap.test.sh +1178 -0
- package/.agents/tests/fm-branch-supervision.test.sh +552 -0
- package/.agents/tests/fm-brief.test.sh +785 -0
- package/.agents/tests/fm-busy-adapter-wiring.test.sh +324 -0
- package/.agents/tests/fm-busy-state.test.sh +465 -0
- package/.agents/tests/fm-calm-pi-extension.test.sh +3979 -0
- package/.agents/tests/fm-captain-hold-lifecycle.test.sh +1187 -0
- package/.agents/tests/fm-cd-pretool-check.test.sh +400 -0
- package/.agents/tests/fm-check-unregister.test.sh +198 -0
- package/.agents/tests/fm-classify-corr-token.test.sh +543 -0
- package/.agents/tests/fm-classify-decision-key.test.sh +340 -0
- package/.agents/tests/fm-claude-stop-autoarm-live-e2e.test.sh +164 -0
- package/.agents/tests/fm-claude-stop-autoarm.test.sh +1190 -0
- package/.agents/tests/fm-cmux-claude-composer-live-e2e.test.sh +110 -0
- package/.agents/tests/fm-codex-continuity-live-e2e.test.sh +55 -0
- package/.agents/tests/fm-composer-ghost.test.sh +716 -0
- package/.agents/tests/fm-composer-lib.test.sh +666 -0
- package/.agents/tests/fm-composer-matrix-live-e2e.test.sh +220 -0
- package/.agents/tests/fm-control-herdr-smoke.test.sh +149 -0
- package/.agents/tests/fm-control-relaunch.test.sh +1530 -0
- package/.agents/tests/fm-control.test.sh +910 -0
- package/.agents/tests/fm-crew-state.test.sh +1610 -0
- package/.agents/tests/fm-cursor-harness.test.sh +404 -0
- package/.agents/tests/fm-cursor-primary-live-e2e.test.sh +217 -0
- package/.agents/tests/fm-cursor-primary.test.sh +708 -0
- package/.agents/tests/fm-daemon.test.sh +2740 -0
- package/.agents/tests/fm-documentation-audiences.test.sh +141 -0
- package/.agents/tests/fm-ensure-agents-md.test.sh +371 -0
- package/.agents/tests/fm-extension-binding.test.sh +2187 -0
- package/.agents/tests/fm-fleet-snapshot-view.test.sh +816 -0
- package/.agents/tests/fm-fleet-sync.test.sh +721 -0
- package/.agents/tests/fm-gate-refuse.test.sh +356 -0
- package/.agents/tests/fm-gitignore-config.test.sh +89 -0
- package/.agents/tests/fm-gotmp.test.sh +230 -0
- package/.agents/tests/fm-grok-continuity-live-e2e.test.sh +112 -0
- package/.agents/tests/fm-grok-harness.test.sh +117 -0
- package/.agents/tests/fm-grok-stop-live-e2e.test.sh +219 -0
- package/.agents/tests/fm-guard-stale-banner.test.sh +709 -0
- package/.agents/tests/fm-harness-adapter-instructions-live-e2e.test.sh +134 -0
- package/.agents/tests/fm-harness-adapter-references.test.sh +30 -0
- package/.agents/tests/fm-harness-liveness-drift-live-e2e.test.sh +151 -0
- package/.agents/tests/fm-herdr-lab.test.sh +243 -0
- package/.agents/tests/fm-herdr-session-cleanup-e2e.test.sh +146 -0
- package/.agents/tests/fm-herdr-session-cleanup.test.sh +329 -0
- package/.agents/tests/fm-herdr-submit-confirm-live-e2e.test.sh +125 -0
- package/.agents/tests/fm-herdr-version-floor-live-e2e.test.sh +144 -0
- package/.agents/tests/fm-home-summary-refresh.test.sh +971 -0
- package/.agents/tests/fm-inactive-reconcile.test.sh +485 -0
- package/.agents/tests/fm-kimi-harness.test.sh +687 -0
- package/.agents/tests/fm-lint-workflows.test.sh +532 -0
- package/.agents/tests/fm-lint.test.sh +1023 -0
- package/.agents/tests/fm-muse-harness.test.sh +932 -0
- package/.agents/tests/fm-muse-signals-live-e2e.test.sh +205 -0
- package/.agents/tests/fm-no-mistakes-required.test.sh +72 -0
- package/.agents/tests/fm-on.test.sh +507 -0
- package/.agents/tests/fm-opencode-primary-live-e2e.test.sh +357 -0
- package/.agents/tests/fm-operational-input.test.sh +160 -0
- package/.agents/tests/fm-peek-remote.test.sh +110 -0
- package/.agents/tests/fm-pending-reply.test.sh +1288 -0
- package/.agents/tests/fm-pi-branch-extension.test.sh +3182 -0
- package/.agents/tests/fm-pi-branch-live-e2e.test.sh +527 -0
- package/.agents/tests/fm-pi-primary-live-e2e.test.sh +345 -0
- package/.agents/tests/fm-pi-primary-types.test.sh +68 -0
- package/.agents/tests/fm-pi-watch-extension.test.sh +2843 -0
- package/.agents/tests/fm-pr-check-security.test.sh +2140 -0
- package/.agents/tests/fm-pr-merge.test.sh +2135 -0
- package/.agents/tests/fm-procevent-quota.test.sh +225 -0
- package/.agents/tests/fm-procevent-when.test.sh +406 -0
- package/.agents/tests/fm-procevent.test.sh +1745 -0
- package/.agents/tests/fm-project-origin.test.sh +110 -0
- package/.agents/tests/fm-public-followup.test.sh +2272 -0
- package/.agents/tests/fm-quota-array-dispatch-live-e2e.test.sh +526 -0
- package/.agents/tests/fm-quota-choose.test.sh +626 -0
- package/.agents/tests/fm-remote-backlog-handoff.test.sh +453 -0
- package/.agents/tests/fm-remote-doctor.test.sh +625 -0
- package/.agents/tests/fm-remote-entrypoint.test.sh +59 -0
- package/.agents/tests/fm-remote-job-orphan-reap.test.sh +205 -0
- package/.agents/tests/fm-remote-job.test.sh +768 -0
- package/.agents/tests/fm-remote-reply.test.sh +520 -0
- package/.agents/tests/fm-remote-secondmate-lifecycle-e2e.test.sh +1256 -0
- package/.agents/tests/fm-remote-secondmate-parent-binding.test.sh +326 -0
- package/.agents/tests/fm-remote-secondmate-trace-context.test.sh +310 -0
- package/.agents/tests/fm-remote-transport-lanes.test.sh +431 -0
- package/.agents/tests/fm-review-diff.test.sh +176 -0
- package/.agents/tests/fm-secondmate-harness.test.sh +2600 -0
- package/.agents/tests/fm-secondmate-lifecycle-e2e.test.sh +259 -0
- package/.agents/tests/fm-secondmate-liveness.test.sh +559 -0
- package/.agents/tests/fm-secondmate-reconcile.test.sh +732 -0
- package/.agents/tests/fm-secondmate-safety.test.sh +2972 -0
- package/.agents/tests/fm-secondmate-sync.test.sh +913 -0
- package/.agents/tests/fm-send-inbox-doorbell-live-e2e.test.sh +206 -0
- package/.agents/tests/fm-send-inbox.test.sh +352 -0
- package/.agents/tests/fm-send-popup-settle.test.sh +167 -0
- package/.agents/tests/fm-send-remote-delivery.test.sh +796 -0
- package/.agents/tests/fm-send-resolve-key.test.sh +551 -0
- package/.agents/tests/fm-send-secondmate-marker-herdr-e2e.test.sh +187 -0
- package/.agents/tests/fm-send-secondmate-marker.test.sh +278 -0
- package/.agents/tests/fm-send-settle.test.sh +147 -0
- package/.agents/tests/fm-send-strict.test.sh +241 -0
- package/.agents/tests/fm-session-lock-ancestry.test.sh +365 -0
- package/.agents/tests/fm-session-start.test.sh +2515 -0
- package/.agents/tests/fm-sessionstart-hook-live-e2e.test.sh +631 -0
- package/.agents/tests/fm-sessionstart-instruction-refresh-live-e2e.test.sh +230 -0
- package/.agents/tests/fm-sessionstart-nudge.test.sh +1039 -0
- package/.agents/tests/fm-shared-captain-inheritance.test.sh +404 -0
- package/.agents/tests/fm-spawn-batch.test.sh +148 -0
- package/.agents/tests/fm-spawn-dispatch-profile.test.sh +827 -0
- package/.agents/tests/fm-spawn-pool-base-freshen.test.sh +440 -0
- package/.agents/tests/fm-spawn-worktree-settle.test.sh +147 -0
- package/.agents/tests/fm-startup-memory-budget.test.sh +333 -0
- package/.agents/tests/fm-startup-network.test.sh +712 -0
- package/.agents/tests/fm-stow-cascade.test.sh +370 -0
- package/.agents/tests/fm-subagent-pretool-check.test.sh +291 -0
- package/.agents/tests/fm-supervision-events.test.sh +156 -0
- package/.agents/tests/fm-supervision-instructions.test.sh +189 -0
- package/.agents/tests/fm-tangle-guard.test.sh +277 -0
- package/.agents/tests/fm-task-delivery.test.sh +420 -0
- package/.agents/tests/fm-task-inbox.test.sh +513 -0
- package/.agents/tests/fm-teardown-endpoint-safety.test.sh +374 -0
- package/.agents/tests/fm-teardown.test.sh +2665 -0
- package/.agents/tests/fm-test-fixture-cleanup.test.sh +172 -0
- package/.agents/tests/fm-test-fixtures.test.sh +132 -0
- package/.agents/tests/fm-test-isolation-proof.test.sh +278 -0
- package/.agents/tests/fm-test-run.test.sh +1196 -0
- package/.agents/tests/fm-tmux-agent-liveness.test.sh +354 -0
- package/.agents/tests/fm-tmux-submit-busy.test.sh +340 -0
- package/.agents/tests/fm-tool-update-check.test.sh +1044 -0
- package/.agents/tests/fm-trace-context-lib.test.sh +253 -0
- package/.agents/tests/fm-trace-context-spawn.test.sh +581 -0
- package/.agents/tests/fm-transition-lib.test.sh +54 -0
- package/.agents/tests/fm-turnend-guard.test.sh +1822 -0
- package/.agents/tests/fm-update.test.sh +304 -0
- package/.agents/tests/fm-vendor-auth-probe.test.sh +395 -0
- package/.agents/tests/fm-voice-relay.test.sh +4741 -0
- package/.agents/tests/fm-wake-daemon-lifecycle-e2e.test.sh +171 -0
- package/.agents/tests/fm-wake-drain-open-decisions-cursor.test.sh +356 -0
- package/.agents/tests/fm-wake-drain-open-decisions.test.sh +226 -0
- package/.agents/tests/fm-wake-drain-unread-status.test.sh +379 -0
- package/.agents/tests/fm-wake-queue.test.sh +1224 -0
- package/.agents/tests/fm-watch-arm.test.sh +821 -0
- package/.agents/tests/fm-watch-checkpoint.test.sh +87 -0
- package/.agents/tests/fm-watch-recovery-loop.test.sh +223 -0
- package/.agents/tests/fm-watch-triage.test.sh +3889 -0
- package/.agents/tests/fm-watcher-lock.test.sh +1133 -0
- package/.agents/tests/fm-x-mode.test.sh +3042 -0
- package/.agents/tests/gleipnir-guard-pretool-check.test.sh +51 -0
- package/.agents/tests/gleipnir-machine-lock.test.sh +130 -0
- package/.agents/tests/herdr-test-safety.sh +42 -0
- package/.agents/tests/lib.sh +315 -0
- package/.agents/tests/remote-herdr-fixture.sh +125 -0
- package/.agents/tests/secondmate-helpers.sh +193 -0
- package/.agents/tests/smoke.test.sh +93 -0
- package/.agents/tests/wake-helpers.sh +338 -0
- package/.agents/tests/zellij-test-safety.sh +60 -0
- package/.agents/tools/README.md +10 -0
- package/.agents/tools/bin/tasks-cli +267 -0
- package/.agents/tools/tasks-cli.ts +216 -0
- package/AGENTS.md +498 -0
- package/CHANGELOG.md +1913 -0
- package/LICENSE +1 -1
- package/NOTICE +63 -0
- package/README.md +114 -60
- package/RULES/01-domains.md +51 -0
- package/RULES/02-agents.md +78 -0
- package/RULES/03-houses.md +61 -0
- package/RULES/04-hoard.md +97 -0
- package/RULES/05-platforms.md +67 -0
- package/RULES/06-append-only.md +108 -0
- package/RULES/07-config.md +101 -0
- package/RULES/08-delivery-gate.md +75 -0
- package/RULES/09-electron.md +58 -0
- package/RULES/README.md +22 -0
- package/bin/README.md +107 -0
- package/bin/a2a-mcp.sh +133 -0
- package/bin/a2a-serve.py +103 -0
- package/bin/a2a-serve.sh +8 -0
- package/bin/a2a-talk.sh +50 -0
- package/bin/a2abridge-ensure.sh +108 -0
- package/bin/agent-run.sh +43 -0
- package/bin/agents-config.sh +256 -0
- package/bin/apodex-smoke-test.sh +105 -0
- package/bin/bifrost-bridge.sh +131 -0
- package/bin/bifrost-ingress.sh +77 -0
- package/bin/bootstrap-macos.sh +112 -0
- package/bin/bootstrap-windows.ps1 +112 -0
- package/bin/branch-guard.sh +64 -0
- package/bin/brokk +73 -0
- package/bin/brokk-classify-lib.sh +1770 -0
- package/bin/brokk-lease-lib.sh +222 -0
- package/bin/brokk-lease.sh +193 -0
- package/bin/brokk-lint.sh +65 -0
- package/bin/brokk-send.sh +51 -0
- package/bin/brokk-update.sh +9 -0
- package/bin/brokk-wake-grant.sh +127 -0
- package/bin/brokk-wake-lib.sh +1849 -0
- package/bin/changelog-assemble.sh +154 -0
- package/bin/changelog-guard.sh +197 -0
- package/bin/crash-sense.sh +173 -0
- package/bin/design-check.sh +80 -0
- package/bin/design-icon.sh +153 -0
- package/bin/desktop-place.sh +224 -0
- package/bin/docs-guard.sh +39 -0
- package/bin/editor-place.sh +225 -0
- package/bin/eindri-acclaim.sh +47 -0
- package/bin/eindri-control.sh +28 -0
- package/bin/eindri-role.sh +83 -0
- package/bin/eindri-seat.sh +83 -0
- package/bin/eindri-seen.sh +54 -0
- package/bin/eindri-send.sh +15 -0
- package/bin/eindri-start.sh +98 -0
- package/bin/eindri-watch.sh +57 -0
- package/bin/einherjar-spawn.sh +576 -0
- package/bin/eir-doctor.sh +158 -0
- package/bin/erindi-brief.sh +271 -0
- package/bin/feature-inventory.sh +73 -0
- package/bin/fleet-apply.sh +81 -0
- package/bin/github-deploy.sh +94 -0
- package/bin/gjallarhorn-expose.sh +136 -0
- package/bin/gjallarhorn-notify.sh +71 -0
- package/bin/gjallarhorn-purge.sh +67 -0
- package/bin/gjallarhorn-tunnel.sh +85 -0
- package/bin/gleipnir-lock-lib.sh +284 -0
- package/bin/groa-update.sh +130 -0
- package/bin/hall-snapshot.sh +111 -0
- package/bin/hamr-harness.sh +246 -0
- package/bin/heimdall-ssh-keys.sh +374 -0
- package/bin/herdr-agents.py +44 -0
- package/bin/herdr-ensure.sh +104 -0
- package/bin/herdr-run.sh +452 -0
- package/bin/hermes-ensure.sh +88 -0
- package/bin/hlidskjalf-agents.sh +172 -0
- package/bin/hlidskjalf-usage.sh +196 -0
- package/bin/hoard-guard.sh +135 -0
- package/bin/hoard-lib.sh +105 -0
- package/bin/hodd.sh +102 -0
- package/bin/host-sense.sh +199 -0
- package/bin/huginn-research-worker.sh +121 -0
- package/bin/local-model-lock.sh +70 -0
- package/bin/mimir-bridge.py +259 -0
- package/bin/mimir-bridge.sh +100 -0
- package/bin/mimir-ingest.sh +131 -0
- package/bin/mimir-reflect.sh +27 -0
- package/bin/mimir.sh +113 -0
- package/bin/mjollnir-webhook.sh +72 -0
- package/bin/mjollnir.sh +104 -0
- package/bin/model-hardware.sh +96 -0
- package/bin/model-resolve.sh +160 -0
- package/bin/models-detect.sh +178 -0
- package/bin/nornir-cron-start.sh +162 -0
- package/bin/nornir-job-daily-briefing.sh +191 -0
- package/bin/nornir-job-git-sync.sh +152 -0
- package/bin/nornir-job-hall-snapshot.sh +28 -0
- package/bin/nornir-job-memory-housekeeping.sh +114 -0
- package/bin/nornir-job-nsr-compliance.sh +54 -0
- package/bin/nornir-job-observer.sh +166 -0
- package/bin/nornir-job-skillopt-sleep.sh +76 -0
- package/bin/npm-publish.sh +136 -0
- package/bin/omarchy-hook-install.sh +60 -0
- package/bin/omarchy-install.sh +161 -0
- package/bin/omarchy-plugins.sh +187 -0
- package/bin/omarchy-sense.sh +162 -0
- package/bin/perm-guard.sh +30 -0
- package/bin/pi-agent.sh +68 -0
- package/bin/pi-ensure.sh +77 -0
- package/bin/pi-local.sh +37 -0
- package/bin/pi-model-agnostic.sh +71 -0
- package/bin/pi-seat.sh +63 -0
- package/bin/prereq-ensure.sh +166 -0
- package/bin/private-guard.sh +58 -0
- package/bin/project-git.sh +69 -0
- package/bin/public-guard.sh +71 -0
- package/bin/ratatoskr.sh +49 -0
- package/bin/realm-lib.sh +32 -0
- package/bin/repo-scrub.sh +62 -0
- package/bin/rodd-operational-input.sh +250 -0
- package/bin/runes-append.sh +198 -0
- package/bin/saga-session-start.sh +172 -0
- package/bin/saga-sessionstart-run.sh +79 -0
- package/bin/saga-wake-drain.sh +41 -0
- package/bin/secret-guard.sh +63 -0
- package/bin/sessrumnir-ensure.sh +100 -0
- package/bin/sessrumnir-sync.sh +237 -0
- package/bin/sessrumnir.sh +104 -0
- package/bin/skillopt-setup.sh +76 -0
- package/bin/skuld-branch-outcome.sh +203 -0
- package/bin/skuld-branch-prompt.sh +101 -0
- package/bin/smidja-bootstrap.sh +94 -0
- package/bin/smidja-observe.sh +86 -0
- package/bin/syn-arm-pretool-check.sh +57 -0
- package/bin/syn-asset-pretool-check.sh +89 -0
- package/bin/syn-cd-pretool-check.sh +28 -0
- package/bin/syn-guard-pretool-check.sh +83 -0
- package/bin/syn-spawn-pretool-check.sh +58 -0
- package/bin/syn-turnend-guard.sh +36 -0
- package/bin/syn-watch-arm.sh +99 -0
- package/bin/tailscale-sync.sh +203 -0
- package/bin/telegram-bot.sh +82 -0
- package/bin/toolchain.sh +74 -0
- package/bin/ui-truth-check.sh +75 -0
- package/bin/utgard.sh +111 -0
- package/bin/valhalla.sh +115 -0
- package/bin/valknut-load.sh +298 -0
- package/bin/vor-crew-state.sh +124 -0
- package/bin/wedge-notify.sh +60 -0
- package/bin/workspace-provision.sh +80 -0
- package/bin/workspace-rag.sh +163 -0
- package/bin/wyrd-db.sh +47 -0
- package/bin/yggdrasil.sh +152 -0
- package/bin/ymir-edit.sh +145 -0
- package/bin/ymir-install.sh +806 -0
- package/bin/ymir-invite.sh +93 -0
- package/bin/ymir-isolation.sh +88 -0
- package/bin/ymir-migrate.sh +61 -0
- package/bin/ymir-plan.sh +356 -0
- package/bin/ymir-platform.sh +348 -0
- package/bin/ymir-say.sh +110 -0
- package/bin/ymir-setup-auth.sh +119 -0
- package/bin/ymir-validate.sh +193 -0
- package/bin/ymir.js +74 -16
- package/docs/Architecture.md +339 -0
- package/docs/design.md +351 -0
- package/docs/installations/README.md +63 -0
- package/docs/installations/ymir-home.md +93 -0
- package/docs/integration.md +105 -0
- package/docs/lore.md +1060 -0
- package/docs/research/agent-harnesses.md +290 -0
- package/docs/runbooks/README.md +19 -0
- package/docs/runbooks/a2a.md +91 -0
- package/docs/runbooks/agent-permissions.md +99 -0
- package/docs/runbooks/agents.md +57 -0
- package/docs/runbooks/models.md +69 -0
- package/docs/runbooks/realm-onboarding.md +60 -0
- package/docs/runbooks/secrets-and-hoard.md +60 -0
- package/docs/runbooks/updates-and-migrations.md +110 -0
- package/docs/runbooks/worktrees-isolation.md +72 -0
- package/docs/session-start.md +105 -0
- package/docs/skillopt-integration.md +321 -0
- package/docs/sync-system.md +66 -0
- package/docs/workspaces.md +104 -0
- package/docs/ymir-rut.md +260 -0
- package/package.json +54 -14
- package/packaging/build.sh +74 -0
- package/packaging/macos/Ymir Installer.command +43 -0
- package/packaging/windows/ymir-setup.nsi +74 -0
- package/scripts/README.md +22 -0
- package/scripts/electron.sh +269 -0
- package/scripts/lower.sh +34 -0
- package/scripts/raise.sh +42 -0
- package/scripts/start.sh +217 -0
- package/scripts/stop.sh +113 -0
- package/install.sh +0 -35
|
@@ -0,0 +1,898 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# fm-tool-update-check.sh - report watched tooling that has an update available,
|
|
3
|
+
# and tooling whose update is installed but not in effect.
|
|
4
|
+
#
|
|
5
|
+
# Usage:
|
|
6
|
+
# fm-tool-update-check.sh [check]
|
|
7
|
+
# fm-tool-update-check.sh arm
|
|
8
|
+
# fm-tool-update-check.sh disarm
|
|
9
|
+
# fm-tool-update-check.sh --help
|
|
10
|
+
#
|
|
11
|
+
# `check` prints one line when something needs attention and prints nothing at
|
|
12
|
+
# all otherwise, so it composes with the existing watcher state-check contract
|
|
13
|
+
# instead of needing a schedule of its own. `arm` writes
|
|
14
|
+
# state/tool-updates.check.sh and binds its bytes with fm-check-register.sh, so
|
|
15
|
+
# the watcher dispatches it on its normal FM_CHECK_INTERVAL cadence and turns
|
|
16
|
+
# its one line into a `check:` wake. `disarm` removes the shim, its trust
|
|
17
|
+
# binding, and the report record.
|
|
18
|
+
#
|
|
19
|
+
# Two conditions are reported, and they are deliberately distinct:
|
|
20
|
+
#
|
|
21
|
+
# "<tool> update available" a newer version exists at the update source.
|
|
22
|
+
# "<tool> update not in effect" a newer copy is installed on this host, but
|
|
23
|
+
# PATH still resolves an older one.
|
|
24
|
+
#
|
|
25
|
+
# The second condition is the reason this script exists. A tool that
|
|
26
|
+
# self-installs into ~/.local/bin while a version manager keeps its own older
|
|
27
|
+
# copy earlier on PATH looks fully up to date to anything that asks only "is a
|
|
28
|
+
# newer version published". So PATH skew is measured, never inferred: every
|
|
29
|
+
# executable copy on PATH is asked for its own version, and those answers are
|
|
30
|
+
# compared. A directory name is never read as a version, because a version
|
|
31
|
+
# manager's "latest" directory can hold an older build. A copy that will not
|
|
32
|
+
# report a version is reported as a check failure rather than assumed current.
|
|
33
|
+
#
|
|
34
|
+
# What this script never does: it reports, and it repairs nothing. It does not
|
|
35
|
+
# install, update, uninstall, reorder PATH, or touch any version manager's
|
|
36
|
+
# configuration, and it never fetches into a watched git repository. Every git
|
|
37
|
+
# probe is read-only (rev-parse, symbolic-ref, ls-remote, cat-file, merge-base,
|
|
38
|
+
# rev-list), so a watched project is never mutated.
|
|
39
|
+
#
|
|
40
|
+
# The watched tools live in config/watched-tools.json, which is local and
|
|
41
|
+
# gitignored, and is never propagated to another home. Adding a tool is a config
|
|
42
|
+
# edit, never a code change. docs/configuration.md owns that schema.
|
|
43
|
+
#
|
|
44
|
+
# Probing costs real time, so `check` runs its probes at most once per
|
|
45
|
+
# FM_TOOL_UPDATE_INTERVAL (default 900, 0 disables the gate, otherwise 60..86400)
|
|
46
|
+
# and stays silent in between. Each probe is bounded by
|
|
47
|
+
# FM_TOOL_UPDATE_PROBE_SECS (default 5, valid 1..30) and a whole sweep by
|
|
48
|
+
# FM_TOOL_UPDATE_BUDGET_SECS (default 20, valid 1..120).
|
|
49
|
+
#
|
|
50
|
+
# The sweep has to finish inside the watcher's own per check bound, because a run
|
|
51
|
+
# the watcher kills prints nothing and writes no record, so it would repeat that
|
|
52
|
+
# silence on every poll. That coupling is enforced rather than assumed: a budget
|
|
53
|
+
# larger than FM_CHECK_TIMEOUT (default 30, read from this check's own
|
|
54
|
+
# environment because the watcher runs it as a direct child) allows is cut down
|
|
55
|
+
# to what fits, and the cut is reported in the report line so the operator sees
|
|
56
|
+
# it. A budget that cannot be read as a whole number from 1 to 120 is still
|
|
57
|
+
# refused outright.
|
|
58
|
+
#
|
|
59
|
+
# The report record state/.tool-updates is written only when a sweep runs to its
|
|
60
|
+
# end, and it carries the whole finding set the last report was made from,
|
|
61
|
+
# uncut, so the same pending update is reported once rather than on every poll
|
|
62
|
+
# while a new finding that lands past the one-line cut is still news. A sweep
|
|
63
|
+
# killed part way through leaves no record and is retried, instead of
|
|
64
|
+
# suppressing its finding.
|
|
65
|
+
set -u
|
|
66
|
+
export LC_ALL=C
|
|
67
|
+
# A watched git remote must never stop to ask for credentials; an unauthenticated
|
|
68
|
+
# probe has to fail inside its bound instead of waiting for an answer.
|
|
69
|
+
export GIT_TERMINAL_PROMPT=0
|
|
70
|
+
|
|
71
|
+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
72
|
+
FM_HOME="${FM_HOME:-${FM_ROOT_OVERRIDE:-$(cd "$SCRIPT_DIR/.." && pwd)}}"
|
|
73
|
+
STATE="${FM_STATE_OVERRIDE:-$FM_HOME/state}"
|
|
74
|
+
CONFIG="${FM_CONFIG_OVERRIDE:-$FM_HOME/config}/watched-tools.json"
|
|
75
|
+
RECORD="$STATE/.tool-updates"
|
|
76
|
+
CHECK_ID=tool-updates
|
|
77
|
+
CHECK_SHIM="$STATE/$CHECK_ID.check.sh"
|
|
78
|
+
CHECK_TRUST="$STATE/$CHECK_ID.check-trust"
|
|
79
|
+
REGISTER_BIN="$SCRIPT_DIR/fm-check-register.sh"
|
|
80
|
+
RECORD_SCHEMA=fm-tool-updates-v1
|
|
81
|
+
# Wider than the digest default because one finding names two absolute paths and
|
|
82
|
+
# their two versions, and several tools can report in the same sweep.
|
|
83
|
+
MAX_LINE=1000
|
|
84
|
+
|
|
85
|
+
# shellcheck source=bin/fm-timeout-lib.sh
|
|
86
|
+
. "$SCRIPT_DIR/fm-timeout-lib.sh"
|
|
87
|
+
# shellcheck source=bin/fm-pr-lib.sh
|
|
88
|
+
. "$SCRIPT_DIR/fm-pr-lib.sh"
|
|
89
|
+
# shellcheck source=bin/fm-line-cap-lib.sh
|
|
90
|
+
. "$SCRIPT_DIR/fm-line-cap-lib.sh"
|
|
91
|
+
# shellcheck source=bin/fm-check-lib.sh
|
|
92
|
+
. "$SCRIPT_DIR/fm-check-lib.sh"
|
|
93
|
+
|
|
94
|
+
usage() {
|
|
95
|
+
cat <<'EOF'
|
|
96
|
+
Usage:
|
|
97
|
+
fm-tool-update-check.sh [check] report watched tools needing attention (silent when current)
|
|
98
|
+
fm-tool-update-check.sh arm write and register state/tool-updates.check.sh
|
|
99
|
+
fm-tool-update-check.sh disarm remove the check shim, its trust binding, and the record
|
|
100
|
+
fm-tool-update-check.sh --help print this help
|
|
101
|
+
|
|
102
|
+
Watched tools are read from config/watched-tools.json (local, gitignored).
|
|
103
|
+
See docs/configuration.md for the schema and docs/examples/watched-tools.json for a starting point.
|
|
104
|
+
EOF
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
die_usage() {
|
|
108
|
+
printf 'fm-tool-update-check: %s\n' "$1" >&2
|
|
109
|
+
usage >&2
|
|
110
|
+
exit 2
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
INTERVAL=${FM_TOOL_UPDATE_INTERVAL:-900}
|
|
114
|
+
case "$INTERVAL" in
|
|
115
|
+
''|*[!0-9]*)
|
|
116
|
+
printf 'fm-tool-update-check: FM_TOOL_UPDATE_INTERVAL must be 0 or a whole number from 60 to 86400\n' >&2
|
|
117
|
+
exit 2
|
|
118
|
+
;;
|
|
119
|
+
esac
|
|
120
|
+
if [ "$INTERVAL" -ne 0 ] && { [ "$INTERVAL" -lt 60 ] || [ "$INTERVAL" -gt 86400 ]; }; then
|
|
121
|
+
printf 'fm-tool-update-check: FM_TOOL_UPDATE_INTERVAL must be 0 or a whole number from 60 to 86400\n' >&2
|
|
122
|
+
exit 2
|
|
123
|
+
fi
|
|
124
|
+
|
|
125
|
+
PROBE_SECS=${FM_TOOL_UPDATE_PROBE_SECS:-5}
|
|
126
|
+
case "$PROBE_SECS" in
|
|
127
|
+
''|*[!0-9]*|0)
|
|
128
|
+
printf 'fm-tool-update-check: FM_TOOL_UPDATE_PROBE_SECS must be a whole number from 1 to 30\n' >&2
|
|
129
|
+
exit 2
|
|
130
|
+
;;
|
|
131
|
+
esac
|
|
132
|
+
if [ "$PROBE_SECS" -gt 30 ]; then
|
|
133
|
+
printf 'fm-tool-update-check: FM_TOOL_UPDATE_PROBE_SECS must be a whole number from 1 to 30\n' >&2
|
|
134
|
+
exit 2
|
|
135
|
+
fi
|
|
136
|
+
|
|
137
|
+
BUDGET_SECS=${FM_TOOL_UPDATE_BUDGET_SECS:-20}
|
|
138
|
+
case "$BUDGET_SECS" in
|
|
139
|
+
''|*[!0-9]*|0)
|
|
140
|
+
printf 'fm-tool-update-check: FM_TOOL_UPDATE_BUDGET_SECS must be a whole number from 1 to 120\n' >&2
|
|
141
|
+
exit 2
|
|
142
|
+
;;
|
|
143
|
+
esac
|
|
144
|
+
if [ "$BUDGET_SECS" -gt 120 ]; then
|
|
145
|
+
printf 'fm-tool-update-check: FM_TOOL_UPDATE_BUDGET_SECS must be a whole number from 1 to 120\n' >&2
|
|
146
|
+
exit 2
|
|
147
|
+
fi
|
|
148
|
+
|
|
149
|
+
# The smallest bound a probe can be given, because fm_run_timed treats a
|
|
150
|
+
# non-positive bound as no bound.
|
|
151
|
+
PROBE_MIN_SECS=1
|
|
152
|
+
# Both clocks here count whole seconds, so a probe can start when the arithmetic
|
|
153
|
+
# says a second is left while almost none of it really is, and it still gets a
|
|
154
|
+
# full bound.
|
|
155
|
+
CLOCK_ROUNDING_SECS=1
|
|
156
|
+
# fm_run_timed asks its runner for -k 1, so a probe that does not stop on TERM is
|
|
157
|
+
# only killed a second after its bound.
|
|
158
|
+
KILL_GRACE_SECS=1
|
|
159
|
+
|
|
160
|
+
# The watcher's per check bound, read from this check's own environment. The
|
|
161
|
+
# watcher runs the check as a direct child, so an operator who raised it is seen
|
|
162
|
+
# here too, and when it is unset both sides resolve the same default.
|
|
163
|
+
CHECK_TIMEOUT=${FM_CHECK_TIMEOUT:-30}
|
|
164
|
+
case "$CHECK_TIMEOUT" in
|
|
165
|
+
''|*[!0-9]*|0) CHECK_TIMEOUT=30 ;;
|
|
166
|
+
esac
|
|
167
|
+
# The last probe of a sweep can end this far past the deadline, so that is what
|
|
168
|
+
# the budget has to leave the watcher's own bound.
|
|
169
|
+
BUDGET_MAX=$((CHECK_TIMEOUT - PROBE_MIN_SECS - CLOCK_ROUNDING_SECS - KILL_GRACE_SECS))
|
|
170
|
+
[ "$BUDGET_MAX" -ge 1 ] || BUDGET_MAX=1
|
|
171
|
+
# Cut rather than refuse. A refusal is reported once and then suppressed by the
|
|
172
|
+
# no-nag gate, which leaves the detector dead and quiet, and a check that goes
|
|
173
|
+
# silent is worse than a check that reports something awkward.
|
|
174
|
+
BUDGET_CUT_FROM=
|
|
175
|
+
if [ "$BUDGET_SECS" -gt "$BUDGET_MAX" ]; then
|
|
176
|
+
BUDGET_CUT_FROM=$BUDGET_SECS
|
|
177
|
+
BUDGET_SECS=$BUDGET_MAX
|
|
178
|
+
fi
|
|
179
|
+
|
|
180
|
+
# --- small helpers ----------------------------------------------------------
|
|
181
|
+
|
|
182
|
+
# The record epoch is overridable so a test can drive the cadence gate; the
|
|
183
|
+
# sweep budget always uses real time so a frozen epoch cannot disable it.
|
|
184
|
+
record_epoch_now() {
|
|
185
|
+
case "${FM_TOOL_UPDATE_NOW:-}" in
|
|
186
|
+
''|*[!0-9]*) date +%s ;;
|
|
187
|
+
*) printf '%s\n' "$FM_TOOL_UPDATE_NOW" ;;
|
|
188
|
+
esac
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
real_epoch() { date +%s; }
|
|
192
|
+
|
|
193
|
+
FINDINGS=
|
|
194
|
+
DEADLINE=0
|
|
195
|
+
INCOMPLETE_REPORTED=0
|
|
196
|
+
|
|
197
|
+
# Each finding is flattened to a single line here, because the whole report must
|
|
198
|
+
# stay one line for the wake record.
|
|
199
|
+
emit() {
|
|
200
|
+
local text
|
|
201
|
+
text=$(printf '%s' "$1" | tr '\t\r\n' ' ')
|
|
202
|
+
if [ -z "$FINDINGS" ]; then
|
|
203
|
+
FINDINGS=$text
|
|
204
|
+
else
|
|
205
|
+
FINDINGS="$FINDINGS; $text"
|
|
206
|
+
fi
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
budget_exhausted() {
|
|
210
|
+
[ "$(real_epoch)" -ge "$DEADLINE" ]
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
# True while the sweep budget still has room for another probe. When it does not,
|
|
214
|
+
# it records once which tool the sweep did not finish, so a sweep that cannot
|
|
215
|
+
# finish says so rather than being killed by the watcher with nothing printed.
|
|
216
|
+
budget_allows() {
|
|
217
|
+
local name=$1
|
|
218
|
+
budget_exhausted || return 0
|
|
219
|
+
if [ "$INCOMPLETE_REPORTED" -eq 0 ]; then
|
|
220
|
+
INCOMPLETE_REPORTED=1
|
|
221
|
+
emit "check incomplete: the time budget ran out before $name"
|
|
222
|
+
fi
|
|
223
|
+
return 1
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
# The bound for one probe: the probe bound, cut down to whatever the sweep
|
|
227
|
+
# budget has left, so no probe can run past the end of the sweep. Never below
|
|
228
|
+
# PROBE_MIN_SECS, because fm_run_timed treats a non-positive bound as no bound.
|
|
229
|
+
probe_bound() {
|
|
230
|
+
local left
|
|
231
|
+
left=$((DEADLINE - $(real_epoch)))
|
|
232
|
+
if [ "$left" -lt "$PROBE_MIN_SECS" ]; then
|
|
233
|
+
printf '%s\n' "$PROBE_MIN_SECS"
|
|
234
|
+
elif [ "$left" -lt "$PROBE_SECS" ]; then
|
|
235
|
+
printf '%s\n' "$left"
|
|
236
|
+
else
|
|
237
|
+
printf '%s\n' "$PROBE_SECS"
|
|
238
|
+
fi
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
# First dotted number in the text, so "herdr 0.8.2" and "v1.46.0" both work.
|
|
242
|
+
parse_version() {
|
|
243
|
+
printf '%s' "$1" | grep -oE '[0-9]+(\.[0-9]+)+' | head -n 1
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
# version_newer <a> <b>: true when version a is numerically newer than b.
|
|
247
|
+
version_newer() {
|
|
248
|
+
local a=$1 b=$2 i left right
|
|
249
|
+
local -a ap bp
|
|
250
|
+
IFS=. read -r -a ap <<< "$a"
|
|
251
|
+
IFS=. read -r -a bp <<< "$b"
|
|
252
|
+
i=0
|
|
253
|
+
while [ "$i" -lt "${#ap[@]}" ] || [ "$i" -lt "${#bp[@]}" ]; do
|
|
254
|
+
left=$((10#${ap[i]:-0}))
|
|
255
|
+
right=$((10#${bp[i]:-0}))
|
|
256
|
+
if [ "$left" -gt "$right" ]; then
|
|
257
|
+
return 0
|
|
258
|
+
elif [ "$left" -lt "$right" ]; then
|
|
259
|
+
return 1
|
|
260
|
+
fi
|
|
261
|
+
i=$((i + 1))
|
|
262
|
+
done
|
|
263
|
+
return 1
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
commit_phrase() {
|
|
267
|
+
if [ "$1" = 1 ]; then
|
|
268
|
+
printf '1 commit\n'
|
|
269
|
+
else
|
|
270
|
+
printf '%s commits\n' "$1"
|
|
271
|
+
fi
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
# --- watched tool registry --------------------------------------------------
|
|
275
|
+
|
|
276
|
+
CONFIG_PROBLEM=
|
|
277
|
+
|
|
278
|
+
# jq can check that an announce_pattern is a non-empty single-line string, but
|
|
279
|
+
# only grep can say whether it compiles as an extended regular expression. A
|
|
280
|
+
# pattern grep refuses would silently disable that tool's update source, which is
|
|
281
|
+
# the exact failure this script exists to prevent.
|
|
282
|
+
announce_pattern_usable() {
|
|
283
|
+
local pattern=$1 status
|
|
284
|
+
printf '%s' '' | grep -qE -- "$pattern" 2>/dev/null
|
|
285
|
+
status=$?
|
|
286
|
+
[ "$status" -le 1 ]
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
# Deliberately separate from config_validate, and asked only by arm. Arming is a
|
|
290
|
+
# deliberate operator action that should fail loudly, but a sweep must not treat
|
|
291
|
+
# one tool's unusable pattern as a reason to stop watching every other tool: that
|
|
292
|
+
# would let a one character typo turn the PATH skew detector off. So `check`
|
|
293
|
+
# reports this per tool instead, in command_findings.
|
|
294
|
+
config_announce_patterns_usable() {
|
|
295
|
+
local name announce
|
|
296
|
+
while IFS=$FIELD_SEP read -r name _ _ announce _; do
|
|
297
|
+
[ -n "$announce" ] || continue
|
|
298
|
+
if ! announce_pattern_usable "$announce"; then
|
|
299
|
+
CONFIG_PROBLEM="tool $name announce_pattern is not a usable extended regular expression"
|
|
300
|
+
return 1
|
|
301
|
+
fi
|
|
302
|
+
done < <(config_records)
|
|
303
|
+
return 0
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
config_validate() {
|
|
307
|
+
local problem status
|
|
308
|
+
if ! command -v jq >/dev/null 2>&1; then
|
|
309
|
+
CONFIG_PROBLEM='jq is required to read the watched tool registry'
|
|
310
|
+
return 1
|
|
311
|
+
fi
|
|
312
|
+
problem=$(jq -r '
|
|
313
|
+
def tool_problem($t):
|
|
314
|
+
if ($t | type) != "object" then "every entry in tools must be an object"
|
|
315
|
+
elif ($t.name | type) != "string" or ($t.name | length) == 0 then "every tool needs a non-empty name"
|
|
316
|
+
elif ($t.name | test("^[A-Za-z0-9._+-]+$") | not) then "tool name \($t.name) may use only letters, digits, dot, underscore, plus, and dash"
|
|
317
|
+
elif ($t | has("command") | not) and ($t | has("git") | not) then "tool \($t.name) needs command, git, or both"
|
|
318
|
+
elif ($t | has("command")) and (($t.command | type) != "string" or ($t.command | test("^[A-Za-z0-9._+-]+$") | not)) then "tool \($t.name) command must be a bare executable name"
|
|
319
|
+
elif ($t | has("version_args")) and (($t.version_args | type) != "array" or ($t.version_args | length) == 0) then "tool \($t.name) version_args must be a non-empty array"
|
|
320
|
+
elif ($t | has("version_args")) and ([$t.version_args[] | select((type != "string") or (test("^[A-Za-z0-9._=+/:-]+$") | not))] | length) > 0 then "tool \($t.name) version_args must be simple flag strings without spaces"
|
|
321
|
+
elif ($t | has("announce_pattern")) and (($t.announce_pattern | type) != "string" or ($t.announce_pattern | length) == 0 or ($t.announce_pattern | test("[[:cntrl:]]"))) then "tool \($t.name) announce_pattern must be a non-empty single-line string"
|
|
322
|
+
elif ($t | has("announce_pattern")) and (($t | has("command")) | not) then "tool \($t.name) announce_pattern needs command"
|
|
323
|
+
elif ($t | has("announce_args")) and (($t.announce_args | type) != "array" or ($t.announce_args | length) == 0) then "tool \($t.name) announce_args must be a non-empty array"
|
|
324
|
+
elif ($t | has("announce_args")) and ([$t.announce_args[] | select((type != "string") or (test("^[A-Za-z0-9._=+/:-]+$") | not))] | length) > 0 then "tool \($t.name) announce_args must be simple flag strings without spaces"
|
|
325
|
+
elif ($t | has("announce_args")) and (($t | has("announce_pattern")) | not) then "tool \($t.name) announce_args needs announce_pattern"
|
|
326
|
+
elif ($t | has("git")) and (($t.git | type) != "object") then "tool \($t.name) git must be an object"
|
|
327
|
+
elif ($t | has("git")) and (($t.git.repo | type) != "string" or ($t.git.repo | startswith("/") | not) or ($t.git.repo | test("[[:cntrl:]]"))) then "tool \($t.name) git.repo must be an absolute path on one line"
|
|
328
|
+
elif ($t | has("git")) and ($t.git | has("remote")) and (($t.git.remote | type) != "string" or ($t.git.remote | test("^[A-Za-z0-9._-]+$") | not)) then "tool \($t.name) git.remote must be a simple remote name"
|
|
329
|
+
elif ($t | has("git")) and ($t.git | has("branch")) and (($t.git.branch | type) != "string" or ($t.git.branch | test("^[A-Za-z0-9._/-]+$") | not)) then "tool \($t.name) git.branch must be a simple branch name"
|
|
330
|
+
else empty
|
|
331
|
+
end;
|
|
332
|
+
def problems:
|
|
333
|
+
if type != "object" then ["the top level must be an object"]
|
|
334
|
+
elif (.tools | type) != "array" then ["tools must be an array"]
|
|
335
|
+
elif (.tools | length) == 0 then ["tools must list at least one tool"]
|
|
336
|
+
else
|
|
337
|
+
[.tools[] | tool_problem(.)]
|
|
338
|
+
+ (if ([.tools[].name] | unique | length) != (.tools | length) then ["tool names must be unique"] else [] end)
|
|
339
|
+
end;
|
|
340
|
+
problems | .[0] // "ok"
|
|
341
|
+
' "$CONFIG" 2>/dev/null)
|
|
342
|
+
status=$?
|
|
343
|
+
if [ "$status" -ne 0 ] || [ -z "$problem" ]; then
|
|
344
|
+
CONFIG_PROBLEM='the watched tool registry is not valid JSON'
|
|
345
|
+
return 1
|
|
346
|
+
fi
|
|
347
|
+
if [ "$problem" != ok ]; then
|
|
348
|
+
CONFIG_PROBLEM=$problem
|
|
349
|
+
return 1
|
|
350
|
+
fi
|
|
351
|
+
CONFIG_PROBLEM=
|
|
352
|
+
return 0
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
# One record per tool, in config order. Fields are joined with the unit
|
|
356
|
+
# separator rather than a tab, because tab is IFS whitespace and `read` would
|
|
357
|
+
# collapse the empty fields that an optional key leaves behind.
|
|
358
|
+
FIELD_SEP=$(printf '\037')
|
|
359
|
+
|
|
360
|
+
config_records() {
|
|
361
|
+
jq -r '
|
|
362
|
+
.tools[] | [
|
|
363
|
+
.name,
|
|
364
|
+
(.command // ""),
|
|
365
|
+
((.version_args // ["--version"]) | join(" ")),
|
|
366
|
+
(.announce_pattern // ""),
|
|
367
|
+
((.announce_args // .version_args // ["--version"]) | join(" ")),
|
|
368
|
+
(.git.repo // ""),
|
|
369
|
+
(.git.remote // "origin"),
|
|
370
|
+
(.git.branch // "")
|
|
371
|
+
] | join("\u001f")
|
|
372
|
+
' "$CONFIG" 2>/dev/null
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
# --- PATH probes ------------------------------------------------------------
|
|
376
|
+
|
|
377
|
+
# Every executable copy of <command> on PATH, in PATH order, deduplicated by
|
|
378
|
+
# device and inode so one copy reached through two PATH entries is not read as
|
|
379
|
+
# two installs.
|
|
380
|
+
path_hits() {
|
|
381
|
+
local command_name=$1 dir candidate identity seen=''
|
|
382
|
+
while IFS= read -r dir; do
|
|
383
|
+
[ -n "$dir" ] || continue
|
|
384
|
+
candidate="$dir/$command_name"
|
|
385
|
+
[ -f "$candidate" ] && [ -x "$candidate" ] || continue
|
|
386
|
+
identity=$(fm_pr_file_identity "$candidate" 2>/dev/null) || identity=
|
|
387
|
+
[ -n "$identity" ] || identity=$candidate
|
|
388
|
+
case " $seen " in
|
|
389
|
+
*" $identity "*) continue ;;
|
|
390
|
+
esac
|
|
391
|
+
seen="$seen $identity"
|
|
392
|
+
printf '%s\n' "$candidate"
|
|
393
|
+
done < <(printf '%s\n' "$PATH" | tr ':' '\n')
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
# Ask one copy for its own version. Combined output, because tools answer on
|
|
397
|
+
# either stream, and no-mistakes announces its update on stderr.
|
|
398
|
+
probe_output() {
|
|
399
|
+
local path=$1
|
|
400
|
+
shift
|
|
401
|
+
fm_run_timed "$(probe_bound)" "$path" "$@" 2>&1
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
command_findings() {
|
|
405
|
+
local name=$1 command_name=$2 args_joined=$3 announce=$4 announce_args=$5
|
|
406
|
+
local hit out version matched announce_out status
|
|
407
|
+
local resolved_path='' resolved_version='' resolved_out=''
|
|
408
|
+
local best_path='' best_version='' unreadable='' hits=''
|
|
409
|
+
|
|
410
|
+
# This tool's announcement source is dead if its pattern cannot be used, which
|
|
411
|
+
# is reported here, for this tool alone, so the rest of the sweep still runs.
|
|
412
|
+
if [ -n "$announce" ] && ! announce_pattern_usable "$announce"; then
|
|
413
|
+
emit "$name check failed: announce_pattern is not a usable extended regular expression"
|
|
414
|
+
announce=
|
|
415
|
+
fi
|
|
416
|
+
|
|
417
|
+
hits=$(path_hits "$command_name")
|
|
418
|
+
if [ -z "$hits" ]; then
|
|
419
|
+
emit "$name check failed: $command_name is not on PATH"
|
|
420
|
+
return 0
|
|
421
|
+
fi
|
|
422
|
+
|
|
423
|
+
while IFS= read -r hit; do
|
|
424
|
+
[ -n "$hit" ] || continue
|
|
425
|
+
if budget_exhausted; then
|
|
426
|
+
emit "$name check failed: the time budget ran out before every copy answered"
|
|
427
|
+
break
|
|
428
|
+
fi
|
|
429
|
+
# shellcheck disable=SC2086 # deliberate split on validated space-free tokens
|
|
430
|
+
out=$(probe_output "$hit" $args_joined)
|
|
431
|
+
version=$(parse_version "$out")
|
|
432
|
+
if [ -z "$resolved_path" ]; then
|
|
433
|
+
resolved_path=$hit
|
|
434
|
+
resolved_version=$version
|
|
435
|
+
resolved_out=$out
|
|
436
|
+
fi
|
|
437
|
+
if [ -z "$version" ]; then
|
|
438
|
+
[ -n "$unreadable" ] || unreadable=$hit
|
|
439
|
+
continue
|
|
440
|
+
fi
|
|
441
|
+
if [ -z "$best_version" ] || version_newer "$version" "$best_version"; then
|
|
442
|
+
best_version=$version
|
|
443
|
+
best_path=$hit
|
|
444
|
+
fi
|
|
445
|
+
done <<EOF
|
|
446
|
+
$hits
|
|
447
|
+
EOF
|
|
448
|
+
|
|
449
|
+
if [ -n "$announce" ] && [ -n "$resolved_path" ]; then
|
|
450
|
+
# A tool does not have to announce its update on the command that reports its
|
|
451
|
+
# version: no-mistakes prints its version for --version but announces a new
|
|
452
|
+
# release on its other commands. So announce_args may name a second command,
|
|
453
|
+
# and it is asked of the copy PATH actually resolves.
|
|
454
|
+
announce_out=$resolved_out
|
|
455
|
+
if [ "$announce_args" != "$args_joined" ]; then
|
|
456
|
+
if budget_exhausted; then
|
|
457
|
+
# The version probe's output cannot carry the announcement, so searching
|
|
458
|
+
# it would present a source that was never asked as a clean result.
|
|
459
|
+
emit "$name check failed: the time budget ran out before the update announcement was checked"
|
|
460
|
+
announce_out=
|
|
461
|
+
else
|
|
462
|
+
# shellcheck disable=SC2086 # deliberate split on validated space-free tokens
|
|
463
|
+
announce_out=$(probe_output "$resolved_path" $announce_args)
|
|
464
|
+
status=$?
|
|
465
|
+
if [ "$status" -eq 124 ]; then
|
|
466
|
+
# A source that was asked and never answered is not a source that had
|
|
467
|
+
# nothing to say. The one that answers with nothing stays silent below.
|
|
468
|
+
emit "$name check failed: $resolved_path did not answer when asked for its update announcement"
|
|
469
|
+
announce_out=
|
|
470
|
+
fi
|
|
471
|
+
fi
|
|
472
|
+
fi
|
|
473
|
+
if [ -n "$announce_out" ]; then
|
|
474
|
+
# Not a pipeline, so grep's own status is still readable here: a pattern
|
|
475
|
+
# grep cannot use is a check failure, never read as nothing to announce.
|
|
476
|
+
matched=$(grep -oE -- "$announce" <<< "$announce_out" 2>/dev/null)
|
|
477
|
+
status=$?
|
|
478
|
+
if [ "$status" -gt 1 ]; then
|
|
479
|
+
emit "$name check failed: announce_pattern is not a usable extended regular expression"
|
|
480
|
+
elif [ -n "$matched" ]; then
|
|
481
|
+
emit "$name update available: $(printf '%s\n' "$matched" | head -n 1)"
|
|
482
|
+
fi
|
|
483
|
+
fi
|
|
484
|
+
fi
|
|
485
|
+
|
|
486
|
+
if [ -z "$resolved_version" ]; then
|
|
487
|
+
# No copy was probed at all when the path is empty, and the budget report
|
|
488
|
+
# already covers that, so do not blame a copy that was never asked.
|
|
489
|
+
[ -z "$resolved_path" ] || emit "$name check failed: $resolved_path did not report a version"
|
|
490
|
+
return 0
|
|
491
|
+
fi
|
|
492
|
+
|
|
493
|
+
if [ -n "$best_version" ] && [ "$best_path" != "$resolved_path" ] \
|
|
494
|
+
&& version_newer "$best_version" "$resolved_version"; then
|
|
495
|
+
emit "$name update not in effect: PATH resolves $resolved_version at $resolved_path but $best_version is installed at $best_path"
|
|
496
|
+
fi
|
|
497
|
+
|
|
498
|
+
if [ -n "$unreadable" ]; then
|
|
499
|
+
emit "$name check failed: $unreadable did not report a version"
|
|
500
|
+
fi
|
|
501
|
+
return 0
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
# --- git probes -------------------------------------------------------------
|
|
505
|
+
|
|
506
|
+
# A probe the sweep budget can no longer afford is never issued, and says so with
|
|
507
|
+
# a status of its own rather than a git status, so no caller can read it as an
|
|
508
|
+
# answer. Neither git nor the bounded runner uses this value.
|
|
509
|
+
GIT_PROBE_NOT_ISSUED=3
|
|
510
|
+
|
|
511
|
+
# One bounded read-only git probe. The budget check lives here rather than in the
|
|
512
|
+
# callers, so no probe can be issued past the sweep deadline whatever a caller
|
|
513
|
+
# does, and the budget only has to leave room for the one probe that was already
|
|
514
|
+
# running when the deadline passed.
|
|
515
|
+
git_probe() {
|
|
516
|
+
local repo=$1
|
|
517
|
+
shift
|
|
518
|
+
budget_exhausted && return "$GIT_PROBE_NOT_ISSUED"
|
|
519
|
+
fm_run_timed "$(probe_bound)" git -C "$repo" "$@"
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
# The single place that reads a probe status as no answer at all, so every probe
|
|
523
|
+
# reports an unanswered read the same way instead of taking it for the answer no.
|
|
524
|
+
git_probe_answered() {
|
|
525
|
+
local status=$1 name=$2 subject=$3 question=$4
|
|
526
|
+
case "$status" in
|
|
527
|
+
"$GIT_PROBE_NOT_ISSUED")
|
|
528
|
+
emit "$name check failed: the time budget ran out before $subject was asked $question"
|
|
529
|
+
return 1
|
|
530
|
+
;;
|
|
531
|
+
124)
|
|
532
|
+
emit "$name check failed: $subject did not answer $question"
|
|
533
|
+
return 1
|
|
534
|
+
;;
|
|
535
|
+
esac
|
|
536
|
+
return 0
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
# Read-only throughout: nothing here writes to the watched repository. This is the
|
|
540
|
+
# one tool kind that issues several probes in a row, two of them over the network,
|
|
541
|
+
# and each of them goes through git_probe, which owns both the bound and the
|
|
542
|
+
# budget check, so the sweep cannot outrun its deadline here.
|
|
543
|
+
git_findings() {
|
|
544
|
+
local name=$1 repo=$2 remote=$3 branch=$4
|
|
545
|
+
local status remote_sha local_sha local_label count short symref
|
|
546
|
+
|
|
547
|
+
if ! command -v git >/dev/null 2>&1; then
|
|
548
|
+
emit "$name check failed: git is not installed"
|
|
549
|
+
return 0
|
|
550
|
+
fi
|
|
551
|
+
if [ ! -d "$repo" ]; then
|
|
552
|
+
emit "$name check failed: $repo is not a directory"
|
|
553
|
+
return 0
|
|
554
|
+
fi
|
|
555
|
+
budget_allows "$name" || return 0
|
|
556
|
+
git_probe "$repo" rev-parse --git-dir >/dev/null 2>&1
|
|
557
|
+
status=$?
|
|
558
|
+
git_probe_answered "$status" "$name" "$repo" "whether it is a git repository" || return 0
|
|
559
|
+
if [ "$status" -ne 0 ]; then
|
|
560
|
+
emit "$name check failed: $repo is not a git repository"
|
|
561
|
+
return 0
|
|
562
|
+
fi
|
|
563
|
+
|
|
564
|
+
if [ -z "$branch" ]; then
|
|
565
|
+
branch=$(git_probe "$repo" symbolic-ref --short "refs/remotes/$remote/HEAD" 2>/dev/null)
|
|
566
|
+
git_probe_answered "$?" "$name" "$repo" "which branch it records for $remote" || return 0
|
|
567
|
+
branch=${branch#"$remote/"}
|
|
568
|
+
fi
|
|
569
|
+
if [ -z "$branch" ]; then
|
|
570
|
+
# A clone made with --single-branch, or one that never ran remote set-head,
|
|
571
|
+
# has no local record of the remote's default branch. Ask the remote itself
|
|
572
|
+
# rather than reporting a check failure the operator cannot act on.
|
|
573
|
+
symref=$(git_probe "$repo" ls-remote --symref "$remote" HEAD 2>/dev/null)
|
|
574
|
+
git_probe_answered "$?" "$name" "$remote" "which branch it uses by default" || return 0
|
|
575
|
+
branch=$(printf '%s\n' "$symref" \
|
|
576
|
+
| awk '$1 == "ref:" { sub(/^refs\/heads\//, "", $2); print $2; exit }')
|
|
577
|
+
fi
|
|
578
|
+
if [ -z "$branch" ]; then
|
|
579
|
+
emit "$name check failed: cannot resolve the default branch of $remote in $repo"
|
|
580
|
+
return 0
|
|
581
|
+
fi
|
|
582
|
+
|
|
583
|
+
remote_sha=$(git_probe "$repo" ls-remote "$remote" "refs/heads/$branch" 2>/dev/null)
|
|
584
|
+
status=$?
|
|
585
|
+
git_probe_answered "$status" "$name" "$remote" "where $branch points" || return 0
|
|
586
|
+
if [ "$status" -ne 0 ]; then
|
|
587
|
+
# The probe itself failed, so nothing at all is known about the branch. An
|
|
588
|
+
# offline host and a deleted branch are different problems, and reporting a
|
|
589
|
+
# missing branch here would name a cause that was never established.
|
|
590
|
+
emit "$name check failed: $remote could not be reached or read from $repo"
|
|
591
|
+
return 0
|
|
592
|
+
fi
|
|
593
|
+
remote_sha=$(printf '%s\n' "$remote_sha" | awk 'NR == 1 { print $1 }')
|
|
594
|
+
if [ -z "$remote_sha" ]; then
|
|
595
|
+
emit "$name check failed: $remote has no branch $branch"
|
|
596
|
+
return 0
|
|
597
|
+
fi
|
|
598
|
+
|
|
599
|
+
# Each probe below is bounded, so a non-zero status means either the answer no
|
|
600
|
+
# or no answer at all. They are kept apart: reading a bound that was hit as an
|
|
601
|
+
# answer would report an update this check never established.
|
|
602
|
+
local_sha=$(git_probe "$repo" rev-parse --verify --quiet "refs/heads/$branch" 2>/dev/null)
|
|
603
|
+
git_probe_answered "$?" "$name" "$repo" "where $branch points" || return 0
|
|
604
|
+
if [ -n "$local_sha" ]; then
|
|
605
|
+
local_label="local $branch"
|
|
606
|
+
else
|
|
607
|
+
local_sha=$(git_probe "$repo" rev-parse --verify --quiet HEAD 2>/dev/null)
|
|
608
|
+
git_probe_answered "$?" "$name" "$repo" "where HEAD points" || return 0
|
|
609
|
+
if [ -z "$local_sha" ]; then
|
|
610
|
+
emit "$name check failed: $repo has no commit to compare"
|
|
611
|
+
return 0
|
|
612
|
+
fi
|
|
613
|
+
local_label='local HEAD'
|
|
614
|
+
fi
|
|
615
|
+
|
|
616
|
+
[ "$local_sha" != "$remote_sha" ] || return 0
|
|
617
|
+
|
|
618
|
+
short=$(printf '%s' "$remote_sha" | cut -c1-12)
|
|
619
|
+
|
|
620
|
+
git_probe "$repo" cat-file -e "$remote_sha^{commit}" 2>/dev/null
|
|
621
|
+
status=$?
|
|
622
|
+
git_probe_answered "$status" "$name" "$repo" "whether it already has $short" || return 0
|
|
623
|
+
if [ "$status" -eq 0 ]; then
|
|
624
|
+
# The local copy may be ahead of, or diverged from, the remote branch; only
|
|
625
|
+
# commits it does not have yet are an available update.
|
|
626
|
+
git_probe "$repo" merge-base --is-ancestor "$remote_sha" "$local_sha" 2>/dev/null
|
|
627
|
+
status=$?
|
|
628
|
+
git_probe_answered "$status" "$name" "$repo" "how its history compares with $remote/$branch" || return 0
|
|
629
|
+
[ "$status" -ne 0 ] || return 0
|
|
630
|
+
count=$(git_probe "$repo" rev-list --count "$local_sha..$remote_sha" 2>/dev/null)
|
|
631
|
+
git_probe_answered "$?" "$name" "$repo" "how many commits it is behind $remote/$branch" || return 0
|
|
632
|
+
case "$count" in
|
|
633
|
+
''|*[!0-9]*|0) count= ;;
|
|
634
|
+
esac
|
|
635
|
+
if [ -n "$count" ]; then
|
|
636
|
+
emit "$name update available: $local_label is $(commit_phrase "$count") behind $remote/$branch"
|
|
637
|
+
return 0
|
|
638
|
+
fi
|
|
639
|
+
fi
|
|
640
|
+
|
|
641
|
+
emit "$name update available: $remote/$branch is at $short which this copy does not have"
|
|
642
|
+
return 0
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
# --- report record ----------------------------------------------------------
|
|
646
|
+
|
|
647
|
+
RECORD_EPOCH=0
|
|
648
|
+
RECORD_REPORTED=
|
|
649
|
+
|
|
650
|
+
record_read() {
|
|
651
|
+
local line first=1
|
|
652
|
+
RECORD_EPOCH=0
|
|
653
|
+
RECORD_REPORTED=
|
|
654
|
+
[ -f "$RECORD" ] || return 0
|
|
655
|
+
while IFS= read -r line; do
|
|
656
|
+
if [ "$first" = 1 ]; then
|
|
657
|
+
first=0
|
|
658
|
+
[ "$line" = "$RECORD_SCHEMA" ] || return 0
|
|
659
|
+
continue
|
|
660
|
+
fi
|
|
661
|
+
case "$line" in
|
|
662
|
+
epoch=*)
|
|
663
|
+
line=${line#epoch=}
|
|
664
|
+
case "$line" in
|
|
665
|
+
''|*[!0-9]*) RECORD_EPOCH=0 ;;
|
|
666
|
+
*) RECORD_EPOCH=$line ;;
|
|
667
|
+
esac
|
|
668
|
+
;;
|
|
669
|
+
reported=*) RECORD_REPORTED=${line#reported=} ;;
|
|
670
|
+
esac
|
|
671
|
+
done < "$RECORD"
|
|
672
|
+
return 0
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
record_write() {
|
|
676
|
+
local reported=$1 tmp
|
|
677
|
+
tmp=$(mktemp "$RECORD.XXXXXX" 2>/dev/null) || return 1
|
|
678
|
+
chmod 0600 "$tmp" 2>/dev/null || { rm -f -- "$tmp"; return 1; }
|
|
679
|
+
{
|
|
680
|
+
printf '%s\n' "$RECORD_SCHEMA"
|
|
681
|
+
printf 'epoch=%s\n' "$(record_epoch_now)"
|
|
682
|
+
printf 'reported=%s\n' "$reported"
|
|
683
|
+
} > "$tmp" || { rm -f -- "$tmp"; return 1; }
|
|
684
|
+
mv -f -- "$tmp" "$RECORD" || { rm -f -- "$tmp"; return 1; }
|
|
685
|
+
return 0
|
|
686
|
+
}
|
|
687
|
+
|
|
688
|
+
# --- actions ----------------------------------------------------------------
|
|
689
|
+
|
|
690
|
+
action_check() {
|
|
691
|
+
local name command_name args_joined announce announce_args repo remote branch
|
|
692
|
+
local line now
|
|
693
|
+
|
|
694
|
+
[ -f "$CONFIG" ] || return 0
|
|
695
|
+
|
|
696
|
+
record_read
|
|
697
|
+
now=$(record_epoch_now)
|
|
698
|
+
if [ "$INTERVAL" -ne 0 ] && [ "$RECORD_EPOCH" -gt 0 ] \
|
|
699
|
+
&& [ "$now" -ge "$RECORD_EPOCH" ] && [ $((now - RECORD_EPOCH)) -lt "$INTERVAL" ]; then
|
|
700
|
+
return 0
|
|
701
|
+
fi
|
|
702
|
+
|
|
703
|
+
DEADLINE=$(($(real_epoch) + BUDGET_SECS))
|
|
704
|
+
|
|
705
|
+
if [ -n "$BUDGET_CUT_FROM" ]; then
|
|
706
|
+
emit "sweep budget ${BUDGET_CUT_FROM}s cut to ${BUDGET_SECS}s to stay inside the watcher check timeout of ${CHECK_TIMEOUT}s"
|
|
707
|
+
fi
|
|
708
|
+
|
|
709
|
+
if ! config_validate; then
|
|
710
|
+
emit "watched tool registry: $CONFIG_PROBLEM"
|
|
711
|
+
else
|
|
712
|
+
while IFS=$FIELD_SEP read -r name command_name args_joined announce announce_args repo remote branch; do
|
|
713
|
+
[ -n "$name" ] || continue
|
|
714
|
+
budget_allows "$name" || break
|
|
715
|
+
[ -z "$command_name" ] || command_findings "$name" "$command_name" "$args_joined" "$announce" "$announce_args"
|
|
716
|
+
[ -z "$repo" ] || git_findings "$name" "$repo" "$remote" "$branch"
|
|
717
|
+
done < <(config_records)
|
|
718
|
+
fi
|
|
719
|
+
|
|
720
|
+
line=
|
|
721
|
+
if [ -n "$FINDINGS" ]; then
|
|
722
|
+
# Capped through the shared cut so an over-long report carries the same
|
|
723
|
+
# visible truncation marker the digests use, instead of ending mid-finding
|
|
724
|
+
# as if that were all of it.
|
|
725
|
+
fm_cap_line_var "tool updates: $FINDINGS" "$MAX_LINE"
|
|
726
|
+
line=$FM_LINE_CAP_LINE
|
|
727
|
+
fi
|
|
728
|
+
|
|
729
|
+
# The cut line is what gets printed, but the whole finding set is what decides
|
|
730
|
+
# whether this is news, because a finding that lands past the cut leaves the
|
|
731
|
+
# printed line unchanged and would otherwise be suppressed for good.
|
|
732
|
+
#
|
|
733
|
+
# Report before recording, so a record that cannot be written costs a repeated
|
|
734
|
+
# report rather than a lost one.
|
|
735
|
+
if [ -n "$line" ] && [ "$FINDINGS" != "$RECORD_REPORTED" ]; then
|
|
736
|
+
printf '%s\n' "$line"
|
|
737
|
+
fi
|
|
738
|
+
record_write "$FINDINGS" || true
|
|
739
|
+
return 0
|
|
740
|
+
}
|
|
741
|
+
|
|
742
|
+
# The home is embedded already resolved, because the watcher runs the shim from
|
|
743
|
+
# its own working directory and a relative spelling would send the check to a
|
|
744
|
+
# different home, or to none at all.
|
|
745
|
+
shim_content() {
|
|
746
|
+
local home=$1
|
|
747
|
+
printf '%s\n' \
|
|
748
|
+
'#!/usr/bin/env bash' \
|
|
749
|
+
'# Auto-generated by fm-tool-update-check.sh - watched tool update poll shim.' \
|
|
750
|
+
'# The watcher validates these bytes, then dispatches the trusted check script.' \
|
|
751
|
+
"export FM_HOME=$(printf '%q' "$home")" \
|
|
752
|
+
"exec $(printf '%q' "$SCRIPT_DIR/fm-tool-update-check.sh") check"
|
|
753
|
+
}
|
|
754
|
+
|
|
755
|
+
# Write the shim the way this repo writes its other trusted check shim: the
|
|
756
|
+
# guards run before anything is written, so a symlink at the shim path is
|
|
757
|
+
# refused instead of followed, and the bytes arrive by rename so the watcher
|
|
758
|
+
# never reads a half-written shim and rejects it as unauthenticated.
|
|
759
|
+
SHIM_WRITE_TMP=
|
|
760
|
+
|
|
761
|
+
shim_write() {
|
|
762
|
+
local want=$1 device tmp
|
|
763
|
+
[ -d "$STATE" ] && [ ! -L "$STATE" ] || return 1
|
|
764
|
+
device=$(fm_pr_file_device "$STATE") || return 1
|
|
765
|
+
[ -n "$device" ] || return 1
|
|
766
|
+
fm_pr_regular_destination_on_device_or_absent "$CHECK_SHIM" "$device" || return 1
|
|
767
|
+
if [ -e "$CHECK_SHIM" ] && [ "$(fm_pr_file_mode "$CHECK_SHIM")" = 700 ] \
|
|
768
|
+
&& [ "$(cat "$CHECK_SHIM" 2>/dev/null)" = "$want" ]; then
|
|
769
|
+
return 0
|
|
770
|
+
fi
|
|
771
|
+
tmp=$(umask 077; mktemp "$STATE/.fm-tool-updates-check.XXXXXX" 2>/dev/null) || return 1
|
|
772
|
+
SHIM_WRITE_TMP=$tmp
|
|
773
|
+
if ! printf '%s\n' "$want" > "$tmp" \
|
|
774
|
+
|| ! chmod 0700 "$tmp" \
|
|
775
|
+
|| ! fm_pr_private_file_valid "$tmp" 700 "$device"; then
|
|
776
|
+
rm -f -- "$tmp"
|
|
777
|
+
SHIM_WRITE_TMP=
|
|
778
|
+
return 1
|
|
779
|
+
fi
|
|
780
|
+
if ! fm_pr_regular_destination_on_device_or_absent "$CHECK_SHIM" "$device" \
|
|
781
|
+
|| ! mv -f -- "$tmp" "$CHECK_SHIM"; then
|
|
782
|
+
rm -f -- "$tmp"
|
|
783
|
+
SHIM_WRITE_TMP=
|
|
784
|
+
return 1
|
|
785
|
+
fi
|
|
786
|
+
SHIM_WRITE_TMP=
|
|
787
|
+
fm_pr_private_file_valid "$CHECK_SHIM" 700 "$device"
|
|
788
|
+
}
|
|
789
|
+
|
|
790
|
+
# Keep a byte copy of a shim that is already in place, so a failed arm can put
|
|
791
|
+
# back the shim a working home was already using rather than an equivalent
|
|
792
|
+
# rewrite. The trust binding is over the bytes, so a rewrite would satisfy it
|
|
793
|
+
# too, but a home that was armed stays armed with what it had.
|
|
794
|
+
shim_backup() {
|
|
795
|
+
local device tmp
|
|
796
|
+
device=$(fm_pr_file_device "$STATE") || return 1
|
|
797
|
+
[ -n "$device" ] || return 1
|
|
798
|
+
tmp=$(umask 077; mktemp "$STATE/.fm-tool-updates-check.XXXXXX" 2>/dev/null) || return 1
|
|
799
|
+
if ! cat "$CHECK_SHIM" > "$tmp" 2>/dev/null \
|
|
800
|
+
|| ! chmod 0700 "$tmp" \
|
|
801
|
+
|| ! fm_pr_private_file_valid "$tmp" 700 "$device"; then
|
|
802
|
+
rm -f -- "$tmp"
|
|
803
|
+
return 1
|
|
804
|
+
fi
|
|
805
|
+
printf '%s\n' "$tmp"
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
ARM_BACKUP=
|
|
809
|
+
|
|
810
|
+
# An unregistered shim is not inert: the watcher rejects it on every cycle and
|
|
811
|
+
# wakes firstmate about unauthenticated state checks. So the one rule after a
|
|
812
|
+
# failed or interrupted arm is that the home never holds a shim without a
|
|
813
|
+
# matching trust binding. The shim a working home had is put back and kept only
|
|
814
|
+
# when it is still bound; otherwise the shim goes, so the home is plainly not
|
|
815
|
+
# armed and the failure is the only thing the operator has to act on.
|
|
816
|
+
arm_rollback() {
|
|
817
|
+
[ -z "$SHIM_WRITE_TMP" ] || rm -f -- "$SHIM_WRITE_TMP"
|
|
818
|
+
SHIM_WRITE_TMP=
|
|
819
|
+
if [ -n "$ARM_BACKUP" ]; then
|
|
820
|
+
mv -f -- "$ARM_BACKUP" "$CHECK_SHIM" 2>/dev/null || rm -f -- "$ARM_BACKUP"
|
|
821
|
+
ARM_BACKUP=
|
|
822
|
+
if fm_custom_check_registered "$STATE" "$CHECK_ID"; then
|
|
823
|
+
return 0
|
|
824
|
+
fi
|
|
825
|
+
fi
|
|
826
|
+
rm -f -- "$CHECK_SHIM"
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
# shellcheck disable=SC2329 # Registered by action_arm's signal trap.
|
|
830
|
+
arm_interrupted() {
|
|
831
|
+
arm_rollback
|
|
832
|
+
printf 'fm-tool-update-check: arming was interrupted, so state/%s.check.sh is not armed\n' "$CHECK_ID" >&2
|
|
833
|
+
exit 1
|
|
834
|
+
}
|
|
835
|
+
|
|
836
|
+
action_arm() {
|
|
837
|
+
local want home
|
|
838
|
+
if [ ! -f "$CONFIG" ]; then
|
|
839
|
+
printf 'fm-tool-update-check: no watched tool registry at %s\n' "$CONFIG" >&2
|
|
840
|
+
return 1
|
|
841
|
+
fi
|
|
842
|
+
if ! config_validate || ! config_announce_patterns_usable; then
|
|
843
|
+
printf 'fm-tool-update-check: %s (%s)\n' "$CONFIG_PROBLEM" "$CONFIG" >&2
|
|
844
|
+
return 1
|
|
845
|
+
fi
|
|
846
|
+
mkdir -p "$STATE" || return 1
|
|
847
|
+
case "$FM_HOME" in
|
|
848
|
+
/*) home=$FM_HOME ;;
|
|
849
|
+
*)
|
|
850
|
+
home=$(CDPATH='' cd -- "$FM_HOME" 2>/dev/null && pwd -P) || {
|
|
851
|
+
printf 'fm-tool-update-check: cannot resolve FM_HOME %s\n' "$FM_HOME" >&2
|
|
852
|
+
return 1
|
|
853
|
+
}
|
|
854
|
+
;;
|
|
855
|
+
esac
|
|
856
|
+
want=$(shim_content "$home")
|
|
857
|
+
ARM_BACKUP=
|
|
858
|
+
if [ -f "$CHECK_SHIM" ] && [ ! -L "$CHECK_SHIM" ]; then
|
|
859
|
+
ARM_BACKUP=$(shim_backup) || {
|
|
860
|
+
printf 'fm-tool-update-check: could not save the existing %s\n' "$CHECK_SHIM" >&2
|
|
861
|
+
return 1
|
|
862
|
+
}
|
|
863
|
+
fi
|
|
864
|
+
# The shim exists unbound from the rename until the register returns, so a
|
|
865
|
+
# signal in that window rolls back the same way a failure does.
|
|
866
|
+
trap arm_interrupted HUP INT TERM
|
|
867
|
+
if ! shim_write "$want"; then
|
|
868
|
+
trap - HUP INT TERM
|
|
869
|
+
arm_rollback
|
|
870
|
+
printf 'fm-tool-update-check: could not write %s\n' "$CHECK_SHIM" >&2
|
|
871
|
+
return 1
|
|
872
|
+
fi
|
|
873
|
+
if ! FM_HOME="$home" "$REGISTER_BIN" "$CHECK_ID" >/dev/null; then
|
|
874
|
+
trap - HUP INT TERM
|
|
875
|
+
arm_rollback
|
|
876
|
+
printf 'fm-tool-update-check: could not register %s\n' "$CHECK_SHIM" >&2
|
|
877
|
+
return 1
|
|
878
|
+
fi
|
|
879
|
+
trap - HUP INT TERM
|
|
880
|
+
[ -z "$ARM_BACKUP" ] || rm -f -- "$ARM_BACKUP"
|
|
881
|
+
ARM_BACKUP=
|
|
882
|
+
printf 'armed: state/%s.check.sh\n' "$CHECK_ID"
|
|
883
|
+
return 0
|
|
884
|
+
}
|
|
885
|
+
|
|
886
|
+
action_disarm() {
|
|
887
|
+
rm -f -- "$CHECK_SHIM" "$CHECK_TRUST" "$RECORD"
|
|
888
|
+
printf 'disarmed: state/%s.check.sh\n' "$CHECK_ID"
|
|
889
|
+
return 0
|
|
890
|
+
}
|
|
891
|
+
|
|
892
|
+
case "${1:-check}" in
|
|
893
|
+
check) action_check ;;
|
|
894
|
+
arm) action_arm ;;
|
|
895
|
+
disarm) action_disarm ;;
|
|
896
|
+
-h|--help) usage ;;
|
|
897
|
+
*) die_usage "unknown action: $1" ;;
|
|
898
|
+
esac
|