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
12 changes: 4 additions & 8 deletions .github/pull_request_template.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,9 @@
## Summary

- Describe the user-facing outcome.
- Describe the user-facing outcome and any compatibility or migration impact.
- If the change is internal-only, say so.

## Validation

- List the commands or checks run.

## Changelog

- [ ] Added `changes/<pr-number>.<type>.md`
- [ ] This is dependency maintenance, or it is internal-only and the PR
explains why a maintainer should apply `skip-changelog`:
- List the exact commands or checks run and the real boundaries exercised.
- Do not replace results with “tests passed.”
57 changes: 56 additions & 1 deletion .github/workflows/desktop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ on:
required: false
default: 14
type: number
sign:
description: Sign and notarize the macOS build (requires Apple secrets).
required: false
default: false
type: boolean
workflow_dispatch:
inputs:
checkout_ref:
Expand All @@ -24,6 +29,11 @@ on:
required: false
default: 14
type: number
sign:
description: Sign and notarize the macOS build (requires Apple secrets).
required: false
default: false
type: boolean
permissions:
contents: read

Expand Down Expand Up @@ -116,9 +126,54 @@ jobs:
- name: Test the locked desktop crate
run: cargo test --release --locked --manifest-path desktop/src-tauri/Cargo.toml

- name: Build the unsigned desktop installer
- name: Prepare the Apple notarization key
id: apple_key
if: runner.os == 'macOS' && inputs.sign
shell: bash
env:
APPLE_API_KEY_P8: ${{ secrets.APPLE_API_KEY_P8 }}
run: |
if [[ -z "$APPLE_API_KEY_P8" ]]; then
echo "::error::inputs.sign is true but Apple signing secrets are missing." >&2
exit 1
fi
key_path="$RUNNER_TEMP/apple_api_key.p8"
printf '%s' "$APPLE_API_KEY_P8" | openssl base64 -d -A > "$key_path"
echo "path=$key_path" >> "$GITHUB_OUTPUT"

- name: Build the desktop installer
working-directory: desktop
env:
VIDXP_DESKTOP_SIGN: ${{ (runner.os == 'macOS' && inputs.sign) && '1' || '' }}
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_SIGNING_IDENTITY }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
APPLE_API_ISSUER: ${{ secrets.APPLE_API_ISSUER }}
APPLE_API_KEY: ${{ secrets.APPLE_API_KEY }}
APPLE_API_KEY_PATH: ${{ steps.apple_key.outputs.path }}
run: npm run desktop:build

- name: Verify signing and notarization
if: runner.os == 'macOS' && inputs.sign
working-directory: desktop
shell: bash
run: |
dmg="$(find src-tauri/target/release/bundle/dmg -maxdepth 1 -name '*.dmg' | head -n1)"
[[ -n "$dmg" ]]
# Tauri removes the intermediate .app after packaging the DMG.
codesign --verify --strict --verbose=2 "$dmg"
mountpoint="$(mktemp -d "$RUNNER_TEMP/vidxp-dmg.XXXXXX")"
trap 'hdiutil detach "$mountpoint" -quiet >/dev/null 2>&1 || true; rmdir "$mountpoint" >/dev/null 2>&1 || true' EXIT
hdiutil attach "$dmg" -readonly -nobrowse -mountpoint "$mountpoint"
app="$(find "$mountpoint" -maxdepth 1 -name '*.app' | head -n1)"
[[ -n "$app" ]]
# The .app is signed (Developer ID), hardened-runtime, notarized + stapled.
codesign --verify --strict --verbose=2 "$app"
codesign -dvv "$app" 2>&1 | grep -i 'Authority=Developer ID Application'
codesign -dvv "$app" 2>&1 | grep -iE 'flags=.*runtime'
xcrun stapler validate "$app"
spctl -a -t exec -vv "$app"

- name: Verify the Windows app uses the GUI subsystem
if: runner.os == 'Windows'
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/release-candidate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,8 @@ jobs:
with:
artifact_retention_days: 30
checkout_ref: ${{ inputs.head_sha }}
sign: true
secrets: inherit

containers:
needs: contract
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/release-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ on:
- reopened

permissions:
actions: write
contents: none
statuses: write

Expand All @@ -20,9 +21,13 @@ jobs:
steps:
- name: Classify the pull request without checking out its code
env:
BASE_REF: ${{ github.event.pull_request.base.ref }}
BASE_SHA: ${{ github.event.pull_request.base.sha }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
HEAD_REF: ${{ github.event.pull_request.head.ref }}
HEAD_REPOSITORY: ${{ github.event.pull_request.head.repo.full_name }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PULL_REQUEST: ${{ github.event.pull_request.number }}
TARGET_URL: ${{ github.event.pull_request.html_url }}
shell: bash
run: |
Expand All @@ -38,3 +43,15 @@ jobs:
-f context=release/candidate \
-f description="$description" \
-f target_url="$TARGET_URL"

if [[ "$state" == "pending" &&
"$HEAD_REPOSITORY" == "$GITHUB_REPOSITORY" &&
"$GITHUB_ACTOR" != "github-actions[bot]" ]]; then
gh workflow run release-candidate.yml \
--repo "$GITHUB_REPOSITORY" \
--ref "$BASE_REF" \
-f pull_request="$PULL_REQUEST" \
-f target_branch="$BASE_REF" \
-f base_sha="$BASE_SHA" \
-f head_sha="$HEAD_SHA"
fi
20 changes: 11 additions & 9 deletions .github/workflows/release-to-test-pypi.yml
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ jobs:
- name: Download and install the exact TestPyPI artifact
shell: bash
run: |
download_succeeded=false
for _ in {1..30}; do
state="$(
python utils/verify_published_distribution.py \
Expand All @@ -111,17 +112,18 @@ jobs:
--version "${{ needs.build.outputs.version }}" \
--dist dist
)"
[[ "$state" == "identical" ]] && break
if [[ "$state" == "identical" ]] && python -m pip download \
--index-url https://test.pypi.org/simple \
--no-deps \
--no-cache-dir \
--dest downloaded \
"vidxp==${{ needs.build.outputs.version }}"; then
download_succeeded=true
break
fi
sleep 10
done
[[ "$state" == "identical" ]]

python -m pip download \
--index-url https://test.pypi.org/simple \
--no-deps \
--no-cache-dir \
--dest downloaded \
"vidxp==${{ needs.build.outputs.version }}"
[[ "$download_succeeded" == "true" ]]
[[ "$(sha256sum dist/*.whl | cut -d' ' -f1)" == \
"$(sha256sum downloaded/*.whl | cut -d' ' -f1)" ]]
python -m venv .testpypi-smoke
Expand Down
2 changes: 1 addition & 1 deletion .release-please-manifest.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
{
".": "0.4.0-b.2"
".": "0.4.0-b.3"
}
31 changes: 31 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Contributor agent guide

VidXP is a Python application with CLI, HTTP, MCP, and Desktop surfaces that
share the same application contracts. Read
[`docs/CONTRIBUTING.md`](docs/CONTRIBUTING.md) before making changes.

## Working in the repository

- Keep pull requests focused on one outcome and preserve unrelated work.
- Put shared behavior in the application or control plane; keep CLI, HTTP,
MCP, and Desktop code as thin adapters.
- Keep capability-specific models, schemas, dependencies, indexing, and search
logic under `src/vidxp/capabilities/`.
- Do not commit generated environments, model weights, media, indexes, build
outputs, or local data.
- Follow the Conventional Commit and release-note rules in the contributing
guide. State explicitly when a change is internal-only.

## Validation

Run the smallest relevant checks while developing, then the applicable checks
from the contributing guide before submitting a pull request. Common checks:

```bash
uv run --no-sync ruff check .
uv run --no-sync pytest -q
npm --prefix desktop run check
```

Do not describe mocked tests as end-to-end validation. Report the exact
commands run and any required validation that could not be completed.
13 changes: 13 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,18 @@
# CHANGELOG

## [0.4.0-b.3](https://github.com/grayhatdevelopers/vidxp/compare/v0.4.0-b.2...v0.4.0-b.3) (2026-08-05)


### Features

* **desktop:** add guided setup and local service management ([#97](https://github.com/grayhatdevelopers/vidxp/issues/97)) ([8e779a3](https://github.com/grayhatdevelopers/vidxp/commit/8e779a3364670aab833668956a289415eac8652b))


### Bug Fixes

* **desktop:** install managed runtimes from macOS Application Support paths ([f5187b7](https://github.com/grayhatdevelopers/vidxp/commit/f5187b7905c3dde27863c0ef9659909dc04000c7))
* **desktop:** provide a signed and notarized macOS installer ([4074332](https://github.com/grayhatdevelopers/vidxp/commit/40743321907c5393dc66a7e7d21a95e8dc3e602e))

## [0.4.0-b.2](https://github.com/grayhatdevelopers/vidxp/compare/v0.4.0-b.1...v0.4.0-b.2) (2026-08-03)


Expand Down
14 changes: 14 additions & 0 deletions CODEX.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Codex contributor notes

Start with [`AGENTS.md`](AGENTS.md) and
[`docs/CONTRIBUTING.md`](docs/CONTRIBUTING.md).

1. Inspect the affected implementation, shared contracts, and existing tests.
2. Make the smallest coherent change at the correct architecture boundary.
3. Reuse repository tooling and generated-file workflows instead of manually
recreating derived artifacts.
4. Run validation appropriate to the changed surface.
5. Summarize the outcome, exact validation performed, and remaining risks.

Ask before introducing a new dependency, migration, public contract change, or
architecture direction that is not already established by the repository.
40 changes: 31 additions & 9 deletions INSTALLATION_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Local model work requires a capability or worker extra.
|---|---|---|
| CLI or MCP | [uv 0.12+](https://docs.astral.sh/uv/getting-started/installation/) | Python and the isolated VidXP environment |
| Desktop-managed target | A supported OS, internet access for first setup, FFmpeg, ffprobe, `libx264`, and `aac` | uv, Python, VidXP, and selected model files |
| Desktop with existing target | A compatible local `vidxp` executable and that installation's own media-runtime setup | Target discovery and launch coordination only |
| Desktop with existing target | A compatible local `vidxp` executable and that installation's own media-runtime setup | Target discovery, service controls, and feature reinstallation for isolated uv tools |
| Docker | Docker Engine or Docker Desktop | Python, VidXP, and FFmpeg inside the image |

Native CLI and desktop processing require FFmpeg, ffprobe, `libx264`, and
Expand Down Expand Up @@ -354,27 +354,49 @@ does not install anything before that choice:

- **Use an existing installation** discovers compatible `vidxp` executables or
lets you browse to one. Desktop validates the versioned probe and launch
contracts, but the installation stays externally owned. Desktop never
installs, repairs, updates, removes, or broadly stops it. If its browser
surface is missing, enable the `frontend` extra with that installation's own
package-management workflow before Desktop can open it.
contracts, and the installation stays selected and externally owned.
For an isolated uv-tool installation, **Setup options** can change its search,
local-processing, browser, AI-assistant, or app-integration features. Desktop recreates that app environment at
its compatible VidXP and Python versions with the complete selected extra set,
then rechecks it. If the saved installation predates the required management
contract, Desktop offers to update that same uv-tool environment to the runtime
version bundled with the Desktop release before applying the chosen features.
It does not interpret fields missing from an older probe as disabled features.
Other environment types stay with their original package
manager. Desktop does not broadly stop an external installation. The
compatibility probe reports installed search, processing, and integration features.
- **Set up VidXP for me** creates a private Python and VidXP runtime owned by
Desktop. Python and uv do not need to be installed separately. Capability
code, the optional browser interface, model storage, and initial model
preparation are selected before applying the draft.
code, optional local video processing, browser interface, AI-assistant
integration, and app integration service,
model storage, and initial model preparation are selected before applying
the draft.

A managed setup or update remains a draft until its candidate runtime passes
the Desktop probe and launch contracts. Activation then replaces the previous
managed target atomically; failed or cancelled work leaves the previous target
authoritative. For an unchanged ready runtime, **Prepare / verify models**
authoritative. For an unchanged ready runtime, **Check downloaded models**
checks cached files and downloads only missing selected model material without
requiring a configuration change.

The active-target panel can run the selected installation's read-only
`vidxp doctor --json` check, start/monitor/stop local video processing through
the existing worker supervisor, generate `mcpServers` JSON bound to that exact
installation and repository, and start/monitor/stop a Desktop-owned loopback
`vidxp-api` process when the app integration service is installed. These controls remain
available after installation; Desktop is not only a first-run installer or a
browser launcher. It broadly stops only a Desktop-owned target and only
reinstalls an existing isolated tool after the user confirms the feature change.
Browser and app-service processes start private to the current computer.
Desktop can also invoke each service's existing `--share` mode: it shows the
resolved LAN port and URLs, warns that the shared browser has no authentication,
and exposes the API/MCP bearer token behind the connection details.

Starting Desktop, or starting it a second time, shows and focuses the control
panel without opening a browser. **Open VidXP** explicitly starts or reuses the
loopback browser service and opens one tab. Closing a configured window hides
it to the tray. Tray actions are **Manage VidXP**, **Open VidXP**, and **Quit
VidXP**. Quit stops the exact browser service Desktop launched; broad worker
VidXP**. Quit stops the exact browser and API services Desktop launched; broad worker
shutdown is limited to a Desktop-owned runtime.

The NSIS, DMG, and AppImage packages do not bundle FFmpeg. Managed setup can
Expand Down
4 changes: 2 additions & 2 deletions desktop/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion desktop/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "vidxp-desktop",
"private": true,
"version": "0.4.0-b.2",
"version": "0.4.0-b.3",
"type": "module",
"scripts": {
"dev": "vite",
Expand Down
24 changes: 21 additions & 3 deletions desktop/runtime-manifest.json
Original file line number Diff line number Diff line change
@@ -1,17 +1,35 @@
{
"schema_version": 1,
"desktop_version": "0.4.0-b.2",
"desktop_version": "0.4.0-b.3",
"package_name": "vidxp",
"package_version": "0.4.0-b.2",
"package_version": "0.4.0-b.3",
"dependency_index": "https://pypi.org/simple",
"python_version": "3.14.6",
"uv_version": "0.12.0",
"surfaces": {
"worker": {
"extra": "local-worker",
"label": "Process videos on this computer",
"description": "Run background indexing, search, and grounded questions locally. This includes all built-in search features and is the normal desktop setup.",
"default": true
},
"browser": {
"extra": "frontend",
"label": "Browser interface",
"description": "Installs the local browser interface. Leave this off for a processing-only runtime.",
"description": "Use VidXP's visual workspace in your default browser. It stays private to this computer unless you explicitly share it without authentication.",
"default": true
},
"mcp": {
"extra": "mcp",
"label": "AI assistant integration",
"description": "Use VidXP from an MCP-compatible AI assistant installed on this computer.",
"default": false
},
"server": {
"extra": "server",
"label": "App integration service",
"description": "Run an API and network-style MCP connection for other software. It is private by default and can be shared on your local network with bearer-token authentication.",
"default": false
}
},
"capabilities": {
Expand Down
Loading
Loading