justfile
Rama main · réplica de lectura del repositorio original.
# Default recipe (runs when just is called without arguments)
default:
@just --list
# Read nightly toolchain from rust-toolchain.toml (single source of truth).
nightly_toolchain := `grep '^channel' rust-toolchain.toml | sed 's/.*"\(.*\)"/\1/'`
# Format Rust code
format:
cargo +{{nightly_toolchain}} fmt --all
# Check if code is formatted
format-check:
cargo +{{nightly_toolchain}} fmt --all -- --check
# Run the full live provider integration workflow locally (sources .envrc when present).
provider-e2e-weekly:
./scripts/run-provider-integration-weekly.sh
# Run only the scenario-driven provider E2E serialization checks.
provider-e2e-scenarios:
./scripts/run-provider-e2e-daily.sh
# Compatibility alias for the old daily recipe name.
provider-e2e-daily: provider-e2e-scenarios
# Verify Cargo.lock is in sync with workspace manifests.
lockfile-check:
cargo fetch --locked
# Lint Rust code using clippy (OS-aware: macOS excludes CUDA features)
lint: lockfile-check
#!/usr/bin/env bash
set -euo pipefail
if [ "$(uname -s)" = "Darwin" ]; then
cargo +{{nightly_toolchain}} clippy -Z unstable-options --workspace --all-targets --exclude moltis-providers --exclude moltis-gateway --exclude moltis-matrix --timings -- -D warnings
cargo +{{nightly_toolchain}} clippy -Z unstable-options -p moltis-providers --all-targets --features local-llm-metal --timings -- -D warnings
cargo +{{nightly_toolchain}} clippy -Z unstable-options -p moltis-gateway --all-targets --features local-llm-metal --timings -- -D warnings
else
cargo +{{nightly_toolchain}} clippy -Z unstable-options --workspace --all-features --all-targets --timings -- -D warnings
fi
# Build Tailwind CSS for the web UI.
build-css:
cd crates/web/ui && ./build.sh
# Build Vite frontend JS (TS/TSX -> dist/).
build-frontend:
cd crates/web/ui && npm run build
# Build the service worker (sw.ts -> sw.js).
build-sw:
cd crates/web/ui && npm run build:sw
# Build all web assets (Vite JS + Tailwind CSS + service worker).
build-web-assets:
./scripts/build-web-assets.sh
# Ad-hoc codesign debug binaries (macOS only, requires MACOS_CODESIGN_IDENTITY).
# Signs the main binary and all test binaries in target/debug/deps/ so Little
# Snitch doesn't prompt on every rebuild during local dev.
[private]
codesign-debug:
#!/usr/bin/env bash
[ "$(uname -s)" = "Darwin" ] || exit 0
[ -n "${MACOS_CODESIGN_IDENTITY:-}" ] || exit 0
id="${MACOS_CODESIGN_IDENTIFIER:-org.moltis.dev}"
sign() { codesign --force --sign "$MACOS_CODESIGN_IDENTITY" --identifier "$id" "$1" 2>/dev/null || true; }
# Main binary
if [ -f target/debug/moltis ]; then sign target/debug/moltis; fi
# Test binaries (Mach-O executables, skip .d/.fingerprint/dylib)
for bin in target/debug/deps/moltis*; do
if [ -f "$bin" ] && [ -x "$bin" ] && [[ "$bin" != *.d ]]; then sign "$bin"; fi
done
# Build the project
build: build-web-assets
cargo build
just codesign-debug
# Build in release mode
build-release:
./scripts/cargo-build-moltis.sh --release
# Build embedded WASM guest tools and pre-compile to .cwasm for AOT loading.
wasm-tools:
cargo build --target wasm32-wasip2 -p moltis-wasm-calc -p moltis-wasm-web-fetch -p moltis-wasm-web-search --release
cargo run -p moltis-wasm-precompile --release
# Build just the release WASM artifacts expected by embedded-wasm builds.
build-wasm-artifacts: wasm-tools
@echo "Built target/wasm32-wasip2/release/{moltis_wasm_calc,moltis_wasm_web_fetch,moltis_wasm_web_search}.{wasm,cwasm}"
# Build release after ensuring embedded WASM artifacts are present.
build-release-with-wasm: build-wasm-artifacts
./scripts/cargo-build-moltis.sh --release
# Run local dev server with workspace-local config/data dirs.
dev-server:
cargo build --bin moltis
just codesign-debug
MOLTIS_SLACK_API_BASE_URL_ALLOWLIST=localhost,127.0.0.1 MOLTIS_CONFIG_DIR=.moltis/config MOLTIS_DATA_DIR=.moltis/ cargo run --bin moltis
# Build Debian package for the current architecture
deb: build-release build-wasm-artifacts
bash ./scripts/stage-wasm-package-assets.sh target/release
cargo deb -p moltis --no-build
# Build Debian package for amd64
deb-amd64: build-wasm-artifacts
./scripts/cargo-build-moltis.sh --release --target x86_64-unknown-linux-gnu
bash ./scripts/stage-wasm-package-assets.sh target/x86_64-unknown-linux-gnu/release
cargo deb -p moltis --no-build --target x86_64-unknown-linux-gnu
# Build Debian package for arm64
deb-arm64: build-wasm-artifacts
./scripts/cargo-build-moltis.sh --release --target aarch64-unknown-linux-gnu
bash ./scripts/stage-wasm-package-assets.sh target/aarch64-unknown-linux-gnu/release
cargo deb -p moltis --no-build --target aarch64-unknown-linux-gnu
# Build Debian packages for all architectures
deb-all: deb-amd64 deb-arm64
# Build Arch package for the current architecture
arch-pkg: build-release
#!/usr/bin/env bash
set -euo pipefail
VERSION="${MOLTIS_VERSION:-$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')}"
ARCH=$(uname -m)
PKG_DIR="target/arch-pkg"
rm -rf "$PKG_DIR"
mkdir -p "$PKG_DIR/usr/bin"
cp target/release/moltis "$PKG_DIR/usr/bin/moltis"
chmod 755 "$PKG_DIR/usr/bin/moltis"
cat > "$PKG_DIR/.PKGINFO" <<PKGINFO
pkgname = moltis
pkgver = ${VERSION}-1
pkgdesc = Personal AI gateway inspired by OpenClaw
url = https://www.moltis.org/
arch = ${ARCH}
license = MIT
PKGINFO
cd "$PKG_DIR"
fakeroot -- tar --zstd -cf "../../moltis-${VERSION}-1-${ARCH}.pkg.tar.zst" .PKGINFO usr/
echo "Built moltis-${VERSION}-1-${ARCH}.pkg.tar.zst"
# Build Arch package for x86_64
arch-pkg-x86_64:
#!/usr/bin/env bash
set -euo pipefail
./scripts/cargo-build-moltis.sh --release --target x86_64-unknown-linux-gnu
VERSION="${MOLTIS_VERSION:-$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')}"
PKG_DIR="target/arch-pkg-x86_64"
rm -rf "$PKG_DIR"
mkdir -p "$PKG_DIR/usr/bin"
cp target/x86_64-unknown-linux-gnu/release/moltis "$PKG_DIR/usr/bin/moltis"
chmod 755 "$PKG_DIR/usr/bin/moltis"
cat > "$PKG_DIR/.PKGINFO" <<PKGINFO
pkgname = moltis
pkgver = ${VERSION}-1
pkgdesc = Personal AI gateway inspired by OpenClaw
url = https://www.moltis.org/
arch = x86_64
license = MIT
PKGINFO
cd "$PKG_DIR"
fakeroot -- tar --zstd -cf "../../moltis-${VERSION}-1-x86_64.pkg.tar.zst" .PKGINFO usr/
echo "Built moltis-${VERSION}-1-x86_64.pkg.tar.zst"
# Build Arch package for aarch64
arch-pkg-aarch64:
#!/usr/bin/env bash
set -euo pipefail
./scripts/cargo-build-moltis.sh --release --target aarch64-unknown-linux-gnu
VERSION="${MOLTIS_VERSION:-$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')}"
PKG_DIR="target/arch-pkg-aarch64"
rm -rf "$PKG_DIR"
mkdir -p "$PKG_DIR/usr/bin"
cp target/aarch64-unknown-linux-gnu/release/moltis "$PKG_DIR/usr/bin/moltis"
chmod 755 "$PKG_DIR/usr/bin/moltis"
cat > "$PKG_DIR/.PKGINFO" <<PKGINFO
pkgname = moltis
pkgver = ${VERSION}-1
pkgdesc = Personal AI gateway inspired by OpenClaw
url = https://www.moltis.org/
arch = aarch64
license = MIT
PKGINFO
cd "$PKG_DIR"
fakeroot -- tar --zstd -cf "../../moltis-${VERSION}-1-aarch64.pkg.tar.zst" .PKGINFO usr/
echo "Built moltis-${VERSION}-1-aarch64.pkg.tar.zst"
# Build Arch packages for all architectures
arch-pkg-all: arch-pkg-x86_64 arch-pkg-aarch64
# Build RPM package for the current architecture
rpm: build-release
cargo generate-rpm -p crates/cli
# Build RPM package for x86_64
rpm-x86_64:
./scripts/cargo-build-moltis.sh --release --target x86_64-unknown-linux-gnu
cargo generate-rpm -p crates/cli --target x86_64-unknown-linux-gnu
# Build RPM package for aarch64
rpm-aarch64:
./scripts/cargo-build-moltis.sh --release --target aarch64-unknown-linux-gnu
cargo generate-rpm -p crates/cli --target aarch64-unknown-linux-gnu
# Build RPM packages for all architectures
rpm-all: rpm-x86_64 rpm-aarch64
# Build AppImage for the current architecture
appimage: build-release
#!/usr/bin/env bash
set -euo pipefail
VERSION="${MOLTIS_VERSION:-$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/')}"
ARCH=$(uname -m)
APP_DIR="target/moltis.AppDir"
rm -rf "$APP_DIR"
mkdir -p "$APP_DIR/usr/bin"
cp target/release/moltis "$APP_DIR/usr/bin/moltis"
chmod 755 "$APP_DIR/usr/bin/moltis"
cat > "$APP_DIR/moltis.desktop" <<DESKTOP
[Desktop Entry]
Type=Application
Name=Moltis
Exec=moltis
Icon=moltis
Categories=Network;
Terminal=true
DESKTOP
cat > "$APP_DIR/moltis.svg" <<SVG
<svg xmlns="http://www.w3.org/2000/svg" width="256" height="256"><rect width="256" height="256" fill="#333"/><text x="128" y="140" font-size="120" text-anchor="middle" fill="white">M</text></svg>
SVG
ln -sf moltis.svg "$APP_DIR/.DirIcon"
cat > "$APP_DIR/AppRun" <<'APPRUN'
#!/bin/sh
SELF=$(readlink -f "$0")
HERE=${SELF%/*}
exec "$HERE/usr/bin/moltis" "$@"
APPRUN
chmod +x "$APP_DIR/AppRun"
if [ ! -f target/appimagetool ]; then
wget -q "https://github.com/AppImage/appimagetool/releases/download/continuous/appimagetool-${ARCH}.AppImage" -O target/appimagetool
chmod +x target/appimagetool
fi
ARCH=${ARCH} target/appimagetool --appimage-extract-and-run "$APP_DIR" "moltis-${VERSION}-${ARCH}.AppImage"
echo "Built moltis-${VERSION}-${ARCH}.AppImage"
# Build Snap package
snap:
snapcraft
# Build Flatpak
flatpak:
cd flatpak && flatpak-builder --repo=repo --force-clean builddir org.moltbot.Moltis.yml
# Run all CI checks (format, lint, build, test)
ci: format-check lint i18n-check build-web-assets build test
# Compile once, then run Rust tests and E2E tests in parallel.
# Uses the same nightly toolchain as clippy/local-validate so the build cache
# is shared — no double-compilation.
build-test: build-web-assets
#!/usr/bin/env bash
set -euo pipefail
echo "==> Building all workspace targets (bins + tests)..."
if [ "$(uname -s)" = "Darwin" ]; then
cargo +{{nightly_toolchain}} build --workspace --all-targets
else
cargo +{{nightly_toolchain}} build --workspace --all-features --all-targets
fi
just codesign-debug
echo "==> Build complete. Running Rust tests and E2E tests in parallel..."
RUST_LOG="$(mktemp)"
E2E_LOG="$(mktemp)"
trap 'rm -f "${RUST_LOG}" "${E2E_LOG}"' EXIT
if [ "$(uname -s)" = "Darwin" ]; then
cargo +{{nightly_toolchain}} nextest run --workspace > "${RUST_LOG}" 2>&1 &
else
cargo +{{nightly_toolchain}} nextest run --workspace --all-features > "${RUST_LOG}" 2>&1 &
fi
TEST_PID=$!
(cd crates/web/ui && npm run e2e) > "${E2E_LOG}" 2>&1 &
E2E_PID=$!
TEST_EXIT=0; E2E_EXIT=0
wait "${TEST_PID}" || TEST_EXIT=$?
wait "${E2E_PID}" || E2E_EXIT=$?
if [ "${TEST_EXIT}" -ne 0 ]; then
echo "==> Rust tests FAILED (exit ${TEST_EXIT}):"
cat "${RUST_LOG}"
else
echo "==> Rust tests PASSED"
fi
if [ "${E2E_EXIT}" -ne 0 ]; then
echo "==> E2E tests FAILED (exit ${E2E_EXIT}):"
cat "${E2E_LOG}"
else
echo "==> E2E tests PASSED"
fi
exit $(( TEST_EXIT > 0 ? TEST_EXIT : E2E_EXIT ))
# Run the same Rust preflight gates used before release packaging.
release-preflight: lint
cargo +{{nightly_toolchain}} fmt --all -- --check
# Sync repo-root install.sh into website/install.sh for Cloudflare deployment.
sync-website-install:
./scripts/sync-website-install.sh
# Ensure repo-root install.sh and website/install.sh are identical.
check-website-install-sync:
./scripts/check-website-install-sync.sh
# Dispatch release workflow from GitHub Actions (normal mode).
release-workflow ref='main':
gh workflow run release.yml --ref {{ref}} -f dry_run=false
# Dispatch release workflow from GitHub Actions (dry-run mode).
release-workflow-dry ref='main':
gh workflow run release.yml --ref {{ref}} -f dry_run=true
# Dispatch both release workflow modes for the same ref (dry-run then normal).
release-workflow-both ref='main':
gh workflow run release.yml --ref {{ref}} -f dry_run=true
gh workflow run release.yml --ref {{ref}} -f dry_run=false
# Regenerate CHANGELOG.md from git history and tags.
changelog:
git-cliff --config cliff.toml --output CHANGELOG.md
# Preview unreleased changelog entries from commits since the last tag.
changelog-unreleased:
git-cliff --config cliff.toml --unreleased
# Generate release entries for unreleased commits under the provided version.
changelog-release version:
git-cliff --config cliff.toml --unreleased --tag "{{version}}" --strip all
# Commit all changes, push branch, create/update PR, and run local validation.
# All args are optional; defaults are auto-generated from branch + changed files.
ship commit_message='' pr_title='' pr_body='':
./scripts/ship-pr.sh {{ quote(commit_message) }} {{ quote(pr_title) }} {{ quote(pr_body) }}
# Run local validation with the full Rust and Playwright test suites.
local-validate-full pr_number='':
#!/usr/bin/env bash
set -euo pipefail
args=()
if [[ -n {{ quote(pr_number) }} ]]; then
args+=({{ quote(pr_number) }})
fi
# `"${args[@]+...}"` is required: bash 3.2 (the default /bin/bash on macOS)
# treats a plain `"${args[@]}"` on an empty array as an unbound variable
# under `set -u`. The array is empty in the supported no-PR local-only mode.
LOCAL_VALIDATE_TEST_CMD="just test" \
LOCAL_VALIDATE_E2E_CMD="cd crates/web/ui && npm run e2e:install && npm run e2e" \
./scripts/local-validate.sh "${args[@]+"${args[@]}"}"
# Run all tests (nightly to share build cache with clippy/lint, OS-aware).
# On macOS: single nextest run using default features (includes Metal, not CUDA).
# On Linux: --all-features (includes CUDA).
# Builds first so codesign can run before test execution (prevents Little Snitch prompts).
test:
#!/usr/bin/env bash
set -euo pipefail
if [ "$(uname -s)" = "Darwin" ]; then
cargo +{{nightly_toolchain}} build --workspace --all-targets
just codesign-debug
cargo +{{nightly_toolchain}} nextest run --workspace
else
cargo +{{nightly_toolchain}} nextest run --workspace --all-features
fi
# Run contract test suites (channel, provider, memory, tools)
contract-tests:
cargo test -p moltis-channels contract
cargo test -p moltis-providers contract
cargo test -p moltis-memory contract
cargo test -p moltis-tools contract
# Verify locale key parity across frontend i18n bundles.
i18n-check:
./scripts/i18n-check.sh
# Install browser tooling for gateway web UI e2e tests.
ui-e2e-install:
cd crates/web/ui && npm install && npm run e2e:install
# Run gateway web UI e2e tests (Playwright).
ui-e2e:
cargo +{{nightly_toolchain}} build --bin moltis
just codesign-debug
cd crates/web/ui && npm run e2e
# Run gateway web UI e2e tests with headed browser.
ui-e2e-headed:
cargo +{{nightly_toolchain}} build --bin moltis
just codesign-debug
cd crates/web/ui && npm run e2e:headed
# Build all Linux packages (deb + rpm + arch + appimage) for all architectures
packages-all: deb-all rpm-all arch-pkg-all
# Build the native gpui desktop client (apps/desktop, its own workspace).
desktop-build:
cd apps/desktop && cargo build
# Run the desktop client's tests.
desktop-test:
cd apps/desktop && cargo test
# Lint the desktop client (all feature combinations it ships with).
desktop-lint:
cd apps/desktop && cargo clippy --all-targets --features mockup -- -D warnings
cd apps/desktop && cargo clippy --all-targets --no-default-features -- -D warnings
# Run the desktop client.
desktop-run:
cd apps/desktop && cargo run
# Show UI variants from FILE side by side (the gpui-mockup agent skill).
# Without FILE it shows the built-in demo (apps/desktop/src/mockup/demo.rs).
desktop-mockup file="" feedback="" capture="":
#!/usr/bin/env bash
set -euo pipefail
absolute() { case "$1" in ""|/*) printf '%s' "$1" ;; *) printf '%s/%s' {{quote(invocation_directory())}} "$1" ;; esac; }
file="$(absolute {{quote(file)}})"
feedback="$(absolute {{quote(feedback)}})"
capture="$(absolute {{quote(capture)}})"
cd apps/desktop
MOLTIS_MOCKUP_FILE="$file" cargo build --locked --features mockup
args=(--mockup)
if [ -n "$feedback" ]; then args+=(--feedback "$feedback"); fi
if [ -n "$capture" ]; then args+=(--capture "$capture"); fi
exec target/debug/moltis-desktop "${args[@]}"
# Package "Moltis Desktop.app" (macOS) with the moltis daemon inside.
desktop-package-macos:
./scripts/package-desktop-macos.sh
# Package the Moltis Desktop AppImage (Linux) with the moltis daemon inside.
desktop-package-linux:
./scripts/package-desktop-linux.sh
# Build "Moltis Desktop.app" with the moltis daemon inside (release) and open it.
desktop-open-macos: desktop-package-macos
open "apps/desktop/target/bundle/Moltis Desktop.app"
# Build Rust static library and generated C header for the macOS app.
swift-build-rust:
./scripts/build-swift-bridge.sh
# Generate Xcode project from YAML spec in apps/macos.
swift-generate:
./scripts/generate-swift-project.sh
# Lint macOS app sources with SwiftLint.
swift-lint:
./scripts/lint-swift.sh
# Build Swift macOS app.
swift-build: swift-build-rust swift-generate
./scripts/build-swift.sh
# Run Swift app unit tests.
swift-test: swift-build-rust swift-generate
./scripts/test-swift.sh
# Build and launch the Swift macOS app locally.
swift-run: swift-build-rust swift-generate
./scripts/run-swift.sh
# Open generated project in Xcode.
swift-open: swift-build-rust swift-generate
open apps/macos/Moltis.xcodeproj
# Generate iOS app Xcode project.
ios-generate:
./scripts/generate-ios-project.sh
# Generate Apollo GraphQL types for iOS.
ios-graphql:
cargo run -p moltis-schema-export -- apps/ios/GraphQL/Schema/schema.graphqls
./scripts/generate-ios-graphql.sh
# Build iOS app (generic iOS destination, no signing).
ios-build: ios-generate ios-graphql
./scripts/generate-ios-project.sh
xcodebuild -project apps/ios/Moltis.xcodeproj -scheme Moltis -configuration Debug -destination "generic/platform=iOS" CODE_SIGNING_ALLOWED=NO build
# Lint iOS app sources with SwiftLint.
ios-lint:
cd apps/ios && swiftlint
# Open iOS project in Xcode (regenerates GraphQL types and project first).
ios-open: ios-generate ios-graphql
./scripts/generate-ios-project.sh
open apps/ios/Moltis.xcodeproj
# Build the APNS push relay.
courier-build:
cargo build -p moltis-courier --release
# Cross-compile courier for linux/x86_64.
courier-cross:
cargo build -p moltis-courier --release --target x86_64-unknown-linux-gnu
# Deploy courier to remote server(s) via Ansible.
courier-deploy:
cd apps/courier/deploy && ansible-playbook playbook.yml
# Run the APNS push relay (dev).
courier-run *ARGS:
cargo run -p moltis-courier -- {{ARGS}}
# Serve the website locally with SSR partial injection (default 127.0.0.1:4000).
website-dev:
node website/scripts/build-changelog.mjs
node website/scripts/dev-server.mjs