diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index cb36e61..1c2e7f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,15 +1,31 @@ name: CI -# Runners: uvacompute (https://uvacompute.com) -# To enable, set the UVA_RUNNER repo variable to the correct runner label. -# runs-on: ${{ vars.UVA_RUNNER || 'ubuntu-latest' }} - on: pull_request: branches: [main] push: branches: [main] workflow_dispatch: + inputs: + bump: + description: Version bump type (only for workflow_dispatch) + type: choice + options: + - patch + - minor + - major + default: patch + publish_npm: + description: Publish to npm + type: boolean + default: true + publish_crates: + description: Publish to crates.io + type: boolean + default: true + +env: + FORCE_JAVASCRIPT_ACTIONS_TO_NODE24: true permissions: contents: write @@ -17,7 +33,7 @@ permissions: jobs: changes: name: Changes - runs-on: ubuntu-latest + runs-on: [self-hosted, netty] outputs: rust: ${{ steps.check.outputs.rust }} version: ${{ steps.version.outputs.version }} @@ -52,19 +68,40 @@ jobs: echo "rust=${{ steps.filter.outputs.rust }}" >> "$GITHUB_OUTPUT" fi - - name: Read current version + - name: Calculate next version id: version if: github.event_name != 'pull_request' && steps.check.outputs.rust == 'true' run: | - VERSION=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') - echo "version=${VERSION}" >> "$GITHUB_OUTPUT" - echo "tag=v${VERSION}" >> "$GITHUB_OUTPUT" + CURRENT=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') + IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" + + BUMP="${{ inputs.bump || 'patch' }}" + case "$BUMP" in + major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; + minor) MINOR=$((MINOR + 1)); PATCH=0 ;; + patch) + LATEST=$(git tag -l "v${MAJOR}.${MINOR}.*" | sort -V | tail -1) + if [ -z "$LATEST" ]; then + NEW_PATCH=$PATCH + else + LATEST_VER="${LATEST#v}" + IFS='.' read -r _ _ LATEST_PATCH <<< "$LATEST_VER" + NEW_PATCH=$((LATEST_PATCH + 1)) + fi + PATCH=$NEW_PATCH + ;; + esac + + NEW="${MAJOR}.${MINOR}.${PATCH}" + echo "version=${NEW}" >> "$GITHUB_OUTPUT" + echo "tag=v${NEW}" >> "$GITHUB_OUTPUT" + echo "Computed version: ${NEW} (v${NEW})" validate: name: Validate needs: changes if: needs.changes.outputs.rust == 'true' - runs-on: ubuntu-latest + runs-on: [self-hosted, netty] steps: - uses: actions/checkout@v4 @@ -88,9 +125,6 @@ jobs: - name: Install site dependencies run: pnpm --dir site install --frozen-lockfile - - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev - - name: Format check run: make fmt-check @@ -107,7 +141,7 @@ jobs: name: Integration (Xvfb) needs: changes if: needs.changes.outputs.rust == 'true' - runs-on: ubuntu-latest + runs-on: [self-hosted, netty] steps: - uses: actions/checkout@v4 @@ -115,9 +149,6 @@ jobs: - uses: Swatinem/rust-cache@v2 - - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev xvfb - - name: Xvfb integration tests run: make test-integration @@ -125,7 +156,7 @@ jobs: name: Distribution Validate needs: changes if: needs.changes.outputs.rust == 'true' - runs-on: ubuntu-latest + runs-on: [self-hosted, netty] steps: - uses: actions/checkout@v4 @@ -137,24 +168,64 @@ jobs: with: node-version: 22 - - uses: cachix/install-nix-action@v30 - with: - extra_nix_config: | - experimental-features = nix-command flakes - - - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev - - name: Distribution validation run: make dist-validate - build: - name: Build Release Asset + # --- Release pipeline: update-manifests -> build -> release -> publish --- + # These stay on ubuntu-latest for artifact upload/download and registry publishing. + + update-manifests: + name: Update Manifests needs: [changes, validate, integration, distribution] if: github.event_name != 'pull_request' && needs.changes.outputs.rust == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - uses: dtolnay/rust-toolchain@stable + + - uses: actions/setup-node@v4 + with: + node-version: 22 + + - name: Update versions + run: | + CURRENT=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') + NEW="${{ needs.changes.outputs.version }}" + if [ "$CURRENT" != "$NEW" ]; then + sed -i "0,/^version = \"${CURRENT}\"/s//version = \"${NEW}\"/" Cargo.toml + cargo generate-lockfile + fi + node -e ' + const fs = require("node:fs"); + const p = "npm/deskctl/package.json"; + const pkg = JSON.parse(fs.readFileSync(p, "utf8")); + pkg.version = process.argv[1]; + fs.writeFileSync(p, JSON.stringify(pkg, null, 2) + "\n"); + ' "$NEW" + + - name: Commit, tag, and push + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git add Cargo.toml Cargo.lock npm/deskctl/package.json + if ! git diff --cached --quiet; then + git commit -m "release: ${{ needs.changes.outputs.tag }} [skip ci]" + fi + git tag "${{ needs.changes.outputs.tag }}" + git push origin main --tags + + build: + name: Build Release Asset + needs: [changes, update-manifests] + if: github.event_name != 'pull_request' && needs.changes.outputs.rust == 'true' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.changes.outputs.tag }} - uses: dtolnay/rust-toolchain@stable with: @@ -165,6 +236,16 @@ jobs: - name: Install system dependencies run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev + - name: Verify version + run: | + CARGO_VER=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') + EXPECTED="${{ needs.changes.outputs.version }}" + if [ "$CARGO_VER" != "$EXPECTED" ]; then + echo "Version mismatch: Cargo.toml=$CARGO_VER expected=$EXPECTED" + exit 1 + fi + echo "Building version $CARGO_VER" + - name: Clippy run: cargo clippy -- -D warnings @@ -179,8 +260,8 @@ jobs: release: name: Release - needs: [changes, build] - if: github.event_name != 'pull_request' + needs: [changes, build, update-manifests] + if: github.event_name != 'pull_request' && needs.changes.outputs.rust == 'true' runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 @@ -209,3 +290,75 @@ jobs: artifacts/deskctl-linux-x86_64 \ artifacts/checksums.txt fi + + publish-npm: + name: Publish npm + needs: [changes, update-manifests, release] + if: >- + github.event_name != 'pull_request' + && needs.changes.outputs.rust == 'true' + && (inputs.publish_npm == true || inputs.publish_npm == '') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.changes.outputs.tag }} + + - uses: actions/setup-node@v4 + with: + node-version: 22 + registry-url: https://registry.npmjs.org + + - name: Check if already published + id: published + run: | + VERSION="${{ needs.changes.outputs.version }}" + if npm view "deskctl@${VERSION}" version >/dev/null 2>&1; then + echo "npm=true" >> "$GITHUB_OUTPUT" + else + echo "npm=false" >> "$GITHUB_OUTPUT" + fi + + - name: Validate npm package + if: steps.published.outputs.npm != 'true' + run: node npm/deskctl/scripts/validate-package.js + + - name: Publish npm + if: steps.published.outputs.npm != 'true' + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: npm publish ./npm/deskctl --access public + + publish-crates: + name: Publish crates.io + needs: [changes, update-manifests, release] + if: >- + github.event_name != 'pull_request' + && needs.changes.outputs.rust == 'true' + && (inputs.publish_crates == true || inputs.publish_crates == '') + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + with: + ref: ${{ needs.changes.outputs.tag }} + + - uses: dtolnay/rust-toolchain@stable + + - name: Install system dependencies + run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev + + - name: Check if already published + id: published + run: | + VERSION="${{ needs.changes.outputs.version }}" + if curl -fsSL "https://crates.io/api/v1/crates/deskctl/${VERSION}" >/dev/null 2>&1; then + echo "crates=true" >> "$GITHUB_OUTPUT" + else + echo "crates=false" >> "$GITHUB_OUTPUT" + fi + + - name: Publish crates.io + if: steps.published.outputs.crates != 'true' + env: + CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} + run: cargo publish --locked diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml deleted file mode 100644 index 60aed4d..0000000 --- a/.github/workflows/publish.yml +++ /dev/null @@ -1,127 +0,0 @@ -name: Publish Registries - -on: - workflow_dispatch: - inputs: - bump: - description: Version bump type - required: true - type: choice - options: - - patch - - minor - - major - publish_npm: - description: Publish deskctl to npm - required: true - type: boolean - default: true - publish_crates: - description: Publish deskctl to crates.io - required: true - type: boolean - default: false - -permissions: - contents: read - -jobs: - publish: - runs-on: ubuntu-latest - permissions: - contents: write - steps: - - uses: actions/checkout@v4 - with: - fetch-depth: 0 - - - uses: dtolnay/rust-toolchain@stable - - - uses: actions/setup-node@v4 - with: - node-version: 22 - registry-url: https://registry.npmjs.org - - - name: Install system dependencies - run: sudo apt-get update && sudo apt-get install -y libx11-dev libxtst-dev - - - name: Compute next version - id: version - run: | - CURRENT=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') - IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" - - case "${{ inputs.bump }}" in - major) MAJOR=$((MAJOR + 1)); MINOR=0; PATCH=0 ;; - minor) MINOR=$((MINOR + 1)); PATCH=0 ;; - patch) PATCH=$((PATCH + 1)) ;; - esac - - NEW="${MAJOR}.${MINOR}.${PATCH}" - TAG="v${NEW}" - - echo "version=${NEW}" >> "$GITHUB_OUTPUT" - echo "tag=${TAG}" >> "$GITHUB_OUTPUT" - echo "Bumping ${CURRENT} -> ${NEW} (${TAG})" - - - name: Bump versions - run: | - NEW="${{ steps.version.outputs.version }}" - CURRENT=$(grep '^version' Cargo.toml | head -1 | sed 's/.*"\(.*\)"/\1/') - sed -i "0,/^version = \"${CURRENT}\"/s//version = \"${NEW}\"/" Cargo.toml - node -e 'const fs=require("node:fs"); const p="npm/deskctl/package.json"; const pkg=JSON.parse(fs.readFileSync(p,"utf8")); pkg.version=process.argv[1]; fs.writeFileSync(p, JSON.stringify(pkg, null, 2)+"\n");' "$NEW" - cargo generate-lockfile - - - name: Commit, tag, and push - run: | - git config user.name "github-actions[bot]" - git config user.email "github-actions[bot]@users.noreply.github.com" - git add Cargo.toml Cargo.lock npm/deskctl/package.json - git commit -m "release: ${{ steps.version.outputs.tag }} [skip ci]" - git tag "${{ steps.version.outputs.tag }}" - git push origin main --tags - - - name: Check current published state - id: published - run: | - VERSION="${{ steps.version.outputs.version }}" - - if npm view "deskctl@${VERSION}" version >/dev/null 2>&1; then - echo "npm=true" >> "$GITHUB_OUTPUT" - else - echo "npm=false" >> "$GITHUB_OUTPUT" - fi - - if curl -fsSL "https://crates.io/api/v1/crates/deskctl/${VERSION}" >/dev/null 2>&1; then - echo "crates=true" >> "$GITHUB_OUTPUT" - else - echo "crates=false" >> "$GITHUB_OUTPUT" - fi - - - name: Validate npm package - run: | - mkdir -p ./tmp/npm-pack - node npm/deskctl/scripts/validate-package.js - npm pack ./npm/deskctl --pack-destination ./tmp/npm-pack >/dev/null - - - name: Validate crate publish path - run: cargo publish --dry-run --locked - - - name: Publish npm - if: inputs.publish_npm && steps.published.outputs.npm != 'true' - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: npm publish ./npm/deskctl --access public - - - name: Publish crates.io - if: inputs.publish_crates && steps.published.outputs.crates != 'true' - env: - CARGO_REGISTRY_TOKEN: ${{ secrets.CARGO_REGISTRY_TOKEN }} - run: cargo publish --locked - - - name: Summary - run: | - echo "tag=${{ steps.version.outputs.tag }}" - echo "bump=${{ inputs.bump }}" - echo "npm_already_published=${{ steps.published.outputs.npm }}" - echo "crates_already_published=${{ steps.published.outputs.crates }}" diff --git a/Cargo.lock b/Cargo.lock index 3fb1666..eb0e2ce 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -241,9 +241,9 @@ checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" -version = "1.2.57" +version = "1.2.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a0dd1ca384932ff3641c8718a02769f1698e7563dc6974ffd03346116310423" +checksum = "e1e928d4b69e3077709075a938a05ffbedfa53a84c8f766efbf8220bb1ff60e1" dependencies = [ "find-msvc-tools", "jobserver", @@ -400,7 +400,7 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" [[package]] name = "deskctl" -version = "0.1.8" +version = "0.1.14" dependencies = [ "ab_glyph", "anyhow", @@ -911,9 +911,9 @@ dependencies = [ [[package]] name = "js-sys" -version = "0.3.91" +version = "0.3.92" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "cc4c90f45aa2e6eacbe8645f77fdea542ac97a494bcd117a67df9ff4d611f995" dependencies = [ "once_cell", "wasm-bindgen", @@ -1039,9 +1039,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -1699,9 +1699,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simd_helpers" @@ -1861,9 +1861,9 @@ checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "uuid" -version = "1.22.0" +version = "1.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a68d3c8f01c0cfa54a75291d83601161799e4a89a39e0929f4b0354d88757a37" +checksum = "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9" dependencies = [ "getrandom 0.4.2", "js-sys", @@ -1907,9 +1907,9 @@ dependencies = [ [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "6523d69017b7633e396a89c5efab138161ed5aafcbc8d3e5c5a42ae38f50495a" dependencies = [ "cfg-if", "once_cell", @@ -1920,9 +1920,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "4e3a6c758eb2f701ed3d052ff5737f5bfe6614326ea7f3bbac7156192dc32e67" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -1930,9 +1930,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "921de2737904886b52bcbb237301552d05969a6f9c40d261eb0533c8b055fedf" dependencies = [ "bumpalo", "proc-macro2", @@ -1943,9 +1943,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.115" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "a93e946af942b58934c604527337bad9ae33ba1d5c6900bbb41c2c07c2364a93" dependencies = [ "unicode-ident", ] @@ -2297,9 +2297,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7a1c0af6e5d8d1363f4994b7a091ccf963d8b694f7da5b0b9cceb82da2c0a6" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index fc7816c..be051c7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "deskctl" -version = "0.1.8" +version = "0.1.14" edition = "2021" description = "X11 desktop control CLI for agents" license = "MIT" diff --git a/README.md b/README.md index 935f329..dccbe04 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,11 @@ # deskctl - [![npm](https://img.shields.io/npm/v/deskctl?label=npm)](https://www.npmjs.com/package/deskctl) [![skill](https://img.shields.io/badge/skills.sh-deskctl-111827)](skills/deskctl) -Desktop control cli for AI agents on Linux X11. +Desktop control cli for AI agents on X11. + +https://github.com/user-attachments/assets/e820787e-4d1a-463f-bdcf-a829588778bf + ## Install diff --git a/demo/index.html b/demo/index.html new file mode 100644 index 0000000..70ac230 --- /dev/null +++ b/demo/index.html @@ -0,0 +1,969 @@ + + + + + +deskctl - Desktop Control for AI Agents + + + + +
+

deskctl

+

desktop control CLI for AI agents

+
+ +
+
+
+
+
+
+
+
+
+ + +
+
+
+ Files ~/reports +
+
+
+
+ 📝 + task_brief.txt + 2.1 KB +
+
+ 📊 + nvda_q1_data.csv + 48 KB +
+
+ 📄 + prev_report.pdf + 1.2 MB +
+
+ 📁 + archive/ + -- +
+
+
+ task: Prepare NVDA Q1 earnings summary
+ source: finance.yahoo.com, local csv
+ output: Google Docs report with chart +
+
+
+ + +
+
+
+ Chrome - Yahoo Finance +
+
+
+ NVDA + $924.68 + +3.42% + 1Y +
+
+ + + + + + + + + + + + + $950 + $800 + $650 + +
+
+
+
+ + +
+
+
+ Chrome - Google Docs +
+
+
+
+
+
+
+
+
+
+
+
+
+
+ + + + + + + + + + + NVDA 1Y + +
+
+
+
+ + +
@w1
+
@w2
+
@w3
+ +
+ +
+ +
+
+ + +
+
Files
+
Yahoo Finance
+
Google Docs
+
+
+
+ +
+
+
+
+
+ agent computer +
+
+
+
+ +
+

AI agent controlling a live desktop via deskctl

+ +
+ + + + diff --git a/docs/releasing.md b/docs/releasing.md index 8f39d3f..849d661 100644 --- a/docs/releasing.md +++ b/docs/releasing.md @@ -59,12 +59,12 @@ The repository release workflow: - publishes the canonical GitHub Release asset - uploads `checksums.txt` -The registry publish workflow: +The registry publish jobs (npm and crates.io run in parallel): -- targets an existing release tag -- checks that Cargo, npm, and the requested tag all agree on version -- checks whether that version is already published on npm and crates.io -- only publishes the channels explicitly requested +- target an existing release tag +- check whether that version is already published on the respective registry +- skip already-published versions +- both default to enabled; can be toggled via workflow_dispatch inputs ## Rerun Safety diff --git a/npm/deskctl/README.md b/npm/deskctl/README.md index 7bb42a9..81f07f4 100644 --- a/npm/deskctl/README.md +++ b/npm/deskctl/README.md @@ -14,6 +14,18 @@ After install, run: deskctl --help ``` +To upgrade version: + +```bash +deskctl upgrade +``` + +For non-interactive use: + +```bash +deskctl upgrade --yes +``` + One-shot usage is also supported: ```bash diff --git a/npm/deskctl/package.json b/npm/deskctl/package.json index 45daefe..c676924 100644 --- a/npm/deskctl/package.json +++ b/npm/deskctl/package.json @@ -1,6 +1,6 @@ { "name": "deskctl", - "version": "0.1.8", + "version": "0.1.14", "description": "Installable deskctl package for Linux X11 agents", "license": "MIT", "homepage": "https://github.com/harivansh-afk/deskctl", diff --git a/site/src/layouts/DocLayout.astro b/site/src/layouts/DocLayout.astro index f2608de..afc8648 100644 --- a/site/src/layouts/DocLayout.astro +++ b/site/src/layouts/DocLayout.astro @@ -30,7 +30,7 @@ function formatTocText(text: string): string { { !isIndex && ( -