Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion .agents/roles.md
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,10 @@ ShellDeck requires an account to launch. Historically the app ran a
* `AppMode::resolve_effective(signed_in = false, …)` returns `User`
as a defensive fallback. Dev is super-admin-only and must never
render for anyone else — not even as a fallback.
* No `welcome_bypass` / guest / standalone flag exists.
* No flag bypasses welcome, login or role authorization. `--agents` opens an
authenticated Agents presentation in the production app; it does not grant
Dev capabilities or create guest access. The normal welcome screen remains
authoritative when signed out, including initial and single-instance launches.

## Rollout status (2026-07-13)

Expand Down
140 changes: 140 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,35 @@ env:
CARGO_TERM_COLOR: always

jobs:
agents-packaging:
name: Agents launcher and packaging contracts
runs-on: ubuntu-latest
timeout-minutes: 10
permissions:
contents: read
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- uses: actions/setup-node@v4
with:
node-version: '24'
- name: Install small launcher tooling
run: |
sudo apt-get update
sudo apt-get install -y nsis desktop-file-utils
- name: Verify production Agents packages without building the application
run: |
# Require the optional local tools here so SDTEST-2632 cannot skip in CI.
node --experimental-strip-types --input-type=module -e ''
command -v makensis
python3 scripts/test-agents-packaging.py
python3 scripts/test-browser-packaging.py
bash -n packaging/linux/build-appimage.sh
bash -n packaging/windows/build-nsis.sh
desktop-file-validate packaging/linux/shelldeck.desktop

check:
name: Check & Test
runs-on: ubuntu-latest
Expand Down Expand Up @@ -81,6 +110,13 @@ jobs:
- name: cargo test
run: cargo test --workspace

- name: Verify production Agents CLI without GUI or profile side effects
env:
PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
run: |
cargo build --locked -p shelldeck --bin shelldeck
python3 scripts/test-agents-cli.py target/debug/shelldeck

- name: cargo clippy
# `--no-deps` skips lints from vendored path deps under `patches/`
# (adabraka-gpui / adabraka-ui): we don't own that code and per
Expand Down Expand Up @@ -231,3 +267,107 @@ jobs:
# there is no shared store to isolate. Each test still writes under a
# pid+nanos host name and deletes it.
run: cargo test -p shelldeck-core -- --ignored config::keychain::tests::live_

browser-runtime:
defaults:
run:
shell: ${{ matrix.shell }}
name: Native Chromium (${{ matrix.platform }})
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
shell: bash
platform: linux-x86_64
- os: macos-15
shell: bash
platform: macos-aarch64
- os: windows-latest
shell: pwsh
platform: windows-x86_64
runs-on: ${{ matrix.os }}
timeout-minutes: 45
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@master
with:
toolchain: nightly-2026-03-06
- uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Configure isolated CEF build cache
shell: bash
run: echo "CEF_PATH=${RUNNER_TEMP}/shelldeck-cef" >> "$GITHUB_ENV"
- name: Cache Cargo and pinned Chromium runtime
uses: actions/cache@v4
with:
path: |
~/.cargo/registry
~/.cargo/git
${{ runner.temp }}/shelldeck-cef
key: ${{ runner.os }}-browser-native-${{ hashFiles('Cargo.lock', 'crates/shelldeck-browser/Cargo.toml') }}
- name: Install Linux Chromium dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y cmake ninja-build pkg-config libgtk-3-0 libnss3 libnspr4 \
libatk-bridge2.0-0 libatspi2.0-0 libcups2 libdrm2 libgbm1 libxcomposite1 \
libxdamage1 libxrandr2 libasound2 libxss1 libx11-xcb1 libxcursor1 libxtst6 xvfb
- name: Install macOS CEF build tools
if: runner.os == 'macOS'
run: brew install cmake ninja
- name: Configure Windows native compiler
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Pin Windows native linker for Bash build scripts
if: runner.os == 'Windows'
shell: pwsh
run: |
$linker = Join-Path $env:VCToolsInstallDir 'bin\Hostx64\x64\link.exe'
if (-not (Test-Path -LiteralPath $linker -PathType Leaf)) {
throw "The MSVC linker was not selected: $linker"
}
"CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=$linker" >> $env:GITHUB_ENV
- name: Verify browser protocol, capability boundaries and exact native screenshots
env:
PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
run: cargo test --locked -p shelldeck-browser --lib
- name: Build native sandboxed CEF helper
env:
PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
run: cargo build --locked -p shelldeck-browser --features runtime
- name: Stage complete native browser payload
run: python3 scripts/bundle-browser-runtime.py --profile debug
- name: Launch staged native stdio entry point
run: python3 scripts/test-browser-helper-launch.py target/debug/browser
- name: Verify Linux browser archive contracts
if: runner.os == 'Linux'
run: python3 scripts/test-browser-packaging.py
- name: Exercise native Linux Chromium rendering and profile persistence
if: runner.os == 'Linux'
shell: bash
run: |
browser_helper="$(python3 -c 'import json,pathlib; root=pathlib.Path("target/debug/browser"); meta=next(root.glob("*/runtime.json")); print(meta.parent/json.loads(meta.read_text())["executable"])')"
xvfb-run -a python3 scripts/test-browser-runtime.py "$browser_helper"
- name: Exercise native MCP computer use and popup permissions
if: runner.os != 'Windows'
shell: bash
env:
PKG_CONFIG_PATH: /usr/lib/x86_64-linux-gnu/pkgconfig
run: |
browser_helper="$(python3 -c 'import json,pathlib; root=pathlib.Path("target/debug/browser"); meta=root/"runtime.json"; meta=meta if meta.is_file() else next(root.glob("*/runtime.json")); print((meta.parent/json.loads(meta.read_text())["executable"]).resolve())')"
if [ "${RUNNER_OS}" = "Linux" ]; then
xvfb-run -a cargo run --locked -p shelldeck-browser --example computer_use_smoke -- "$browser_helper"
else
cargo run --locked -p shelldeck-browser --example computer_use_smoke -- "$browser_helper"
fi
- name: Exercise native Windows MCP computer use and popup permissions
if: runner.os == 'Windows'
shell: pwsh
run: |
$browserHelper = python3 -c 'import json,pathlib; root=pathlib.Path("target/debug/browser"); meta=next(root.glob("*/runtime.json")); print((meta.parent/json.loads(meta.read_text())["executable"]).resolve())'
cargo run --locked -p shelldeck-browser --example computer_use_smoke -- $browserHelper
if ($LASTEXITCODE -ne 0) { throw "Native MCP browser interaction failed" }
118 changes: 98 additions & 20 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,27 +50,33 @@ jobs:

# ─── Build on each platform ───
build:
defaults:
run:
shell: ${{ matrix.shell }}
name: Build (${{ matrix.platform }})
needs: prepare
strategy:
fail-fast: false
matrix:
include:
- os: ubuntu-22.04
shell: bash
target: x86_64-unknown-linux-gnu
platform: linux-x86_64
archive_ext: tar.gz
- os: macos-15
shell: bash
target: aarch64-apple-darwin
platform: macos-aarch64
archive_ext: zip
- os: windows-latest
shell: pwsh
target: x86_64-pc-windows-msvc
platform: windows-x86_64
archive_ext: zip

runs-on: ${{ matrix.os }}
timeout-minutes: 60
timeout-minutes: 90
steps:
- uses: actions/checkout@v4

Expand All @@ -91,6 +97,40 @@ jobs:
restore-keys: |
${{ runner.os }}-cargo-release-

- name: Install packaging Python
uses: actions/setup-python@v5
with:
python-version: '3.12'

- name: Verify browser archive contracts
if: runner.os == 'Linux'
run: python3 scripts/test-browser-packaging.py

- name: Configure isolated CEF build cache
shell: bash
run: echo "CEF_PATH=${RUNNER_TEMP}/shelldeck-cef" >> "$GITHUB_ENV"

- name: Cache pinned Chromium runtime
uses: actions/cache@v4
with:
path: ${{ runner.temp }}/shelldeck-cef
key: ${{ runner.os }}-cef-${{ hashFiles('crates/shelldeck-browser/Cargo.toml') }}

- name: Configure Windows native compiler
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x64
- name: Pin Windows native linker for Bash build scripts
if: runner.os == 'Windows'
shell: pwsh
run: |
$linker = Join-Path $env:VCToolsInstallDir 'bin\Hostx64\x64\link.exe'
if (-not (Test-Path -LiteralPath $linker -PathType Leaf)) {
throw "The MSVC linker was not selected: $linker"
}
"CARGO_TARGET_X86_64_PC_WINDOWS_MSVC_LINKER=$linker" >> $env:GITHUB_ENV

# Linux dependencies
- name: Install Linux dependencies
if: runner.os == 'Linux'
Expand All @@ -109,12 +149,31 @@ jobs:
libxkbcommon-x11-dev \
libdbus-1-dev \
libxdo-dev \
libfuse2
libfuse2 \
cmake \
ninja-build \
libgtk-3-0 \
libnss3 \
libnspr4 \
libatk-bridge2.0-0 \
libatspi2.0-0 \
libcups2 \
libdrm2 \
libgbm1 \
libxcomposite1 \
libxdamage1 \
libxrandr2 \
libasound2 \
libxss1 \
libx11-xcb1 \
libxcursor1 \
libxtst6 \
xvfb

# macOS dependencies
- name: Install macOS dependencies
if: runner.os == 'macOS'
run: brew install openssl pkg-config create-dmg
run: brew install openssl pkg-config create-dmg cmake ninja

- name: Require update verification key for tagged releases
if: startsWith(github.ref, 'refs/tags/v')
Expand All @@ -131,6 +190,9 @@ jobs:
SHELLDECK_DEFER_ARCHIVE: "1"
run: bash scripts/build-release.sh

- name: Verify packaged browser native entry point
run: python3 scripts/test-browser-helper-launch.py target/release/browser

# ─── Native Installers ───

- name: Import Linux AppImage signing key
Expand All @@ -150,6 +212,8 @@ jobs:
if: runner.os == 'Linux'
env:
SHELLDECK_APPIMAGE_SIGN: ${{ startsWith(github.ref, 'refs/tags/v') && '1' || '0' }}
SHELLDECK_APPIMAGE_VERIFY_RUNTIME: '1'
GH_TOKEN: ${{ github.token }}
run: bash packaging/linux/build-appimage.sh

- name: Import Apple Developer ID certificate
Expand Down Expand Up @@ -223,7 +287,7 @@ jobs:
shell: bash
run: choco install nsis -y

- name: Sign Windows executable
- name: Sign Windows application and browser binaries
if: runner.os == 'Windows' && (startsWith(github.ref, 'refs/tags/v') || inputs.test_windows_signing)
shell: pwsh
env:
Expand Down Expand Up @@ -262,25 +326,39 @@ jobs:
)
Write-Host "Loaded the self-signed certificate without modifying Windows trust stores"
}
if ($useSelfSignedCertificate) {
$signature = Set-AuthenticodeSignature `
-FilePath "target/release/shelldeck.exe" `
-Certificate $testCertificate `
-HashAlgorithm SHA256
if (
-not $signature.SignerCertificate -or
$signature.SignerCertificate.Thumbprint -ne $testCertificate.Thumbprint
) {
throw "Self-signed Authenticode signature verification failed"
}
} else {
$version = "${{ needs.prepare.outputs.version }}"
$browserRoot = Join-Path "target/release/browser" $version
$payload = @((Resolve-Path "target/release/shelldeck.exe").Path)
$payload += Get-ChildItem $browserRoot -Recurse -File |
Where-Object { $_.Extension -in ".exe", ".dll" } |
Select-Object -ExpandProperty FullName
if (-not $useSelfSignedCertificate) {
$signtool = Get-ChildItem "${env:ProgramFiles(x86)}\Windows Kits\10\bin\*\x64\signtool.exe" |
Sort-Object FullName | Select-Object -Last 1 -ExpandProperty FullName
if (-not $signtool) { throw "signtool.exe was not found" }
& $signtool sign /fd SHA256 /td SHA256 /tr "http://timestamp.digicert.com" `
/f $certificate /p $certificatePassword `
"target/release/shelldeck.exe"
& $signtool verify /pa /all /v "target/release/shelldeck.exe"
}
foreach ($binary in $payload) {
# Chromium's bootstrap and chrome_elf must have the same identity.
# Preserve valid signatures on other third-party dependencies.
$forced = [IO.Path]::GetFileName($binary) -in @(
"shelldeck.exe", "shelldeck-browser.exe", "shelldeck-browser.dll", "chrome_elf.dll"
)
$existing = Get-AuthenticodeSignature -FilePath $binary
if (-not $forced -and $existing.Status -eq "Valid") { continue }
if ($useSelfSignedCertificate) {
$signature = Set-AuthenticodeSignature -FilePath $binary `
-Certificate $testCertificate -HashAlgorithm SHA256
if (
-not $signature.SignerCertificate -or
$signature.SignerCertificate.Thumbprint -ne $testCertificate.Thumbprint
) { throw "Self-signed Authenticode signature verification failed: $binary" }
} else {
& $signtool sign /fd SHA256 /td SHA256 /tr "http://timestamp.digicert.com" `
/f $certificate /p $certificatePassword $binary
if ($LASTEXITCODE -ne 0) { throw "Authenticode signing failed: $binary" }
& $signtool verify /pa /all /tw /v $binary
if ($LASTEXITCODE -ne 0) { throw "Authenticode verification failed: $binary" }
}
}

- name: Build NSIS installer
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,4 @@ node_modules/
/.claude/*.lock
# Outils de confort locaux, gardés hors du dépôt et lancés à la demande.
/scripts/typo-fr.py
/scripts/__pycache__/
Loading
Loading