default: @just --list export FASTLANE_SKIP_UPDATE_CHECK := "1" # iOS simulator name override (default: an available iPhone, preferring a booted one) DEVICE := env("OMNIGENT_IOS_SIMULATOR", "") # --- uv Python env --- _check-uv: uv run --no-sync ruff --version uv run --no-sync pyrefly --version uv run --no-sync pre-commit --version _ensure-uv: uv sync --extra all --group dev # Installs the pre-commit and pre-push git hooks (see default_install_hook_types # in .pre-commit-config.yaml). Worktrees share the main checkout's .git, so one # install covers all of them. _ensure-hooks: _ensure-uv uv run --no-sync pre-commit install # --- iOS Ruby dependencies --- _check-ios: cd web/ios && bundle check _ensure-ios: cd web/ios && (bundle check || bundle install) # --- omnidev Rust dev tool --- _install-omnidev: cargo install --path dev/omnidev --locked --force _check-omnidev: command -v omnidev >/dev/null 2>&1 _ensure-omnidev: command -v omnidev >/dev/null 2>&1 || just _install-omnidev # --- Aggregate setup checks / installs --- [group('setup')] check: _check-uv _check-ios _check-omnidev [group('setup')] ensure: _ensure-uv _ensure-hooks _ensure-ios _ensure-omnidev # --- Local dev --- [group('dev')] dev: _ensure-omnidev omnidev [group('dev')] dev-mobile: _ensure-omnidev omnidev --vite-host 0.0.0.0 --trust-lan-origins [group('dev')] crdb-up: docker compose -f deploy/cockroachdb/docker-compose.yml up -d --wait --wait-timeout 90 docker compose -f deploy/cockroachdb/docker-compose.yml exec -T crdb-23-2-28 cockroach sql --insecure --execute="SET CLUSTER SETTING sql.txn.read_committed_isolation.enabled = true" [group('dev')] crdb-stop: docker compose -f deploy/cockroachdb/docker-compose.yml stop [group('dev')] crdb-test: crdb-up ./scripts/test_crdb_matrix.sh # Destructive: stops CRDB and deletes all four persistent development volumes. [group('dev')] crdb-reset: docker compose -f deploy/cockroachdb/docker-compose.yml down --volumes # --- Mobile builds --- [group('mobile')] run-ios: _ensure-ios cd web/ios && bundle exec fastlane simulator device:"{{ DEVICE }}" [group('mobile')] run-android: cd web/android && ./gradlew installDebug runDebug [group('mobile')] android-reverse: cd web/android && ./gradlew reverseProxy # --- Web --- _ensure-web: cd web && test -d node_modules || pnpm install [group('web')] storybook: _ensure-web pnpm --filter web run storybook [group('web')] storybook-build: _ensure-web pnpm --filter web run build:storybook [group('web')] generate-theme-palettes: _ensure-web cd web && node --experimental-strip-types scripts/generate-theme-palettes.mjs # --- Electron desktop app --- _ensure-electron: cd web/electron && test -d node_modules || pnpm install [group('electron')] electron-dev: _ensure-web _ensure-electron pnpm --filter ./web/electron run dev [group('electron')] electron-build: _ensure-web _ensure-electron pnpm --filter ./web/electron run build # Build (if needed) and launch the development package (reads dev-domain prefs). # Flags: --rebuild (force a fresh build even if one exists), # --v2-flow (force the new server-selector wizard on), # --reset-state (first uninstall the CLI + wipe app data for a fresh # user; destructive, asks for confirmation). [group('electron')] electron-run *flags: #!/usr/bin/env bash set -euo pipefail # Validate flags and get reset confirmation BEFORE any side effect (dep # install, build, quitting the app), so declining leaves the machine # untouched. [ "$(uname)" = "Darwin" ] || { echo "electron-run is macOS-only (managed-prefs testing); build with 'just electron-build' and open the app for your OS."; exit 1; } # just delivers variadic args via {{flags}} substitution; put them into # positional params so the loop treats each as one datum. (Local dev recipe: # the caller already has shell access, so this isn't a trust boundary.) set -- {{flags}} rebuild=0; v2=0; reset_state=0 for f in "$@"; do case "$f" in --rebuild) rebuild=1 ;; --v2-flow) v2=1 ;; --reset-state) reset_state=1 ;; *) echo "unknown flag: $f (supported: --rebuild, --v2-flow, --reset-state)"; exit 2 ;; esac done if [ "$reset_state" = 1 ]; then echo "--reset-state will UNINSTALL the omnigent CLI and remove the desktop" echo "app's data (session cookies, recent servers) for a fresh-user test." read -r -p 'Type "yes" to proceed: ' reply [ "$reply" = "yes" ] || { echo "Aborted."; exit 1; } fi # Pick the bundle for THIS machine's architecture (electron-builder emits both # mac-arm64 and mac-x64/mac); a bare glob could launch the wrong one on Intel. case "$(uname -m)" in arm64) archdir="mac-arm64" ;; *) archdir="mac" ;; # electron-builder names the x64 output "mac" esac find_app() { ls -d "web/electron/dist-dev/$archdir/Omnigent Dev.app" 2>/dev/null | head -1 || true; } app="$(find_app)" if [ "$rebuild" = 1 ] || [ -z "$app" ]; then echo "Building the packaged app (this takes a few minutes)…" just _ensure-web _ensure-electron pnpm --filter ./web/electron run build app="$(find_app)" fi [ -n "$app" ] || { echo "No $archdir build found after building."; exit 1; } echo "Quitting any running Omnigent Dev…" osascript -e 'quit app "Omnigent Dev"' 2>/dev/null || true pkill -x 'Omnigent Dev' 2>/dev/null || true sleep 1 if [ "$reset_state" = 1 ]; then # Uninstall the CLI. The shared uninstaller exits non-zero both when no # install exists (fine) and on a genuine failure (e.g. removal blocked) — # and its exit codes don't distinguish those. So verify the OUTCOME # instead: after running it, if the binary is still resolvable the # uninstall really failed — abort BEFORE deleting data or launching, so a # half-reset can't masquerade as a clean fresh-user state. sh scripts/uninstall_oss.sh cli --yes || true hash -r 2>/dev/null || true if command -v omnigent >/dev/null 2>&1; then echo "CLI uninstall did not remove 'omnigent' (still on PATH). Aborting" >&2 echo "before touching app data. Remove it manually, then retry." >&2 exit 1 fi # Wipe the desktop app data directly so a fresh-user reset works even with # no CLI installed (the uninstaller's global guard skips desktop-data in # that case). Intentional destroy — see the confirmation above. rm -rf "$HOME/Library/Application Support/Omnigent Dev" \ "$HOME/Library/Caches/Omnigent Dev" \ "$HOME/Library/Logs/Omnigent Dev" fi if [ "$v2" = 1 ]; then echo "Launching $app (v2 flow forced)" open -n "$app" --env OMNIGENT_SERVER_SELECTOR_V2=1 else echo "Launching $app" open -n "$app" fi # set [,...] [--internal] offer servers; --internal sets databricksInternalFeaturesEnabled # import copy this Mac's MDM values for the release app # show | clear # Relaunch to apply. Real MDM profiles and release builds are untouched. # Test managed preferences for local builds (ai.omnigent.desktop-dev). [group('electron')] [positional-arguments] electron-mdm cmd="show" *args: #!/usr/bin/env bash set -euo pipefail [ "$(uname)" = "Darwin" ] || { echo "electron-mdm is macOS-only."; exit 1; } domain=ai.omnigent.desktop-dev managed="/Library/Managed Preferences/ai.omnigent.desktop.plist" usage="usage: just electron-mdm [set [,...] [--internal] | import | show | clear]" unset_all() { defaults delete "$domain" serverUrls 2>/dev/null || true defaults delete "$domain" databricksInternalFeaturesEnabled 2>/dev/null || true } cmd="${1:-show}" shift || true case "$cmd" in set) [ $# -ge 1 ] || { echo "$usage"; exit 2; } IFS=',' read -ra urls <<< "$1" shift internal=0 for f in "$@"; do case "$f" in --internal) internal=1 ;; *) echo "unknown flag: $f"; echo "$usage"; exit 2 ;; esac done unset_all defaults write "$domain" serverUrls -array "${urls[@]}" [ "$internal" = 0 ] || defaults write "$domain" databricksInternalFeaturesEnabled -bool true ;; import) [ -r "$managed" ] || { echo "No managed preferences for ai.omnigent.desktop on this Mac."; exit 1; } # import merges, so drop the old values first for an exact copy. unset_all defaults import "$domain" "$managed" ;; clear) unset_all ;; show) ;; *) echo "$usage"; exit 2 ;; esac for key in serverUrls databricksInternalFeaturesEnabled; do printf '%s = ' "$key" defaults read "$domain" "$key" 2>/dev/null || echo "(unset)" done # --- Lint --- [group('lint')] lint: _ensure-uv uv run --no-sync pre-commit run [group('lint')] lint-all: _ensure-uv uv run --no-sync pre-commit run --all-files # Install (or reinstall) the git hooks without the rest of `just ensure`. [group('lint')] hooks: _ensure-hooks [group('lint')] typecheck-python: _ensure-uv uv run --no-sync pyrefly check [group('lint')] lint-ts: pnpm install --frozen-lockfile --filter web --filter omnigent-vscode pnpm --filter web run lint pnpm --filter web run type-check pnpm --filter omnigent-vscode run type-check # --- Lockfile maintenance --- [group('lint')] normalize-locks: _ensure-uv uv run --no-sync scripts/normalize_uv_lock_registry.py uv.lock || true # Resolve the declared dependencies through a package mirror without writing # uv.lock; fails when a floor needs a release the mirror does not serve yet. # Dependency groups resolve too, so a dev-only gap can fail it as well. # No `_ensure-uv`: its `uv sync` would re-resolve and rewrite uv.lock first. [group('lint')] check-internal-mirror index="https://pypi-proxy.cloud.databricks.com/simple": uv lock --dry-run --default-index {{ index }}