diff --git a/.github/workflows/security-scan.yml b/.github/workflows/security-scan.yml index 137af1a..165e991 100644 --- a/.github/workflows/security-scan.yml +++ b/.github/workflows/security-scan.yml @@ -228,6 +228,19 @@ jobs: } >> "${GITHUB_STEP_SUMMARY}" exit 1 + fleet_self_service_tests: + name: Fleet Self-Service Tests + runs-on: macos-15 + steps: + - name: Check Out Repository + uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6 + + - name: Run Native Fleet Regression Tests + # Extracts helpers only; HTTP is mocked and no installations are requested. + run: | + /bin/zsh --no-rcs tests/fleet-self-service.zsh + /bin/zsh --no-rcs tests/fleet-transport.zsh + zsh_syntax: name: Zsh Syntax runs-on: ubuntu-latest @@ -445,6 +458,7 @@ jobs: - semgrep - gitleaks - zsh_syntax + - fleet_self_service_tests - shellcheck steps: - name: Security Scan Complete @@ -454,6 +468,7 @@ jobs: semgrep_result="${{ needs.semgrep.result }}" gitleaks_result="${{ needs.gitleaks.result }}" zsh_syntax_result="${{ needs.zsh_syntax.result }}" + fleet_tests_result="${{ needs.fleet_self_service_tests.result }}" shellcheck_result="${{ needs.shellcheck.result }}" badge() { @@ -468,22 +483,23 @@ jobs: semgrep_badge="$(badge "${semgrep_result}")" gitleaks_badge="$(badge "${gitleaks_result}")" zsh_syntax_badge="$(badge "${zsh_syntax_result}")" + fleet_tests_badge="$(badge "${fleet_tests_result}")" shellcheck_badge="$(badge "${shellcheck_result}")" { echo "## Security Scan Summary" echo - echo "Semgrep ${semgrep_badge} | Gitleaks ${gitleaks_badge} | Zsh Syntax ${zsh_syntax_badge} | ShellCheck ${shellcheck_badge}" + echo "Semgrep ${semgrep_badge} | Gitleaks ${gitleaks_badge} | Zsh Syntax ${zsh_syntax_badge} | Fleet Tests ${fleet_tests_badge} | ShellCheck ${shellcheck_badge}" echo - if [[ "${semgrep_result}" == "success" && "${gitleaks_result}" == "success" && "${zsh_syntax_result}" == "success" && "${shellcheck_result}" == "success" ]]; then + if [[ "${semgrep_result}" == "success" && "${gitleaks_result}" == "success" && "${zsh_syntax_result}" == "success" && "${fleet_tests_result}" == "success" && "${shellcheck_result}" == "success" ]]; then echo "🐉 Mac Admin Security Dragon Slain" else echo "⚠️ Security Scan requires attention" fi } >> "${GITHUB_STEP_SUMMARY}" - echo "Semgrep ${semgrep_badge} | Gitleaks ${gitleaks_badge} | Zsh Syntax ${zsh_syntax_badge} | ShellCheck ${shellcheck_badge}" - if [[ "${semgrep_result}" == "success" && "${gitleaks_result}" == "success" && "${zsh_syntax_result}" == "success" && "${shellcheck_result}" == "success" ]]; then + echo "Semgrep ${semgrep_badge} | Gitleaks ${gitleaks_badge} | Zsh Syntax ${zsh_syntax_badge} | Fleet Tests ${fleet_tests_badge} | ShellCheck ${shellcheck_badge}" + if [[ "${semgrep_result}" == "success" && "${gitleaks_result}" == "success" && "${zsh_syntax_result}" == "success" && "${fleet_tests_result}" == "success" && "${shellcheck_result}" == "success" ]]; then echo "🐉 Mac Admin Security Dragon Slain" exit 0 fi diff --git a/AGENTS.md b/AGENTS.md index 932075f..a58d1ed 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -7,12 +7,12 @@ Takes precedence over `README.md`, `.github/copilot-instructions.md`, and simila This file codifies project rules, boundaries, workflows, and repeatable skills. If same correction repeats, formalize it here instead of re-prompting it. ## Project Overview -`SYM-Lite` is macOS-only, root-run zsh workflow for executing approved software and management actions through one swiftDialog-driven experience. Primary artifact: `SYM-Lite.zsh`. Supported operation modes: **interactive** (default) and **silent**. Scope limited to Installomator labels, Jamf policy triggers, and approved Homebrew items. +`SYM-Lite` is macOS-only, root-run zsh workflow for executing approved software and management actions through one swiftDialog-driven experience. Primary artifact: `SYM-Lite.zsh`. Supported operation modes: **interactive** (default) and **silent**. Scope limited to Installomator labels, Jamf policy triggers, approved Homebrew items, and explicitly configured Fleet self-service software. ## Key Commands - Validate syntax after every Zsh edit: `zsh -n SYM-Lite.zsh` - Inspect current script version: `rg -n '^scriptVersion=' SYM-Lite.zsh` -- Inspect current item inventories: `rg -n '^(installomatorLabels|jamfPolicyItems|homebrewItems)=\\(' SYM-Lite.zsh` +- Inspect current item inventories: `rg -n '^(installomatorLabels|jamfPolicyItems|homebrewItems|fleetSoftwareItems)=\(' SYM-Lite.zsh` - Review canonical runtime docs before behavior edits: `sed -n '1,240p' AGENTS.md` ## Agent Workflow @@ -30,9 +30,9 @@ This file codifies project rules, boundaries, workflows, and repeatable skills. Invoke relevant skill name during planning. ### Add New Executable Item Skill -1. Start from matching array format in `installomatorLabels`, `jamfPolicyItems`, or `homebrewItems`. +1. Start from matching array format in `installomatorLabels`, `jamfPolicyItems`, `homebrewItems`, or `fleetSoftwareItems`. 2. Keep item list sorted by display-name intent because UI merges and sorts groups together. -3. Set real `validationPath`; skip logic and Inspect Mode completion depend on it. +3. Set real `validationPath`; skip logic and Inspect Mode completion depend on it. Fleet items may omit the path, particularly for script-only packages; those items must run each time selected and rely on Fleet's install result. 4. Validate affected parsing and execution flow in `SYM-Lite.zsh`. 5. Update `README.md` if user-visible configuration or behavior changed. @@ -58,14 +58,14 @@ Invoke relevant skill name during planning. **Ask before doing** - Add new production dependencies. -- Run commands that can install software, trigger Jamf policies, update Homebrew metadata, or otherwise mutate host state outside repo. +- Run commands that can install software, request Fleet installs, trigger Jamf policies, update Homebrew metadata, or otherwise mutate host state outside repo. - Change default operation mode, parameter semantics, logging contract, or restart behavior. - Rebuild release notes or prepare release versioning not explicitly requested. **Never do** - Hardcode secrets, tokens, org-private endpoints, or credentials. - Modify files outside current task scope without approval. -- Add arbitrary package workflows beyond Jamf triggers, Installomator labels, or explicitly configured Homebrew packages. +- Add arbitrary package workflows beyond Jamf triggers, Installomator labels, explicitly configured Homebrew packages, or explicitly configured Fleet self-service software. - Break macOS-only or root-run assumptions by accident. ## Source of Truth @@ -83,13 +83,14 @@ In scope: - Installomator label execution - Jamf policy trigger execution - approved Homebrew formula and cask execution +- explicitly configured Fleet self-service software execution, including custom packages and script-only packages - path-based validation, logging, completion reporting, and restart prompts Out of scope: - non-macOS support - non-root execution as primary runtime model - enrollment, inventory collection strategy, or broad device orchestration -- arbitrary package pipelines outside configured Jamf, Installomator, and Homebrew items +- arbitrary package pipelines outside configured Jamf, Installomator, Homebrew, and Fleet items ## Implementation Priorities 1. Preserve single-script architecture in `SYM-Lite.zsh`. @@ -108,18 +109,24 @@ Out of scope: ## Current Runtime Hotspots - `dialogCheck()` always runs in pre-flight and can auto-install or update swiftDialog from GitHub; blocked network breaks bootstrap. -- Installomator availability is optional for each run; missing or unparsable Installomator filters those labels instead of aborting Jamf or Homebrew execution. -- `validationPath` drives both pre-execution skip logic and Inspect Mode completion detection; wrong path can suppress needed work or hide completion. +- Installomator availability is optional for each run; missing or unparsable Installomator filters those labels instead of aborting Jamf, Homebrew, or Fleet execution. +- `validationPath` drives pre-execution skip logic and path-based completion detection; wrong paths can suppress needed work or hide completion. Fleet items with empty paths never pre-skip; a configured path also requires postvalidation after Fleet success. - Interactive mode needs active logged-in GUI user and exits after wait window if none appears. - Homebrew execution runs in logged-in user context even though script itself runs as root. - Jamf and Homebrew completion remain path-based, not rich progress parsed. +- Fleet support is opt-in and reads the rotating device token from Orbit's `identifier` file for each request. Never add API-user credentials or expose device tokens, raw API responses, or script output in logs or dialog files. +- Fleet request acceptance is not completion. Track the requested install through its outcome; a timeout ends SYM-Lite's wait without cancelling Fleet's operation. +- Fleet execution errors produce exit status `1` after the completion flow. Preserve explicit failure reasons and distinguish confirmed failures from unconfirmed results. +- In mixed Fleet/Installomator sessions, disable Installomator log auto-matching so a matching display name cannot falsely complete a Fleet row. +- Fleet catalog eligibility is checked during execution. Only Fleet-managed/custom/script packages are supported; App Store apps and self-service browser SSO require separate flows. +- Fleet delivery must finish before SYM-Lite waits on further Fleet installs; use a detached launch or separate job to avoid blocking the install queue. ## Repository Rules - Always run `zsh -n` after modifying Zsh files. - Do not add new production dependencies without explicit approval. - Keep durable repo rules near top of this file; avoid timestamps, counters, or ephemeral task notes in stable sections. - Preserve existing script style unless strong reason exists to refactor. -- Keep `installomatorLabels`, `jamfPolicyItems`, and `homebrewItems` sorted by display-name intent. +- Keep `installomatorLabels`, `jamfPolicyItems`, `homebrewItems`, and `fleetSoftwareItems` sorted by display-name intent. - If behavior, configuration semantics, or environment assumptions change, update `README.md` in same pass. - `CHANGELOG.md` is long-term history for released versions. - `SYM-Lite.zsh` `HISTORY` section should describe current version under development only. @@ -138,6 +145,7 @@ Match established `SYM-Lite.zsh` style unless user explicitly asks otherwise. - Installomator: `"label | Display Name | Validation Path | Icon URL"` - Jamf: `"trigger | Display Name | Validation Path | Icon URL"` - Homebrew: `"formula:token"` or `"cask:token"` item id with same four-field layout + - Fleet: `"fleet: | Display Name | Validation Path | Icon URL"`; a validation path is optional 8. Keep silent-mode parsing tolerant of operator input normalization when touching CSV or item ID logic. 9. Prefer degraded-but-continuable warnings over fatal exits unless workflow truly cannot proceed. 10. Keep user-facing strings concise and operator-friendly. @@ -150,10 +158,13 @@ Match established `SYM-Lite.zsh` style unless user explicitly asks otherwise. - Default Installomator path: `/Library/Application Support/AppAutoPatch/Installomator/Installomator.sh` - Default Jamf binary path: `/usr/local/bin/jamf` - Homebrew detection prefers `/opt/homebrew/bin/brew`, then `/usr/local/bin/brew` +- Default Fleet Orbit root: `/opt/orbit`; Fleet support starts disabled with no configured items and discovers the server URL unless `fleetURL` is set +- Fleet requires an enrolled agent, a usable device token, and software available to the device through self-service without browser SSO - Default logging paths: `/var/log/org.churchofjesuschrist.log` and `/var/log/Installomator.log` ## Quality Bar -- Keep pre-flight behavior reliable across missing GUI user, missing swiftDialog, missing Installomator, missing Jamf, and missing Homebrew cases. +- Keep pre-flight behavior reliable across missing GUI user, missing swiftDialog, missing Installomator, missing Jamf, missing Homebrew, and unavailable Fleet cases. +- Verify Fleet changes with mocked requests and responses. Do not request live installs as a test without explicit authorization. - Keep interactive UX valid: selection dialog, Inspect Mode JSON, command file, completion dialog, restart prompt. - Keep silent mode deterministic: parameter parsing, item lookup, skip logic, and completion reporting must stay clear. - Logging must remain structured and useful for operators. diff --git a/README.md b/README.md index d8e614c..18153a0 100644 --- a/README.md +++ b/README.md @@ -2,7 +2,7 @@ # SYM-Lite (1.2.0) -> **SYM-Lite** is a lean, purpose-built script for executing MDM-agnostic [Installomator labels](https://github.com/Installomator/Installomator/tree/main/fragments/labels) and [Homebrew](https://brew.sh) casks / formulas, as well as Jamf Pro-specific [policy triggers](https://learn.jamf.com/r/en-US/jamf-pro-documentation-current/Triggers_for_Policies), all through a unified [swiftDialog](https://swiftdialog.app) selection and reporting interface. +> **SYM-Lite** is a single macOS script for executing approved [Installomator labels](https://github.com/Installomator/Installomator/tree/main/fragments/labels), [Homebrew](https://brew.sh) casks / formulas, Jamf Pro [policy triggers](https://learn.jamf.com/r/en-US/jamf-pro-documentation-current/Triggers_for_Policies), and [Fleet self-service software installs](https://fleetdm.com/docs/rest-api/rest-api#install-self-service-software-by-fleet-desktop-token), all through a unified [swiftDialog](https://swiftdialog.app) selection and reporting interface. ## Screenshots @@ -35,13 +35,14 @@ ## Key Features -✓ **Unified execution support** — Installomator labels, Homebrew packages, and (optionally) Jamf Pro policies in a single session +✓ **Unified execution support** — Installomator labels, Homebrew packages, and optional Jamf Pro policies and Fleet software installs in a single session
✓ **Interactive selection UI** — User-friendly checkbox dialog with per-item icons; optional install-state labels disable already-installed items and exit cleanly when nothing remains selectable ✓ **Alphabetical sorting** — All items sorted together by display name in selection dialog ✓ **Silent mode** — CSV-based automation support ✓ **Early Installomator label validation** — Configured Installomator labels are verified against the active Installomator file before they can appear or run ✓ **Homebrew package support** — Approved casks and formulas run in the logged-in user context when `brew` is available -✓ **Inspect Mode monitoring** — Rich status updates for Installomator labels and path-based progress for Homebrew/Jamf items +✓ **Fleet software support** — Approved self-service custom packages and script-only packages use the Mac's Fleet Desktop token, with completion checked through Fleet
+✓ **Inspect Mode monitoring** — Rich status updates for Installomator labels, path-based progress for Homebrew/Jamf items, and Fleet install outcome monitoring
✓ **Log monitoring** — Parses Installomator.log for intermediate states and captures Homebrew/Jamf output into the main log ✓ **Path-based validation** — Pre/post-execution checks via file system monitoring ✓ **Cache monitoring** — Detects in-progress downloads @@ -142,6 +143,60 @@ When Homebrew items are disabled: - Homebrew pre-flight detection is skipped - Silent mode warns and skips Homebrew item IDs in the CSV input +### Adding Fleet Self-Service Software + +Enable Fleet support and edit `fleetSoftwareItems` near the top of `SYM-Lite.zsh`. Fleet support is disabled by default, and its item array starts empty. + +```zsh +enableFleetSoftwareItems="true" +fleetURL="" +fleetOrbitRoot="/opt/orbit" +fleetInstallTimeout="1800" +fleetPollInterval="5" + +fleetSoftwareItems=( + "fleet:123 | Configure Dock | | SF=dock.rectangle" + "fleet:456 | Internal App | /Applications/Internal App.app | SF=app" +) +``` + +Each entry has four fields: + +```text +fleet: | Display Name | Validation Path | Icon URL +``` + +Replace the example IDs with **Fleet software title IDs**, not installer IDs. Configure only approved items that Fleet offers to this Mac as self-service software. SYM-Lite verifies each selected title against the device's available catalog before requesting its installation. Fleet-managed apps, custom packages, and script-only packages are supported; App Store apps use a different result API and are not supported by this integration. + +- **Custom packages:** Use an application or marker path that proves the desired result. An existing path skips the install. A new install must finish successfully in Fleet and create the configured path to pass validation. +- **Script-only packages:** Leave the validation field empty to run the item each time it is selected. SYM-Lite waits for Fleet's result even when there is no app to watch. The Fleet package's script must return a nonzero exit code when its work fails. Supply a real marker path only when that marker should suppress future runs. + +Authentication uses the device token from the installed Fleet agent's `identifier` file under `fleetOrbitRoot`, as described in [Fleet's device-token example](https://fleetdm.com/scripts/macos-refetch-host). SYM-Lite rereads this file for each request because Orbit rotates the token. Do not configure an API-user token or copy the device token into the script. Fleet support uses macOS tools already present on the system and the installed Fleet agent; the Fleet Desktop app does not need to be running. + +Leave `fleetURL` empty to discover the server from `EnvironmentVariables.ORBIT_FLEET_URL` in `/Library/LaunchDaemons/com.fleetdm.orbit.plist`, falling back to `fleet_url.txt` under `fleetOrbitRoot`. Set it explicitly only when automatic discovery is unavailable. The URL must use HTTPS with a DNS hostname and optional port; path prefixes, credentials in the URL, and IPv6 literals are not supported. + +Pre-flight filters malformed Fleet IDs and disables Fleet items when local credentials or server configuration are unavailable. Other configured item types remain available. Catalog availability and title eligibility are checked during execution; an unavailable or out-of-scope title is reported as a failed item. Set `enableFleetSoftwareItems="false"` to disable Fleet pre-flight checks and execution. + +Fleet self-service SSO is not supported by this integration. When Fleet requires a browser SSO session for self-service installs, a device token alone cannot authorize the request. SYM-Lite reports access denied and asks the operator to check token readiness and self-service SSO requirements. + +#### Fleet Completion and Timeouts + +An accepted install request means Fleet queued the work; it does not mean installation succeeded. SYM-Lite identifies the new install attempt and polls its result every `fleetPollInterval` seconds. If the title already has a pending install, SYM-Lite waits for that attempt instead of queuing another. `fleetInstallTimeout` limits how long SYM-Lite waits for each selected item, including queue time. Both values must be integers: timeout accepts 1–86400 seconds and poll interval accepts 1–300 seconds. + +Fleet's [self-service install endpoint](https://fleetdm.com/docs/rest-api/rest-api#install-self-service-software-by-fleet-desktop-token) returns HTTP `202` without an install ID. SYM-Lite compares the title's last install before and after the request, then tracks the resulting install ID. It cannot distinguish simultaneous requests for the same title, so do not launch competing installs for that title from another SYM-Lite process, Fleet Desktop, or other automation. + +Fleet-confirmed failures appear in the completion report's **Not installed** group with a red **Failed** status. Timeouts and unconfirmed outcomes appear in **Needs review**, with **Timed out** or **Needs review** status and a reason, including a missing validation path after Fleet success. A timeout stops SYM-Lite's wait and does **not** cancel the Fleet operation; the package or script may run later. If a request outcome is unconfirmed, check Fleet before retrying. + +Any Fleet execution error makes SYM-Lite exit with status `1` after the normal completion flow. This applies in interactive and silent modes; other providers retain their existing exit behavior. + +Logs contain Fleet install status and attempt identifiers, but do not include raw API responses or package script output, which may contain sensitive values. + +#### Deployment During Onboarding + +The Fleet agent must be enrolled with a usable device token before SYM-Lite can run Fleet items. Interactive mode also requires a logged-in GUI user. This integration supplies a software execution option; a complete replacement for an enrollment workflow such as Baseline still needs delivery, first-login sequencing, and Fleet readiness handling. + +If Fleet installs SYM-Lite itself, launch SYM-Lite as a detached process or separate job and let its delivery installer finish before requesting other Fleet installs. Keeping the delivery installer open while SYM-Lite waits for work in the same Fleet install queue can block that work until SYM-Lite times out. + --- ## Usage @@ -178,13 +233,19 @@ Run with Jamf parameters or direct positional arguments: - Parameter 4: `silent` - Parameter 5: `androidstudio,appleXcode,cask:codex` -Parameter 5 must contain item identifiers exactly as they are defined in the configured item arrays. In this repo, that means values such as `androidstudio`, `appleXcode`, `homebrew`, `cask:1password-cli`, `cask:codex`, or `formula:direnv`, not a full Jamf command such as `jamf policy -event homebrew`. +Parameter 5 must contain item identifiers exactly as they are defined in the configured item arrays. In this repo, that means values such as `androidstudio`, `appleXcode`, `homebrew`, `cask:1password-cli`, `cask:codex`, or `formula:direnv`, not a full Jamf command such as `jamf policy -event homebrew`. Fleet entries use `fleet:` after they have been enabled and configured. **Direct execution:** ```bash sudo /path/to/SYM-Lite.zsh "" "" "" silent "androidstudio,appleXcode,cask:codex" ``` +**Fleet example, using the configured items above:** + +```bash +sudo /path/to/SYM-Lite.zsh "" "" "" silent "fleet:123,fleet:456" +``` + Silent mode also normalizes surrounding straight quotes and common smart quotes copied from rich-text sources, including when the entire CSV is wrapped once or when individual item IDs are quoted. That normalization is safe even when Jamf launches the script under a non-UTF shell locale. Plain comma-separated item IDs are still the recommended input format. If SYM-Lite reports an unknown item ID, compare Parameter 5 against the identifiers configured near the top of [SYM-Lite.zsh](SYM-Lite.zsh). For the current repo state, `googleChrome` is not a configured item ID, so silent mode will reject it until it is added to the appropriate item array. @@ -198,6 +259,8 @@ If SYM-Lite reports an unknown item ID, compare Parameter 5 against the identifi - Installomator labels filtered out during pre-flight validation are warned and skipped in the CSV input - If Jamf policy items are disabled, Jamf item IDs in the CSV are warned and skipped - If Homebrew items are disabled or unavailable for the current run, Homebrew item IDs in the CSV are warned and skipped +- If Fleet items are disabled or unavailable for the current run, Fleet item IDs in the CSV are warned and skipped +- A Fleet execution error produces exit status `1`; details are written to the main log - Exits with an error if the CSV contains no valid item IDs - Suitable for automated deployment @@ -216,6 +279,7 @@ If SYM-Lite reports an unknown item ID, compare Parameter 5 against the identifi - If the Installomator file is unavailable or cannot be parsed, Installomator labels are hidden and skipped for that run - **Homebrew Binary** — Required only when `enableHomebrewItems="true"` and Homebrew items are configured - **Jamf Pro Binary** — Required only when `enableJamfPolicyItems="true"` and Jamf policy items are configured +- **Fleet Agent / Orbit** — Required only when `enableFleetSoftwareItems="true"` and Fleet items are configured; the Mac must have a usable device token and access to the configured self-service software without browser SSO --- @@ -229,26 +293,28 @@ PRE-FLIGHT CHECKS ├─ Normalize Homebrew item availability and detect brew path ├─ Normalize Jamf item availability from configuration ├─ Verify Jamf binary (if enabled and items configured) + └─ Validate Fleet configuration, local device credentials, and item IDs (if enabled) ↓ SELECTION INTERFACE ├─ Show dialog (interactive) or parse CSV (silent) ├─ Validate at least one selection - └─ Separate items by type (Installomator → Homebrew → Jamf) + └─ Preserve selection order (display-name order in picker, CSV order in silent mode) ↓ INSPECT MODE CONFIGURATION ├─ Interactive mode only ├─ Build unified JSON config - ├─ Merge Installomator + Homebrew + Jamf items + ├─ Merge Installomator + Homebrew + Jamf + Fleet items ├─ Add cachePaths for download detection └─ Validate JSON with plutil ↓ EXECUTION ENGINE ├─ Interactive mode launches Inspect Mode dialog (background) │ └─ Silent mode logs progress without UI - ├─ Process items sequentially (Installomator → Homebrew → Jamf) + ├─ Process items sequentially in selection order │ ├─ Installomator: executeInstallomatorLabel() │ ├─ Homebrew: executeHomebrewItem() - │ └─ Jamf: executeJamfPolicy() + │ ├─ Jamf: executeJamfPolicy() + │ └─ Fleet: submit a self-service install and wait for its result ├─ Interactive mode waits for Inspect Mode to close └─ Silent mode exits when execution completes ↓ @@ -274,6 +340,8 @@ swiftDialog's [Inspect Mode](https://swiftdialog.app/advanced/inspect-mode/) use - Watches validation path via FSEvents API - Item marks complete when app appears at specified path +When Fleet and Installomator items share a session, Inspect Mode uses path monitoring for Installomator and disables automatic log matching. This prevents an Installomator log entry from completing a Fleet row with the same display name. Installomator log capture continues as usual. + ### For Homebrew Items (Binary Status) **File System Monitoring Only:** @@ -286,6 +354,13 @@ swiftDialog's [Inspect Mode](https://swiftdialog.app/advanced/inspect-mode/) use - Shows binary states: "Waiting" → "Completed" - Watches validation path (e.g., `/usr/bin/arch`) +### For Fleet Software (Install Outcome) + +- Waits for Fleet to report the requested install's result +- Does not treat request acceptance or an app appearing early as completed installation +- Requires the optional validation path to exist after a successful install +- Supports script-only items without a validation path + ### Common Features - `cachePaths` monitoring for in-progress downloads @@ -300,7 +375,7 @@ swiftDialog's [Inspect Mode](https://swiftdialog.app/advanced/inspect-mode/) use ### Installomator Items 1. Pre-check: If validation path exists → skip 2. Execute: `Installomator.sh