Compare commits

..

3 Commits

Author SHA1 Message Date
GitHub Action
d4e76e2d1a chore: bump version to 0.0.145 2026-01-06 19:08:41 +00:00
Solomon
8ea7c6fec5 test build 2026-01-06 19:07:34 +00:00
Solomon
0fd7e016a4 opencode sdk wip 2026-01-06 19:04:50 +00:00
2231 changed files with 59687 additions and 234896 deletions

View File

@@ -1,15 +1,12 @@
# Node modules
node_modules/
**/node_modules
.pnpm-store/
.pnpm/
npm-debug.log*
yarn-debug.log*
yarn-error.log*
lerna-debug.log*
# Build artifacts
**/target/
target/
dist/
build/
*.tgz
@@ -36,12 +33,6 @@ Thumbs.db
.git/
.gitignore
# Repo content not needed for Docker builds
.github/
docs/
dev_assets/
dev_assets_seed/
# Docker
Dockerfile*
.dockerignore
@@ -68,4 +59,4 @@ coverage/
# Temporary folders
tmp/
temp/
temp/

View File

@@ -1,163 +0,0 @@
name: container
# Build the self-hosted images from THIS repo and publish them to the Gitea
# registry:
#
# git.lair.cafe/lair/vibe-kanban-remote crates/remote/Dockerfile :8081
# git.lair.cafe/lair/vibe-kanban-relay crates/relay-tunnel/Dockerfile :8082
#
# This build used to live in lair/containers, which is for *third-party* images
# built from someone else's source. We own this fork now, so the build belongs
# next to the code it builds — a source change and its image are one commit.
#
# Both crates ship their own Dockerfile (context = repo root), so there is no
# vendored Containerfile here. Nothing in this workflow reaches out to GitHub:
# upstream is sunset and may disappear, and a build that asks github.com what to
# build would disappear with it.
on:
push:
branches: [main]
paths:
- "crates/**"
- "packages/**"
- "shared/**"
- "assets/**"
- "patches/**"
- "Cargo.toml"
- "Cargo.lock"
- "package.json"
- "pnpm-lock.yaml"
- "pnpm-workspace.yaml"
- "rust-toolchain.toml"
- ".gitea/workflows/container.yml"
workflow_dispatch:
# Never let two builds race: they would fight over the :latest tags and the
# loser's digest would win at random.
concurrency:
group: container
cancel-in-progress: false
jobs:
remote:
runs-on:
- metal
- podman
# The Rust workspace builds in release mode; from a cold cache the default
# job timeout is not enough headroom.
timeout-minutes: 120
steps:
- uses: actions/checkout@v4
- name: derive image tags
id: meta
run: |
set -euo pipefail
version=$(jq -r .version package.json)
short=$(git rev-parse --short HEAD)
# Immutable tag first. :latest is a convenience for humans, never a
# deployment target -- a floating tag decides which build you are
# running based on whenever something last pulled.
echo "immutable=${version}-g${short}" >> "$GITHUB_OUTPUT"
- name: login to registry
run: podman login -u ${{ gitea.actor }} -p ${{ secrets.REGISTRY_TOKEN }} git.lair.cafe
- name: build
env:
IMMUTABLE: ${{ steps.meta.outputs.immutable }}
run: |
set -euo pipefail
IMAGE=git.lair.cafe/lair/vibe-kanban-remote
# FEATURES is deliberately unset: the Dockerfile strips the private
# billing crate only when it is empty (the self-host path), and we have
# no access to BloopAI/vibe-kanban-private.
#
# VITE_RELAY_API_BASE_URL is baked into the SPA at build time and is
# ORIGIN-RELATIVE on purpose. It is not a switch that disables the
# relay -- leaving it empty makes Bootstrap.tsx fall back to
# window.location.origin, which aims relay calls at the remote API,
# which does not serve them. "/relay-api" instead means the SPA calls
# whichever host served it, so one build works unchanged behind both
# kanban.internal and a future public name, with no CORS and no second
# certificate. nginx strips the prefix and forwards to the relay.
podman build --pull=newer \
-f crates/remote/Dockerfile \
--build-arg VITE_RELAY_API_BASE_URL=/relay-api \
-t "${IMAGE}:${IMMUTABLE}" \
-t "${IMAGE}:latest" \
.
- name: verify the sunset really is gone
env:
IMMUTABLE: ${{ steps.meta.outputs.immutable }}
run: |
set -euo pipefail
IMAGE=git.lair.cafe/lair/vibe-kanban-remote
# This fork exists because upstream replaced the board with an
# export-only page. If a merge or a bad rebase ever reinstates it, fail
# here rather than publish an image that greets us with a dead product.
if podman run --rm --entrypoint sh "${IMAGE}:${IMMUTABLE}" -c \
'grep -rlq "Project functionality has been retired" /srv/static' 2>/dev/null; then
echo "ERROR: the sunset page is present in the built bundle."
echo "The revert of #3387 has been lost -- refusing to publish."
exit 1
fi
echo "ok: no sunset page in the bundle"
- name: push
env:
IMMUTABLE: ${{ steps.meta.outputs.immutable }}
run: |
set -euo pipefail
IMAGE=git.lair.cafe/lair/vibe-kanban-remote
podman push "${IMAGE}:${IMMUTABLE}"
podman push "${IMAGE}:latest"
echo "published ${IMAGE}:${IMMUTABLE} (and :latest)"
echo
echo "pin the quadlet to the immutable tag:"
echo " Image=${IMAGE}:${IMMUTABLE}"
relay:
runs-on:
- metal
- podman
timeout-minutes: 120
steps:
- uses: actions/checkout@v4
- name: derive image tags
id: meta
run: |
set -euo pipefail
version=$(jq -r .version package.json)
short=$(git rev-parse --short HEAD)
echo "immutable=${version}-g${short}" >> "$GITHUB_OUTPUT"
- name: login to registry
run: podman login -u ${{ gitea.actor }} -p ${{ secrets.REGISTRY_TOKEN }} git.lair.cafe
- name: build
env:
IMMUTABLE: ${{ steps.meta.outputs.immutable }}
run: |
set -euo pipefail
IMAGE=git.lair.cafe/lair/vibe-kanban-relay
# No build args: the relay takes all of its configuration from the
# environment at runtime (DATABASE_URL, RELAY_LISTEN_ADDR and the JWT
# secret it shares with the remote server). It has no notion of its own
# public address, which is what makes mounting it under a path safe.
podman build --pull=newer \
-f crates/relay-tunnel/Dockerfile \
-t "${IMAGE}:${IMMUTABLE}" \
-t "${IMAGE}:latest" \
.
- name: push
env:
IMMUTABLE: ${{ steps.meta.outputs.immutable }}
run: |
set -euo pipefail
IMAGE=git.lair.cafe/lair/vibe-kanban-relay
podman push "${IMAGE}:${IMMUTABLE}"
podman push "${IMAGE}:latest"
echo "published ${IMAGE}:${IMMUTABLE} (and :latest)"

View File

@@ -1,198 +0,0 @@
name: host-release
# Build the LOCAL vibe-kanban server and publish it as a signed RPM to
# rpm.lair.cafe, so a host installs it with dnf instead of `npx vibe-kanban`.
#
# npx is not an option here: npx-cli resolves its download from R2_BASE_URL, a
# placeholder substituted at npm-publish time to BloopAI's bucket, with no
# runtime override (npx-cli/src/download.ts). That serves upstream's binary --
# which still carries the product sunset -- from infrastructure that disappears
# when they do.
#
# Convention mirrors lair/claude-desktop-package: package and publish on the
# `rpm` runner, sign with RPM_SIGNING_KEY, rsync to oolon, createrepo under
# flock. The compile happens first on a podman runner rather than on the rpm
# runner, so this never depends on a host toolchain matching the pinned
# nightly in rust-toolchain.toml.
on:
push:
branches: [main]
paths:
- "crates/**"
- "packages/local-web/**"
- "packages/web-core/**"
- "packages/ui/**"
- "shared/**"
- "Cargo.toml"
- "Cargo.lock"
- "rust-toolchain.toml"
- "rpm/**"
- ".gitea/workflows/host-release.yml"
workflow_dispatch:
concurrency:
group: host-release
cancel-in-progress: false
jobs:
build:
runs-on:
- metal
- podman
timeout-minutes: 120
steps:
- uses: actions/checkout@v4
- name: derive version
id: meta
run: |
set -euo pipefail
echo "version=$(jq -r .version package.json)" >> "$GITHUB_OUTPUT"
echo "commit=$(git rev-parse --short HEAD)" >> "$GITHUB_OUTPUT"
- name: build the host binary
run: |
set -euo pipefail
# --target export writes the binary straight out of the scratch stage,
# so nothing has to be extracted from an image afterwards.
mkdir -p out
podman build --pull=newer \
-f rpm/Containerfile \
--target export \
--output type=local,dest=out \
.
test -s out/vibe-kanban
chmod 0755 out/vibe-kanban
file out/vibe-kanban || true
- name: sanity-check the binary
run: |
set -euo pipefail
# The whole point of building our own: upstream's carries the sunset.
if grep -qa "Project functionality has been retired" out/vibe-kanban; then
echo "ERROR: the sunset page is embedded in the host binary."
echo "The revert of #3387 has been lost -- refusing to publish."
exit 1
fi
if grep -qa "Cloud is shutting down" out/vibe-kanban; then
echo "ERROR: the cloud-shutdown banner is embedded in the host binary."
exit 1
fi
# And it must point at us, not at api.vibekanban.com.
grep -qa "kanban.internal" out/vibe-kanban \
|| { echo "ERROR: baked API base missing from the binary"; exit 1; }
echo "ok: no sunset UI, lair endpoints baked in"
- uses: actions/upload-artifact@v3
with:
name: host-binary
path: out/vibe-kanban
retention-days: 7
outputs:
version: ${{ steps.meta.outputs.version }}
commit: ${{ steps.meta.outputs.commit }}
package:
needs: build
runs-on: rpm
strategy:
fail-fast: false
matrix:
fedora_version: ["43", "44"]
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
with:
name: host-binary
path: bin/
- name: rpmbuild
run: |
set -euo pipefail
find bin/ -name vibe-kanban -exec mv --target-directory=bin/ {} + 2>/dev/null || true
mkdir -p ~/rpmbuild/SOURCES
install -m 0755 bin/vibe-kanban ~/rpmbuild/SOURCES/vibe-kanban
rpmbuild -bb rpm/vibe-kanban.spec \
--define "vk_version ${{ needs.build.outputs.version }}" \
--define "vk_commit ${{ needs.build.outputs.commit }}" \
--define "dist .fc${{ matrix.fedora_version }}"
ls -l ~/rpmbuild/RPMS/x86_64/
- uses: actions/upload-artifact@v3
with:
name: rpm-fc${{ matrix.fedora_version }}
path: ~/rpmbuild/RPMS/x86_64/*.rpm
retention-days: 7
publish:
needs: package
runs-on: rpm
env:
RPM_REPO_HOST: oolon.kosherinata.internal
strategy:
fail-fast: false
matrix:
fedora_version: ["43", "44"]
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v3
with:
name: rpm-fc${{ matrix.fedora_version }}
path: rpms/
- name: Flatten RPM artifacts
run: |
find rpms/ -name '*.rpm' -exec mv --target-directory=rpms/ {} +
find rpms/ -mindepth 1 -type d -empty -delete
- name: Import signing key
run: |
echo "${{ secrets.RPM_SIGNING_KEY }}" | gpg --batch --import
fpr=$(gpg --batch --with-colons --list-keys "${{ secrets.RPM_SIGNING_KEY_ID }}" | awk -F: '/^fpr:/ { print $10; exit }')
echo "${fpr}:6:" | gpg --batch --import-ownertrust
sed "s/@GPG_NAME@/${{ secrets.RPM_SIGNING_KEY_ID }}/" rpm/rpmmacros > ~/.rpmmacros
- name: Sign RPMs
run: |
for rpm in rpms/*.rpm; do
echo "signing ${rpm}..."
rpm --addsign "${rpm}"
done
- name: Set up SSH
run: |
install --directory --mode 700 ~/.ssh
echo "${RSYNC_SSH_KEY}" | install --mode 600 /dev/stdin ~/.ssh/id_ed25519
env:
RSYNC_SSH_KEY: ${{ secrets.RSYNC_SSH_KEY }}
- name: Test SSH connectivity
run: |
ssh -o StrictHostKeyChecking=accept-new "gitea_ci@${RPM_REPO_HOST}" exit
- name: Sync RPMs to repo
run: |
rsync \
--archive \
--verbose \
--chmod D755,F644 \
rpms/*.rpm \
"gitea_ci@${RPM_REPO_HOST}:/var/www/rpm/fedora/${{ matrix.fedora_version }}/x86_64/"
- name: Update repo metadata
run: |
# flock guards createrepo against concurrent publishes into the
# shared repo tree (other package repos publish here too).
ssh "gitea_ci@${RPM_REPO_HOST}" \
"flock /var/www/rpm/.publish.lock -c 'cd /var/www/rpm/fedora/${{ matrix.fedora_version }}/x86_64 && createrepo_c --update .'"
- name: Generate packages.json
run: |
scp rpm/generate-packages-json.py "gitea_ci@${RPM_REPO_HOST}:/tmp/"
ssh "gitea_ci@${RPM_REPO_HOST}" \
"flock /var/www/rpm/.publish.lock -c 'python3 /tmp/generate-packages-json.py \
--repodata-dir /var/www/rpm/fedora/${{ matrix.fedora_version }}/x86_64/repodata \
--output /var/www/rpm/fedora/${{ matrix.fedora_version }}/x86_64/packages.json \
--base-url https://rpm.lair.cafe/fedora/${{ matrix.fedora_version }}/x86_64'"

View File

@@ -1,64 +0,0 @@
name: 'Cargo checks common setup'
description: 'Common setup for cargo checks in CI'
inputs:
toolchain:
description: 'Rust toolchain version'
required: true
components:
description: 'Comma-separated rustup components'
required: false
default: ''
cache-key:
description: 'Shared key for rust-cache'
required: true
setup-node:
description: 'Whether to install Node.js and pnpm'
required: false
default: 'false'
setup-sqlx-cli:
description: 'Whether to install sqlx-cli'
required: false
default: 'false'
runs:
using: 'composite'
steps:
- name: Setup Node
if: ${{ inputs.setup-node == 'true' }}
uses: ./.github/actions/setup-node
- name: Setup sccache
uses: BloopAI/sccache-action@main
- name: Install Rust toolchain
if: ${{ inputs.components == '' }}
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
with:
toolchain: ${{ inputs.toolchain }}
- name: Install Rust toolchain with components
if: ${{ inputs.components != '' }}
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
with:
toolchain: ${{ inputs.toolchain }}
components: ${{ inputs.components }}
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@e18b497796c12c097a38f9edb9d0641fb99eee32 # v2
env:
RUST_CACHE_DEBUG: true
with:
workspaces: "."
cache-provider: "github"
cache-on-failure: true
shared-key: ${{ inputs.cache-key }}
cache-all-crates: true
- name: Install sqlx-cli
if: ${{ inputs.setup-sqlx-cli == 'true' }}
uses: taiki-e/cache-cargo-install-action@34ce5120836e5f9f1508d8713d7fdea0e8facd6f # v3.0.1
with:
tool: sqlx-cli
no-default-features: true
features: sqlite,postgres

View File

@@ -1,43 +0,0 @@
name: 'Setup Jsign'
description: 'Downloads and caches Jsign for cross-platform Windows code signing'
inputs:
version:
description: 'Jsign version'
required: false
default: '7.4'
sha256:
description: 'Expected SHA256 of the jar'
required: false
default: '2abf2ade9ea322acc2d60c24794eadc465ff9380938fca4c932d09e0b25f1c28'
outputs:
jar-path:
description: 'Path to the Jsign jar'
value: ${{ steps.path.outputs.jar }}
runs:
using: 'composite'
steps:
- name: Set jar path
id: path
shell: bash
run: echo "jar=${{ runner.temp }}/jsign-${{ inputs.version }}.jar" >> $GITHUB_OUTPUT
- name: Cache Jsign
id: cache
uses: actions/cache@v5
with:
path: ${{ steps.path.outputs.jar }}
key: jsign-${{ inputs.version }}-${{ inputs.sha256 }}
- name: Download Jsign
if: steps.cache.outputs.cache-hit != 'true'
shell: bash
run: |
curl -sL "https://github.com/ebourg/jsign/releases/download/${{ inputs.version }}/jsign-${{ inputs.version }}.jar" \
-o "${{ steps.path.outputs.jar }}"
- name: Verify checksum
shell: bash
run: echo "${{ inputs.sha256 }} ${{ steps.path.outputs.jar }}" | sha256sum -c -

View File

@@ -11,7 +11,7 @@ runs:
registry-url: 'https://registry.npmjs.org'
- name: Setup pnpm
uses: pnpm/action-setup@fc06bc1257f339d1d5d8b3a19a8cae5388b55320 # v4
uses: pnpm/action-setup@v4
with:
version: ${{ env.PNPM_VERSION }}
@@ -24,6 +24,6 @@ runs:
uses: actions/cache@v4
with:
path: ${{ env.STORE_PATH }}
key: ${{ runner.os }}-${{ runner.arch }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
key: ${{ runner.os }}-pnpm-store-${{ hashFiles('**/pnpm-lock.yaml') }}
restore-keys: |
${{ runner.os }}-${{ runner.arch }}-pnpm-store-
${{ runner.os }}-pnpm-store-

523
.github/workflows/pre-release.yml vendored Normal file
View File

@@ -0,0 +1,523 @@
name: Create GitHub Pre-Release
on:
workflow_dispatch:
inputs:
version_type:
description: "Version bump type"
required: true
default: "patch"
type: choice
options:
- patch
- minor
- major
- prerelease
concurrency:
group: release-${{ github.ref_name }} # allow concurrent prerelease from different branches
cancel-in-progress: true
permissions:
contents: write
packages: write
pull-requests: write
env:
NODE_VERSION: 22
PNPM_VERSION: 10.13.1
RUST_TOOLCHAIN: nightly-2025-12-04
CARGO_XWIN_VERSION: 0.20.2
jobs:
bump-version:
runs-on: ubuntu-latest-m
outputs:
new_tag: ${{ steps.version.outputs.new_tag }}
new_version: ${{ steps.version.outputs.new_version }}
branch_suffix: ${{ steps.branch.outputs.suffix }}
steps:
- name: Cache cargo-edit
uses: actions/cache@v3
id: cache-cargo-edit
with:
path: ~/.cargo/bin/cargo-set-version
key: cargo-edit-${{ runner.os }}-${{ env.RUST_TOOLCHAIN }}
- name: Install cargo-edit
if: steps.cache-cargo-edit.outputs.cache-hit != 'true'
run: cargo install cargo-edit
- uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
ssh-key: ${{ secrets.DEPLOY_KEY }}
- name: setup node
uses: ./.github/actions/setup-node
- name: Generate branch suffix
id: branch
run: |
branch_name="${{ github.ref_name }}"
# Get last 6 characters of branch name, remove all special chars (including dashes)
suffix=$(echo "$branch_name" | tail -c 7 | sed 's/[^a-zA-Z0-9]//g' | tr '[:upper:]' '[:lower:]')
echo "Branch: $branch_name"
echo "Suffix: $suffix"
echo "suffix=$suffix" >> $GITHUB_OUTPUT
- name: Determine and update versions
id: version
run: |
# Get the latest version from npm registry
latest_npm_version=$(npm view vibe-kanban version 2>/dev/null || echo "0.0.0")
echo "Latest npm version: $latest_npm_version"
timestamp=$(date +%Y%m%d%H%M%S)
# Update root package.json based on npm version, not current package.json
if [[ "${{ github.event.inputs.version_type }}" == "prerelease" ]]; then
# For prerelease, use current package.json version and add branch suffix
npm version prerelease --preid="${{ steps.branch.outputs.suffix }}" --no-git-tag-version
new_version=$(node -p "require('./package.json').version")
new_tag="v${new_version}.${timestamp}"
else
# For regular releases, use npm version and bump it
npm version $latest_npm_version --no-git-tag-version --allow-same-version
npm version ${{ github.event.inputs.version_type }} --no-git-tag-version
new_version=$(node -p "require('./package.json').version")
new_tag="v${new_version}-${timestamp}"
fi
# Update npx-cli package.json to match
cd npx-cli
npm version $new_version --no-git-tag-version --allow-same-version
cd ..
# Update frontend package.json to match
cd frontend
npm version $new_version --no-git-tag-version --allow-same-version
cd ..
cargo set-version --workspace "$new_version"
echo "New version: $new_version"
echo "new_version=$new_version" >> $GITHUB_OUTPUT
echo "new_tag=$new_tag" >> $GITHUB_OUTPUT
- name: Commit changes and create tag
run: |
git config --local user.email "action@github.com"
git config --local user.name "GitHub Action"
git add package.json pnpm-lock.yaml npx-cli/package.json frontend/package.json Cargo.lock
git add $(find . -name Cargo.toml)
git commit -m "chore: bump version to ${{ steps.version.outputs.new_version }}"
git tag -a ${{ steps.version.outputs.new_tag }} -m "Release ${{ steps.version.outputs.new_tag }}"
git push
git push --tags
build-frontend:
needs: bump-version
runs-on: ubuntu-latest-x64-l
env:
VITE_PUBLIC_REACT_VIRTUOSO_LICENSE_KEY: ${{ secrets.PUBLIC_REACT_VIRTUOSO_LICENSE_KEY }}
VITE_VK_SHARED_API_BASE: ${{ secrets.VK_SHARED_API_BASE }}
steps:
- uses: actions/checkout@v4
with:
ref: 0
- name: Setup Node
uses: ./.github/actions/setup-node
- name: Install dependencies
run: pnpm install
- name: Lint frontend
run: cd frontend && npm run lint
- name: Type check frontend
run: cd frontend && npx tsc --noEmit
- name: Build frontend
run: cd frontend && npm run build
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
VITE_POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
VITE_POSTHOG_API_ENDPOINT: ${{ secrets.POSTHOG_API_ENDPOINT }}
- name: Upload frontend artifact
uses: actions/upload-artifact@v4
with:
name: frontend-dist
path: frontend/dist/
retention-days: 1
build-backend:
needs: [build-frontend]
runs-on: ${{ matrix.os }}
strategy:
# Platform matrix - keep target/name in sync with package-npx-cli job
matrix:
include:
- target: x86_64-unknown-linux-musl
os: ubuntu-latest-x64-l
name: linux-x64
- target: aarch64-unknown-linux-musl
os: ubuntu-latest-arm64-l
name: linux-arm64
- target: x86_64-pc-windows-msvc
os: ubuntu-latest-x64-l
name: windows-x64
- target: x86_64-apple-darwin
os: macos-15-xlarge
name: macos-x64
- target: aarch64-apple-darwin
os: macos-15-xlarge
name: macos-arm64
- target: aarch64-pc-windows-msvc
os: ubuntu-latest-x64-l
name: windows-arm64
env:
CARGO_INCREMENTAL: "0"
SCCACHE_GHA_ENABLED: "true"
RUSTC_WRAPPER: "sccache"
SCCACHE_CACHE_SIZE: "10G"
CARGO_HOME: ${{ github.workspace }}/.cargo
RUSTUP_HOME: ${{ github.workspace }}/.rustup
XWIN_CACHE_DIR: ${{ github.workspace }}/.xwin-cache
steps:
- uses: actions/checkout@v4
- name: Setup sccache
uses: BloopAI/sccache-action@main
- name: Cache Rust toolchain
uses: actions/cache@v4
with:
path: .rustup/toolchains
key: rust-toolchain-${{ runner.os }}-${{ matrix.target }}-${{ env.RUST_TOOLCHAIN }}
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: ${{ env.RUST_TOOLCHAIN }}
targets: ${{ matrix.target }}
components: rustfmt, clippy
- name: Install dependencies (Linux)
if: runner.os == 'Linux'
run: |
sudo apt-get update
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y clang libclang-dev lld llvm nasm cmake ninja-build
if [[ "${{ matrix.target }}" == *"windows"* ]]; then
DEBIAN_FRONTEND=noninteractive sudo apt-get install -y clang-19 clang-tools-19 llvm-19 lld-19
echo "/usr/lib/llvm-19/bin" >> $GITHUB_PATH
fi
- name: Cache Cargo registry
uses: actions/cache@v5
with:
path: |
.cargo/registry/cache
.cargo/registry/index
.cargo/git/db
.cargo/bin
.cargo/.crates.toml
.cargo/.crates2.json
key: cargo-${{ runner.os }}-${{ env.RUST_TOOLCHAIN }}-${{ matrix.target }}-${{ hashFiles('**/Cargo.lock') }}
restore-keys: |
cargo-${{ runner.os }}-${{ env.RUST_TOOLCHAIN }}-${{ matrix.target }}-
- name: Install Zig
if: runner.os == 'Linux' && !contains(matrix.target, 'windows')
uses: BloopAI/setup-zig@main
with:
version: 0.15.2
- name: Install cargo zigbuild
if: runner.os == 'Linux' && !contains(matrix.target, 'windows')
run: cargo install --locked cargo-zigbuild --version 0.20.1
- name: Install cargo xwin
if: runner.os == 'Linux' && contains(matrix.target, 'windows')
run: cargo install --locked cargo-xwin --version ${{ env.CARGO_XWIN_VERSION }}
- name: Cache xwin downloads
if: runner.os == 'Linux' && contains(matrix.target, 'windows')
uses: actions/cache@v5
with:
path: ${{ github.workspace }}/.xwin-cache
key: xwin-${{ runner.os }}-${{ matrix.target }}-cargo-xwin-${{ env.CARGO_XWIN_VERSION }}
- name: Cache target
uses: actions/cache@v5
with:
path: target
key: target-${{ runner.os }}-${{ env.RUST_TOOLCHAIN }}-${{ matrix.target }}-${{ github.ref_name }}-${{ github.sha }}
restore-keys: |
target-${{ runner.os }}-${{ env.RUST_TOOLCHAIN }}-${{ matrix.target }}-${{ github.ref_name }}-
target-${{ runner.os }}-${{ env.RUST_TOOLCHAIN }}-${{ matrix.target }}-
- name: Setup cargo-sweep
shell: bash
run: |
cargo install --locked cargo-sweep --version 0.8.0
- name: Download frontend artifact
uses: actions/download-artifact@v4
with:
name: frontend-dist
path: frontend/dist/
- name: Build backend (Linux)
if: runner.os == 'Linux' && !contains(matrix.target, 'windows')
run: |
cargo zigbuild --release --target ${{ matrix.target }} -p server -p review --bin server --bin mcp_task_server --bin review
env:
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
POSTHOG_API_ENDPOINT: ${{ secrets.POSTHOG_API_ENDPOINT }}
VK_SHARED_API_BASE: ${{ secrets.VK_SHARED_API_BASE }}
- name: Build backend (macOS)
if: runner.os == 'macOS'
run: |
if [[ "${{ matrix.target }}" == "x86_64-apple-darwin" ]]; then
export MACOSX_DEPLOYMENT_TARGET=10.12
elif [[ "${{ matrix.target }}" == "aarch64-apple-darwin" ]]; then
export MACOSX_DEPLOYMENT_TARGET=11.0
fi
cargo build --release --target ${{ matrix.target }} -p server -p review --bin server --bin mcp_task_server --bin review
env:
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
POSTHOG_API_ENDPOINT: ${{ secrets.POSTHOG_API_ENDPOINT }}
VK_SHARED_API_BASE: ${{ secrets.VK_SHARED_API_BASE }}
- name: Build backend (Windows)
if: runner.os == 'Linux' && contains(matrix.target, 'windows')
run: |
cargo xwin build --release --target ${{ matrix.target }} -p server -p review --bin server --bin mcp_task_server --bin review
env:
POSTHOG_API_KEY: ${{ secrets.POSTHOG_API_KEY }}
POSTHOG_API_ENDPOINT: ${{ secrets.POSTHOG_API_ENDPOINT }}
VK_SHARED_API_BASE: ${{ secrets.VK_SHARED_API_BASE }}
# Avoid aws-lc-sys CMake failures when Rust's `release` profile includes debug info.
# Without this, cmake-rs selects RelWithDebInfo and CMake fails when ASM is enabled.
CARGO_PROFILE_RELEASE_DEBUG: 0
# - name: Setup Sentry CLI
# uses: matbour/setup-sentry-cli@v2
# with:
# token: ${{ secrets.SENTRY_AUTH_TOKEN }}
# organization: ${{ secrets.SENTRY_ORG }}
# project: ${{ secrets.SENTRY_PROJECT }}
# version: 2.21.2
# - name: Upload source maps to Sentry
# run: sentry-cli debug-files upload --include-sources target/${{ matrix.target }}/release
- name: Prepare binaries (non-macOS)
if: runner.os != 'macOS'
shell: bash
run: |
mkdir -p dist
if [[ "${{ matrix.name }}" == *"windows"* ]]; then
cp target/${{ matrix.target }}/release/server.exe dist/vibe-kanban-${{ matrix.name }}.exe
cp target/${{ matrix.target }}/release/mcp_task_server.exe dist/vibe-kanban-mcp-${{ matrix.name }}.exe
cp target/${{ matrix.target }}/release/review.exe dist/vibe-kanban-review-${{ matrix.name }}.exe
else
cp target/${{ matrix.target }}/release/server dist/vibe-kanban-${{ matrix.name }}
cp target/${{ matrix.target }}/release/mcp_task_server dist/vibe-kanban-mcp-${{ matrix.name }}
cp target/${{ matrix.target }}/release/review dist/vibe-kanban-review-${{ matrix.name }}
fi
# Code signing for macOS only
- name: Prepare Apple certificate (macOS)
if: runner.os == 'macOS'
run: |
echo "${{ secrets.APPLE_CERTIFICATE_P12_BASE64 }}" | base64 --decode > certificate.p12
- name: Write API Key to file
if: runner.os == 'macOS'
env:
API_KEY: ${{ secrets.APP_STORE_API_KEY }}
run: echo $API_KEY > app_store_key.json
- name: Sign main binary (macOS)
if: runner.os == 'macOS'
uses: BloopAI/apple-code-sign-action@v1
with:
input_path: target/${{ matrix.target }}/release/server
output_path: vibe-kanban
p12_file: certificate.p12
p12_password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
sign: true
sign_args: "--code-signature-flags=runtime"
- name: Package main binary (macOS)
if: runner.os == 'macOS'
run: zip vibe-kanban.zip vibe-kanban
- name: Sign MCP binary (macOS)
if: runner.os == 'macOS'
uses: BloopAI/apple-code-sign-action@v1
with:
input_path: target/${{ matrix.target }}/release/mcp_task_server
output_path: vibe-kanban-mcp
p12_file: certificate.p12
p12_password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
sign: true
sign_args: "--code-signature-flags=runtime"
- name: Package MCP binary (macOS)
if: runner.os == 'macOS'
run: zip vibe-kanban-mcp.zip vibe-kanban-mcp
- name: Sign Review binary (macOS)
if: runner.os == 'macOS'
uses: BloopAI/apple-code-sign-action@v1
with:
input_path: target/${{ matrix.target }}/release/review
output_path: vibe-kanban-review
p12_file: certificate.p12
p12_password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
sign: true
sign_args: "--code-signature-flags=runtime"
- name: Package Review binary (macOS)
if: runner.os == 'macOS'
run: zip vibe-kanban-review.zip vibe-kanban-review
- name: Notarize signed binaries (macOS)
if: runner.os == 'macOS'
uses: BloopAI/apple-code-sign-action@main
continue-on-error: true
with:
input_path: |
vibe-kanban.zip
vibe-kanban-mcp.zip
vibe-kanban-review.zip
sign: false
notarize: true
app_store_connect_api_key_json_file: app_store_key.json
- name: Prepare signed binaries (macOS)
if: runner.os == 'macOS'
run: |
mkdir -p dist
cp vibe-kanban.zip dist/vibe-kanban-${{ matrix.name }}.zip
cp vibe-kanban-mcp.zip dist/vibe-kanban-mcp-${{ matrix.name }}.zip
cp vibe-kanban-review.zip dist/vibe-kanban-review-${{ matrix.name }}.zip
- name: Clean up certificates (macOS)
if: runner.os == 'macOS'
run: |
rm -f certificate.p12
rm -rf private_keys/
- name: Upload binary artifact
uses: actions/upload-artifact@v4
with:
name: backend-binary-${{ matrix.name }}
path: dist/
retention-days: 1
- name: Sweep Cargo target cache
shell: bash
run: |
cargo sweep --maxsize 10GB
cargo sweep --time 30
package-npx-cli:
needs: [build-frontend, build-backend]
runs-on: ubuntu-latest-m
strategy:
# NOTE: This matrix must be kept in sync with build-backend job above
# GitHub Actions doesn't support YAML anchors, so duplication is unavoidable
matrix:
include:
- target: x86_64-unknown-linux-musl
name: linux-x64
binary: vibe-kanban
mcp_binary: vibe-kanban-mcp
review_binary: vibe-kanban-review
- target: x86_64-pc-windows-msvc
name: windows-x64
binary: vibe-kanban.exe
mcp_binary: vibe-kanban-mcp.exe
review_binary: vibe-kanban-review.exe
- target: x86_64-apple-darwin
name: macos-x64
binary: vibe-kanban
mcp_binary: vibe-kanban-mcp
review_binary: vibe-kanban-review
- target: aarch64-apple-darwin
name: macos-arm64
binary: vibe-kanban
mcp_binary: vibe-kanban-mcp
review_binary: vibe-kanban-review
- target: aarch64-pc-windows-msvc
name: windows-arm64
binary: vibe-kanban.exe
mcp_binary: vibe-kanban-mcp.exe
review_binary: vibe-kanban-review.exe
- target: aarch64-unknown-linux-musl
name: linux-arm64
binary: vibe-kanban
mcp_binary: vibe-kanban-mcp
review_binary: vibe-kanban-review
steps:
- uses: actions/checkout@v4
- name: Download frontend artifact
uses: actions/download-artifact@v4
with:
name: frontend-dist
path: frontend/dist/
- name: Download backend binary artifact
uses: actions/download-artifact@v4
with:
name: backend-binary-${{ matrix.name }}
path: dist/
- name: List downloaded artifacts
run: |
echo "Downloaded backend binaries:"
find dist/
- name: Create platform package
if: matrix.name != 'macos-arm64' && matrix.name != 'macos-x64'
run: |
mkdir -p npx-cli/dist/${{ matrix.name }}
mkdir vibe-kanban-${{ matrix.name }}
mkdir vibe-kanban-mcp-${{ matrix.name }}
mkdir vibe-kanban-review-${{ matrix.name }}
cp dist/vibe-kanban-${{ matrix.name }}* vibe-kanban-${{ matrix.name }}/${{ matrix.binary }}
cp dist/vibe-kanban-mcp-${{ matrix.name }}* vibe-kanban-mcp-${{ matrix.name }}/${{ matrix.mcp_binary }}
cp dist/vibe-kanban-review-${{ matrix.name }}* vibe-kanban-review-${{ matrix.name }}/${{ matrix.review_binary }}
zip -j npx-cli/dist/${{ matrix.name }}/vibe-kanban.zip vibe-kanban-${{ matrix.name }}/${{ matrix.binary }}
zip -j npx-cli/dist/${{ matrix.name }}/vibe-kanban-mcp.zip vibe-kanban-mcp-${{ matrix.name }}/${{ matrix.mcp_binary }}
zip -j npx-cli/dist/${{ matrix.name }}/vibe-kanban-review.zip vibe-kanban-review-${{ matrix.name }}/${{ matrix.review_binary }}
- name: Create platform package (macOS)
if: matrix.name == 'macos-arm64' || matrix.name == 'macos-x64'
run: |
mkdir -p npx-cli/dist/${{ matrix.name }}
mkdir vibe-kanban-${{ matrix.name }}
cp dist/vibe-kanban-${{ matrix.name }}* npx-cli/dist/${{ matrix.name }}/vibe-kanban.zip
cp dist/vibe-kanban-mcp-${{ matrix.name }}* npx-cli/dist/${{ matrix.name }}/vibe-kanban-mcp.zip
cp dist/vibe-kanban-review-${{ matrix.name }}* npx-cli/dist/${{ matrix.name }}/vibe-kanban-review.zip
- name: Upload platform package artifact
uses: actions/upload-artifact@v4
with:
name: npx-platform-${{ matrix.name }}
path: npx-cli/dist/
retention-days: 1

145
.github/workflows/publish.yml vendored Normal file
View File

@@ -0,0 +1,145 @@
name: Publish to npm
on:
release:
types: [released]
workflow_dispatch:
inputs:
tag_name:
description: "Release tag (e.g., v1.2.3)"
required: true
release_id:
description: "GitHub release ID"
required: true
concurrency:
group: publish
cancel-in-progress: true
permissions:
contents: write
packages: write
env:
NODE_VERSION: 22
PNPM_VERSION: 10.13.1
jobs:
publish:
runs-on: ubuntu-latest
# Only run if this was converted from a pre-release
if: github.event.release.prerelease == false
steps:
- uses: actions/checkout@v4
with:
ref: ${{ github.event.release.tag_name || inputs.tag_name }}
- name: Setup Node
uses: ./.github/actions/setup-node
- name: Configure npm authentication
run: |
echo "//registry.npmjs.org/:_authToken=${{ secrets.NPM_TOKEN }}" > ~/.npmrc
- name: Download release assets
uses: actions/github-script@v7
env:
RELEASE_ID: ${{ inputs.release_id }}
with:
script: |
const fs = require('fs');
const path = require('path');
const releaseId = context.payload.release?.id || process.env.RELEASE_ID;
console.log("releaseId:", releaseId);
if (!releaseId) {
core.setFailed('No release ID found.');
return;
}
const release = await github.rest.repos.getRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId
});
// Find the .tgz file
const tgzAsset = release.data.assets.find(asset => asset.name.endsWith('.tgz'));
if (!tgzAsset) {
core.setFailed('No .tgz file found in release assets');
return;
}
// Download the asset
const response = await github.rest.repos.getReleaseAsset({
owner: context.repo.owner,
repo: context.repo.repo,
asset_id: tgzAsset.id,
headers: {
Accept: 'application/octet-stream'
}
});
// Save to npx-cli directory
const filePath = path.join('npx-cli', tgzAsset.name);
fs.writeFileSync(filePath, Buffer.from(response.data));
console.log(`Downloaded ${tgzAsset.name} to ${filePath}`);
// Set output for next step
core.setOutput('package-file', filePath);
core.setOutput('package-name', tgzAsset.name);
- name: Verify package integrity
id: verify
run: |
cd npx-cli
# List files to confirm download
ls -la *.tgz
# Verify the package can be read
npm pack --dry-run || echo "Note: This is expected to show differences since we're using the pre-built package"
# Extract package name from the downloaded file
PACKAGE_FILE=$(ls *.tgz | head -n1)
echo "package-file=$PACKAGE_FILE" >> $GITHUB_OUTPUT
- name: Publish to npm
run: |
cd npx-cli
# Publish the exact same package that was tested
PACKAGE_FILE="${{ steps.verify.outputs.package-file }}"
echo "Publishing $PACKAGE_FILE to npm..."
npm publish "$PACKAGE_FILE"
echo "✅ Successfully published to npm!"
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
- name: Update release description
uses: actions/github-script@v7
env:
RELEASE_ID: ${{ inputs.release_id }}
with:
script: |
const releaseId = context.payload.release?.id || process.env.RELEASE_ID;;
// Fetch the release to get the current body
const release = await github.rest.repos.getRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId
});
const currentBody = release.data.body || '';
await github.rest.repos.updateRelease({
owner: context.repo.owner,
repo: context.repo.repo,
release_id: releaseId,
body: currentBody + '\n\n✅ **Published to npm registry**'
});

30
.github/workflows/remote-deploy-dev.yml vendored Normal file
View File

@@ -0,0 +1,30 @@
name: Remote Deploy Dev
on:
push:
branches:
- electric
- main
paths:
- crates/remote/**
- remote-frontend/**
workflow_dispatch:
jobs:
run-remote-deploy:
name: Deploy Remote Dev
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Dispatch dev remote deployment workflow
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.REMOTE_DEPLOYMENT_TOKEN }}
repository: BloopAI/vibe-kanban-remote-deployment
event-type: vibe-kanban-remote-deploy-dev
client-payload: |
{
"ref": "${{ github.ref_name }}",
"sha": "${{ github.sha }}"
}

View File

@@ -0,0 +1,23 @@
name: Remote Deploy Prod
on:
workflow_dispatch:
jobs:
run-remote-deploy:
name: Deploy Remote Prod
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- name: Dispatch prod remote deployment workflow
uses: peter-evans/repository-dispatch@v3
with:
token: ${{ secrets.REMOTE_DEPLOYMENT_TOKEN }}
repository: BloopAI/vibe-kanban-remote-deployment
event-type: vibe-kanban-remote-deploy-prod
client-payload: |
{
"ref": "${{ github.ref_name }}",
"sha": "${{ github.sha }}"
}

81
.github/workflows/test.yml vendored Normal file
View File

@@ -0,0 +1,81 @@
name: Test
on:
pull_request:
branches:
- main
- louis/fe-revision
workflow_dispatch:
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: ${{ github.ref != 'refs/heads/main' }}
env:
CARGO_TERM_COLOR: always
NODE_VERSION: 22
PNPM_VERSION: 10.13.1
jobs:
test:
runs-on: buildjet-8vcpu-ubuntu-2204
steps:
- uses: actions/checkout@v4
- name: Setup Node
uses: ./.github/actions/setup-node
- name: Install dependencies
run: pnpm install
- name: Lint frontend
run: cd frontend && npm run lint
- name: Check i18n regressions
env:
GITHUB_BASE_REF: ${{ github.base_ref || 'main' }}
run: ./scripts/check-i18n.sh
- name: Format check frontend
run: cd frontend && npm run format:check
- name: Type check frontend
run: cd frontend && npx tsc --noEmit
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@stable
with:
toolchain: nightly-2025-12-04
components: rustfmt, clippy
- name: Cache Rust dependencies
uses: Swatinem/rust-cache@v2
env:
RUST_CACHE_DEBUG: true
with:
workspaces: "."
cache-provider: "buildjet"
cache-on-failure: true
shared-key: "shared"
cache-all-crates: true
- name: Install sqlx-cli
uses: taiki-e/cache-cargo-install-action@v2
with:
tool: sqlx-cli
no-default-features: true
features: sqlite,postgres
- name: Build frontend
run: cd frontend && npm run build
env:
NODE_OPTIONS: --max-old-space-size=8192
- name: Checks
run: |
cargo fmt --all -- --check
npm run generate-types:check
npm run prepare-db:check
npm run remote:prepare-db:check
cargo test --workspace
cargo clippy --all --all-targets -- -D warnings

18
.gitignore vendored
View File

@@ -1,5 +1,5 @@
# Rust
target/
/target
**/*.rs.bk
# Node.js
@@ -66,11 +66,11 @@ coverage/
.storybook-out
.env
packages/**/dist
frontend/dist
remote-frontend/dist
build-npm-package-codesign.sh
npx-cli/bin/
npx-cli/dist
npx-cli/vibe-kanban-*
vibe-kanban-*.tgz
@@ -79,19 +79,9 @@ vibe-kanban-*.tgz
.dev-ports.json
dev_assets
/packages/web/.env.sentry-build-plugin
/frontend/.env.sentry-build-plugin
.ssh
vibe-kanban-cloud/
.jj
.sisyphus/
# Vite temp files
*.timestamp-*.mjs
# Mobile testing (per-developer, do not commit)
*.ts.net.crt
*.ts.net.key
Caddyfile
Caddyfile.dev

View File

@@ -1,46 +1,33 @@
# Repository Guidelines
## Project Structure & Module Organization
- `crates/`: Rust workspace crates — `server` (API + bins), `db` (SQLx models/migrations), `executors`, `services`, `utils`, `git` (Git operations), `api-types` (shared API types for local + remote), `review` (PR review tool), `deployment`, `local-deployment`, `remote`.
- `packages/local-web/`: Local React + TypeScript app entrypoint (Vite, Tailwind). Shell source in `packages/local-web/src`.
- `packages/remote-web/`: Remote deployment frontend entrypoint.
- `packages/web-core/`: Shared React + TypeScript frontend library used by local + remote web (`packages/web-core/src`).
- `shared/`: Generated TypeScript types (`shared/types.ts`, `shared/remote-types.ts`) and agent tool schemas (`shared/schemas/`). Do not edit generated files directly.
- `crates/`: Rust workspace crates — `server` (API + bins), `db` (SQLx models/migrations), `executors`, `services`, `utils`, `deployment`, `local-deployment`, `remote`.
- `frontend/`: React + TypeScript app (Vite, Tailwind). Source in `frontend/src`.
- `frontend/src/components/dialogs`: Dialog components for the frontend.
- `remote-frontend/`: Remote deployment frontend.
- `shared/`: Generated TypeScript types (`shared/types.ts`). Do not edit directly.
- `assets/`, `dev_assets_seed/`, `dev_assets/`: Packaged and local dev assets.
- `npx-cli/`: Files published to the npm CLI package.
- `scripts/`: Dev helpers (ports, DB preparation).
- `docs/`: Documentation files.
### Crate-specific guides
- [`crates/remote/AGENTS.md`](crates/remote/AGENTS.md) — Remote server architecture, ElectricSQL integration, mutation patterns, environment variables.
- [`docs/AGENTS.md`](docs/AGENTS.md) — Mintlify documentation writing guidelines and component reference.
- [`packages/local-web/AGENTS.md`](packages/local-web/AGENTS.md) — Web app design system styling guidelines.
## Managing Shared Types Between Rust and TypeScript
ts-rs allows you to derive TypeScript types from Rust structs/enums. By annotating your Rust types with #[derive(TS)] and related macros, ts-rs will generate .ts declaration files for those types.
When making changes to the types, you can regenerate them using `pnpm run generate-types`
Do not manually edit shared/types.ts, instead edit crates/server/src/bin/generate_types.rs
For remote/cloud types, regenerate using `pnpm run remote:generate-types`
Do not manually edit shared/remote-types.ts, instead edit crates/remote/src/bin/remote-generate-types.rs (see crates/remote/AGENTS.md for details).
## Build, Test, and Development Commands
- Install: `pnpm i`
- Run dev (web app + backend with ports auto-assigned): `pnpm run dev`
- Run dev (frontend + backend with ports auto-assigned): `pnpm run dev`
- Backend (watch): `pnpm run backend:dev:watch`
- Web app (dev): `pnpm run local-web:dev`
- Type checks: `pnpm run check` (frontend + all backend Rust workspaces) and `pnpm run backend:check` (all backend Rust workspaces, including `crates/remote`)
- Frontend (dev): `pnpm run frontend:dev`
- Type checks: `pnpm run check` (frontend) and `pnpm run backend:check` (Rust cargo check)
- Rust tests: `cargo test --workspace`
- Generate TS types from Rust: `pnpm run generate-types` (or `generate-types:check` in CI)
- Prepare SQLx (offline): `pnpm run prepare-db`
- Prepare SQLx (remote package, postgres): `pnpm run remote:prepare-db`
- Local NPX build: `pnpm run build:npx` then `pnpm pack` in `npx-cli/`
- Format code: `pnpm run format` (runs `cargo fmt` for all backend Rust workspaces + web-core/web Prettier)
- Lint: `pnpm run lint` (runs web/ui ESLint + `cargo clippy` for all backend Rust workspaces)
## Before Completing a Task
- Run `pnpm run format` to format all Rust workspaces and web code.
## Coding Style & Naming Conventions
- Rust: `rustfmt` enforced (`rustfmt.toml`); group imports by crate; snake_case modules, PascalCase types.
@@ -49,10 +36,8 @@ Do not manually edit shared/remote-types.ts, instead edit crates/remote/src/bin/
## Testing Guidelines
- Rust: prefer unit tests alongside code (`#[cfg(test)]`), run `cargo test --workspace`. Add tests for new logic and edge cases.
- Web app: ensure `pnpm run check` and `pnpm run lint` pass. If adding runtime logic, include lightweight tests (e.g., Vitest) in the same directory.
- Frontend: ensure `pnpm run check` and `pnpm run lint` pass. If adding runtime logic, include lightweight tests (e.g., Vitest) in the same directory.
## Security & Config Tips
- Use `.env` for local overrides; never commit secrets. Key envs: `FRONTEND_PORT`, `BACKEND_PORT`, `HOST`
- Dev ports and assets are managed by `scripts/setup-dev-environment.js`.

View File

@@ -1,61 +0,0 @@
# Contributing & Change Control
## Change Control Policy
All changes to production code are governed by formal change control procedures. These procedures ensure that modifications are reviewed, approved, and deployed in a controlled manner.
## Code Review Requirements
A maintainer must review pull requests before they are merged into any production branch. No code changes shall be merged without explicit approval from a qualified reviewer.
## Pull Request Process
1. Create a feature or fix branch from the base branch.
2. Make changes and open a pull request.
3. Obtain the required review and approval from a maintainer.
4. All required CI checks must pass before merging.
5. Merge only after approval has been granted and CI checks have passed.
## Separation of Duties
Development, testing, and deployment of changes shall not be performed by a single individual without approval and oversight. All significant changes require independent review to ensure correctness, security, and alignment with project standards.
## Coding Practices
Contributors are expected to follow the project's coding standards throughout the development cycle. These standards cover code quality, style consistency, and security.
### Style & Formatting
- **Rust**: Code must be formatted with `rustfmt` (config in `rustfmt.toml`). Use `snake_case` for modules and functions, `PascalCase` for types. Group imports by crate.
- **TypeScript/React**: Code must pass ESLint and Prettier (2 spaces, single quotes, 80-column width). Use `PascalCase` for components, `camelCase` for variables and functions, and `kebab-case` for file names.
- Run `pnpm run format` before submitting a pull request.
- Run `pnpm run lint` to verify there are no linting errors.
### Code Quality
- Keep functions small and focused on a single responsibility.
- Write clear, self-documenting code. Add comments only where the logic is not self-evident.
- Do not introduce unnecessary abstractions or over-engineer solutions.
- Do not manually edit generated files (e.g., `shared/types.ts`). Modify the source and regenerate.
### Testing
- **Rust**: Add unit tests alongside code using `#[cfg(test)]`. Run `cargo test --workspace` to verify.
- **TypeScript**: Ensure `pnpm run check` and `pnpm run lint` pass. Include lightweight tests (e.g., Vitest) for new runtime logic.
- All CI checks must pass before a pull request can be merged.
### Security
- Never commit secrets, credentials, or API keys. Use `.env` for local configuration.
- Be mindful of common vulnerabilities (injection, XSS, insecure deserialization) when writing code that handles user input or external data.
- Report security issues privately to the maintainers rather than opening a public issue.
### Commit Messages
- Use clear, descriptive commit messages that explain the _why_ behind a change.
- Prefix with a conventional type where appropriate (e.g., `feat:`, `fix:`, `chore:`, `docs:`, `refactor:`, `test:`).
- Keep the subject line under 72 characters. Use the body for additional context if needed.
## Scope
These procedures apply to all production branches in this repository.

View File

@@ -1,15 +0,0 @@
localhost:3001, relay.localhost:3001, *.relay.localhost:3001 {
tls internal
@relay host relay.localhost *.relay.localhost
handle @relay {
reverse_proxy 127.0.0.1:8082
}
@app expression `{http.request.host} == "localhost:3001" || {http.request.host} == "localhost"`
handle @app {
reverse_proxy 127.0.0.1:3000
}
respond "not found" 404
}

8633
Cargo.lock generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,58 +1,33 @@
[workspace]
resolver = "3"
resolver = "2"
members = [
"crates/api-types",
"crates/relay-client",
"crates/server",
"crates/relay-types",
"crates/trusted-key-auth",
"crates/mcp",
"crates/db",
"crates/executors",
"crates/services",
"crates/worktree-manager",
"crates/workspace-manager",
"crates/relay-control",
"crates/relay-protocol",
"crates/relay-ws",
"crates/ws-bridge",
"crates/relay-hosts",
"crates/client-info",
"crates/remote-info",
"crates/utils",
"crates/git",
"crates/git-host",
"crates/local-deployment",
"crates/deployment",
"crates/review",
"crates/embedded-ssh",
"crates/desktop-bridge",
"crates/tauri-app",
"crates/preview-proxy",
"crates/relay-tunnel-core",
"crates/relay-webrtc",
"crates/remote",
"crates/review"
]
exclude = ["crates/remote", "crates/relay-tunnel"]
[workspace.dependencies]
tokio = { version = "1.0", features = ["full"] }
axum = { version = "0.8.4", features = ["macros", "multipart", "ws"] }
tower-http = { version = "0.5", features = ["cors", "request-id", "trace", "fs", "validate-request", "compression-gzip", "compression-br"] }
tower-http = { version = "0.5", features = ["cors", "request-id", "trace", "fs"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = { version = "1.0", features = ["preserve_order"] }
anyhow = "1.0"
git2 = { version = "0.20.3", default-features = false }
reqwest = { version = "0.13", default-features = false, features = ["json", "query", "stream", "rustls"] }
reqwest = { version = "0.12", default-features = false, features = ["json", "rustls-tls-webpki-roots-no-provider"] }
rustls = { version = "0.23", default-features = false, features = ["aws_lc_rs", "std", "tls12"] }
thiserror = "2.0.12"
tracing = "0.1.43"
tracing-subscriber = { version = "0.3", features = ["env-filter", "fmt", "json"] }
ts-rs = { git = "https://github.com/xazukx/ts-rs.git", branch = "use-ts-enum", features = ["uuid-impl", "chrono-impl", "no-serde-warnings", "serde-json-impl"] }
schemars = { version = "1.0.4", features = ["derive", "chrono04", "uuid1", "preserve_order"] }
serde_with = "3"
async-trait = "0.1"
aws-lc-sys = "=0.37.0"
aws-lc-rs = "=1.16.0"
[profile.release]
debug = 1

View File

@@ -1,152 +1,82 @@
# syntax=docker/dockerfile:1.6
# Build stage
FROM node:24-alpine AS builder
FROM node:24-alpine AS fe-builder
# Install build dependencies
RUN apk add --no-cache \
curl \
build-base \
perl \
llvm-dev \
clang-dev
ARG POSTHOG_API_KEY=""
ARG POSTHOG_API_ENDPOINT=""
# Allow linking libclang on musl
ENV RUSTFLAGS="-C target-feature=-crt-static"
# Install Rust
RUN curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
ENV PATH="/root/.cargo/bin:${PATH}"
ARG POSTHOG_API_KEY
ARG POSTHOG_API_ENDPOINT
ENV VITE_PUBLIC_POSTHOG_KEY=$POSTHOG_API_KEY
ENV VITE_PUBLIC_POSTHOG_HOST=$POSTHOG_API_ENDPOINT
# Set working directory
WORKDIR /app
ENV PNPM_HOME=/pnpm
ENV PATH=${PNPM_HOME}:${PATH}
ENV VITE_PUBLIC_POSTHOG_KEY=${POSTHOG_API_KEY}
ENV VITE_PUBLIC_POSTHOG_HOST=${POSTHOG_API_ENDPOINT}
ENV NODE_OPTIONS=--max-old-space-size=4096
# Copy package files for dependency caching
COPY package*.json pnpm-lock.yaml pnpm-workspace.yaml ./
COPY frontend/package*.json ./frontend/
COPY npx-cli/package*.json ./npx-cli/
RUN corepack enable
RUN pnpm config set store-dir /pnpm/store
# Install pnpm and dependencies
RUN npm install -g pnpm && pnpm install
COPY pnpm-lock.yaml pnpm-workspace.yaml package.json ./
COPY packages/local-web/package.json packages/local-web/package.json
COPY packages/ui/package.json packages/ui/package.json
COPY packages/web-core/package.json packages/web-core/package.json
# Copy source code
COPY . .
RUN --mount=type=cache,id=pnpm,target=/pnpm/store \
pnpm install --frozen-lockfile
# Build application
RUN npm run generate-types
RUN cd frontend && pnpm run build
RUN cargo build --release --bin server
COPY packages/local-web/ packages/local-web/
COPY packages/public/ packages/public/
COPY packages/ui/ packages/ui/
COPY packages/web-core/ packages/web-core/
COPY shared/ shared/
# Runtime stage
FROM alpine:latest AS runtime
RUN pnpm -C packages/local-web build
FROM rust:1.93-slim-bookworm AS builder
ARG POSTHOG_API_KEY=""
ARG POSTHOG_API_ENDPOINT=""
ARG SENTRY_DSN=""
ARG VK_SHARED_API_BASE=""
ENV CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
ENV CARGO_NET_GIT_FETCH_WITH_CLI=true
ENV CARGO_TARGET_DIR=/app/target
ENV POSTHOG_API_KEY=${POSTHOG_API_KEY}
ENV POSTHOG_API_ENDPOINT=${POSTHOG_API_ENDPOINT}
ENV SENTRY_DSN=${SENTRY_DSN}
ENV VK_SHARED_API_BASE=${VK_SHARED_API_BASE}
WORKDIR /app
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential \
# Install runtime dependencies
RUN apk add --no-cache \
ca-certificates \
git \
libclang-dev \
libssl-dev \
pkg-config \
&& rm -rf /var/lib/apt/lists/*
COPY rust-toolchain.toml ./
RUN cargo --version >/dev/null
COPY Cargo.toml Cargo.lock ./
COPY crates/api-types/Cargo.toml crates/api-types/Cargo.toml
COPY crates/db/Cargo.toml crates/db/Cargo.toml
COPY crates/deployment/Cargo.toml crates/deployment/Cargo.toml
COPY crates/executors/Cargo.toml crates/executors/Cargo.toml
COPY crates/git/Cargo.toml crates/git/Cargo.toml
COPY crates/git-host/Cargo.toml crates/git-host/Cargo.toml
COPY crates/local-deployment/Cargo.toml crates/local-deployment/Cargo.toml
COPY crates/mcp/Cargo.toml crates/mcp/Cargo.toml
COPY crates/relay-control/Cargo.toml crates/relay-control/Cargo.toml
COPY crates/relay-hosts/Cargo.toml crates/relay-hosts/Cargo.toml
COPY crates/relay-protocol/Cargo.toml crates/relay-protocol/Cargo.toml
COPY crates/relay-tunnel-core/Cargo.toml crates/relay-tunnel-core/Cargo.toml
COPY crates/relay-webrtc/Cargo.toml crates/relay-webrtc/Cargo.toml
COPY crates/relay-ws/Cargo.toml crates/relay-ws/Cargo.toml
COPY crates/review/Cargo.toml crates/review/Cargo.toml
COPY crates/server/Cargo.toml crates/server/Cargo.toml
COPY crates/server-info/Cargo.toml crates/server-info/Cargo.toml
COPY crates/services/Cargo.toml crates/services/Cargo.toml
COPY crates/tauri-app/Cargo.toml crates/tauri-app/Cargo.toml
COPY crates/trusted-key-auth/Cargo.toml crates/trusted-key-auth/Cargo.toml
COPY crates/utils/Cargo.toml crates/utils/Cargo.toml
COPY crates/workspace-manager/Cargo.toml crates/workspace-manager/Cargo.toml
COPY crates/worktree-manager/Cargo.toml crates/worktree-manager/Cargo.toml
COPY crates/ws-bridge/Cargo.toml crates/ws-bridge/Cargo.toml
COPY crates/api-types/ crates/api-types/
COPY crates/db/ crates/db/
COPY crates/deployment/ crates/deployment/
COPY crates/executors/ crates/executors/
COPY crates/git/ crates/git/
COPY crates/git-host/ crates/git-host/
COPY crates/local-deployment/ crates/local-deployment/
COPY crates/mcp/ crates/mcp/
COPY crates/relay-control/ crates/relay-control/
COPY crates/relay-hosts/ crates/relay-hosts/
COPY crates/relay-protocol/ crates/relay-protocol/
COPY crates/relay-tunnel-core/ crates/relay-tunnel-core/
COPY crates/relay-webrtc/ crates/relay-webrtc/
COPY crates/relay-ws/ crates/relay-ws/
COPY crates/review/ crates/review/
COPY crates/server/ crates/server/
COPY crates/server-info/ crates/server-info/
COPY crates/services/ crates/services/
COPY crates/trusted-key-auth/ crates/trusted-key-auth/
COPY crates/utils/ crates/utils/
COPY crates/workspace-manager/ crates/workspace-manager/
COPY crates/worktree-manager/ crates/worktree-manager/
COPY crates/ws-bridge/ crates/ws-bridge/
COPY assets/ assets/
COPY --from=fe-builder /app/packages/local-web/dist packages/local-web/dist
RUN --mount=type=cache,id=cargo-registry,target=/usr/local/cargo/registry \
--mount=type=cache,id=cargo-git,target=/usr/local/cargo/git \
--mount=type=cache,id=workspace-target,target=/app/target \
cargo build --locked --release --bin server \
&& cp /app/target/release/server /usr/local/bin/server
FROM debian:bookworm-slim AS runtime
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ca-certificates \
git \
openssh-client \
tini \
wget \
&& rm -rf /var/lib/apt/lists/* \
&& useradd --system --create-home --uid 10001 appuser
libgcc \
wget
WORKDIR /repos
# Create app user for security
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
COPY --from=builder /usr/local/bin/server /usr/local/bin/server
# Copy binary from builder
COPY --from=builder /app/target/release/server /usr/local/bin/server
RUN mkdir -p /repos \
&& chown -R appuser:appuser /repos
# Create repos directory and set permissions
RUN mkdir -p /repos && \
chown -R appuser:appgroup /repos
# Switch to non-root user
USER appuser
# Set runtime environment
ENV HOST=0.0.0.0
ENV PORT=3000
EXPOSE 3000
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD ["/bin/sh", "-c", "wget --spider -q http://127.0.0.1:${PORT:-3000}/health"]
# Set working directory
WORKDIR /repos
ENTRYPOINT ["/usr/bin/tini", "--", "/usr/local/bin/server"]
# Health check
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
CMD wget --quiet --tries=1 --spider "http://${HOST:-localhost}:${PORT:-3000}" || exit 1
# Run the application
ENTRYPOINT ["/sbin/tini", "--"]
CMD ["server"]

View File

@@ -1,9 +1,9 @@
<p align="center">
<a href="https://vibekanban.com">
<picture>
<source srcset="packages/public/vibe-kanban-logo-dark.svg" media="(prefers-color-scheme: dark)">
<source srcset="packages/public/vibe-kanban-logo.svg" media="(prefers-color-scheme: light)">
<img src="packages/public/vibe-kanban-logo.svg" alt="Vibe Kanban Logo">
<source srcset="frontend/public/vibe-kanban-logo-dark.svg" media="(prefers-color-scheme: dark)">
<source srcset="frontend/public/vibe-kanban-logo.svg" media="(prefers-color-scheme: light)">
<img src="frontend/public/vibe-kanban-logo.svg" alt="Vibe Kanban Logo">
</picture>
</a>
</p>
@@ -19,33 +19,24 @@
<a href="https://jobs.polymer.co/vibe-kanban?source=github"><strong>We're hiring!</strong></a>
</h1>
![](packages/public/vibe-kanban-screenshot-overview.png)
![](frontend/public/vibe-kanban-screenshot-overview.png)
## Overview
In a world where software engineers spend most of their time planning and reviewing coding agents, the most impactful way to ship more is to get faster at planning and review.
AI coding agents are increasingly writing the world's code and human engineers now spend the majority of their time planning, reviewing, and orchestrating tasks. Vibe Kanban streamlines this process, enabling you to:
Vibe Kanban is built for this. Use kanban issues to plan work, either privately or with your team. When you're ready to begin, create workspaces where coding agents can execute.
- **Plan with kanban issues** — create, prioritise, and assign issues on a kanban board
- **Run coding agents in workspaces** — each workspace gives an agent a branch, a terminal, and a dev server
- **Review diffs and leave inline comments** — send feedback directly to the agent without leaving the UI
- **Preview your app** — built-in browser with devtools, inspect mode, and device emulation
- **Switch between 10+ coding agents** — Claude Code, Codex, Gemini CLI, GitHub Copilot, Amp, Cursor, OpenCode, Droid, CCR, and Qwen Code
- **Create pull requests and merge** — open PRs with AI-generated descriptions, review on GitHub, and merge
![](packages/public/vibe-kanban-screenshot-workspace.png)
One command. Describe the work, review the diff, ship it.
```bash
npx vibe-kanban
```
- Easily switch between different coding agents
- Orchestrate the execution of multiple coding agents in parallel or in sequence
- Quickly review work and start dev servers
- Track the status of tasks that your coding agents are working on
- Centralise configuration of coding agent MCP configs
- Open projects remotely via SSH when running Vibe Kanban on a remote server
You can watch a video overview [here](https://youtu.be/TFT3KnZOOAk).
## Installation
Make sure you have authenticated with your favourite coding agent. A full list of supported coding agents can be found in the [docs](https://vibekanban.com/docs/supported-coding-agents). Then in your terminal run:
Make sure you have authenticated with your favourite coding agent. A full list of supported coding agents can be found in the [docs](https://vibekanban.com/docs). Then in your terminal run:
```bash
npx vibe-kanban
@@ -53,11 +44,7 @@ npx vibe-kanban
## Documentation
Head to the [website](https://vibekanban.com/docs) for the latest documentation and user guides.
## Self-Hosting
Want to host your own Vibe Kanban Cloud instance? See our [self-hosting guide](https://vibekanban.com/docs/self-hosting/deploy-docker).
Please head to the [website](https://vibekanban.com/docs) for the latest documentation and user guides.
## Support
@@ -72,7 +59,7 @@ We would prefer that ideas and changes are first raised with the core team via [
### Prerequisites
- [Rust](https://rustup.rs/) (latest stable)
- [Node.js](https://nodejs.org/) (>=20)
- [Node.js](https://nodejs.org/) (>=18)
- [pnpm](https://pnpm.io/) (>=8)
Additional development tools:
@@ -92,15 +79,15 @@ pnpm i
pnpm run dev
```
This will start the backend and web app. A blank DB will be copied from the `dev_assets_seed` folder.
This will start the backend. A blank DB will be copied from the `dev_assets_seed` folder.
### Building the web app
### Building the frontend
To build just the web app:
To build just the frontend:
```bash
cd packages/local-web
pnpm run build
cd frontend
pnpm build
```
### Build from source (macOS)
@@ -108,6 +95,7 @@ pnpm run build
1. Run `./local-build.sh`
2. Test with `cd npx-cli && node bin/cli.js`
### Environment Variables
The following environment variables can be configured at build time or runtime:
@@ -120,30 +108,10 @@ The following environment variables can be configured at build time or runtime:
| `BACKEND_PORT` | Runtime | `0` (auto-assign) | Backend server port (dev mode only, overrides PORT+1) |
| `FRONTEND_PORT` | Runtime | `3000` | Frontend dev server port (dev mode only, overrides PORT) |
| `HOST` | Runtime | `127.0.0.1` | Backend server host |
| `MCP_HOST` | Runtime | Value of `HOST` | MCP server connection host (use `127.0.0.1` when `HOST=0.0.0.0` on Windows) |
| `MCP_PORT` | Runtime | Value of `BACKEND_PORT` | MCP server connection port |
| `DISABLE_WORKTREE_CLEANUP` | Runtime | Not set | Disable all git worktree cleanup including orphan and expired workspace cleanup (for debugging) |
| `VK_ALLOWED_ORIGINS` | Runtime | Not set | Comma-separated list of origins that are allowed to make backend API requests (e.g., `https://my-vibekanban-frontend.com`) |
| `VK_SHARED_API_BASE` | Runtime | Not set | Base URL for the remote/cloud API used by the local desktop app |
| `VK_SHARED_RELAY_API_BASE` | Runtime | Not set | Base URL for the relay API used by tunnel-mode connections |
| `VK_TUNNEL` | Runtime | Not set | Enable relay tunnel mode when set (requires relay API base URL) |
| `DISABLE_WORKTREE_ORPHAN_CLEANUP` | Runtime | Not set | Disable git worktree cleanup (for debugging) |
**Build-time variables** must be set when running `pnpm run build`. **Runtime variables** are read when the application starts.
#### Self-Hosting with a Reverse Proxy or Custom Domain
When running Vibe Kanban behind a reverse proxy (e.g., nginx, Caddy, Traefik) or on a custom domain, you must set the `VK_ALLOWED_ORIGINS` environment variable. Without this, the browser's Origin header won't match the backend's expected host, and API requests will be rejected with a 403 Forbidden error.
Set it to the full origin URL(s) where your frontend is accessible:
```bash
# Single origin
VK_ALLOWED_ORIGINS=https://vk.example.com
# Multiple origins (comma-separated)
VK_ALLOWED_ORIGINS=https://vk.example.com,https://vk-staging.example.com
```
### Remote Deployment
When running Vibe Kanban on a remote server (e.g., via systemctl, Docker, or cloud hosting), you can configure your editor to open projects via SSH:
@@ -159,4 +127,4 @@ When running Vibe Kanban on a remote server (e.g., via systemctl, Docker, or clo
When configured, the "Open in VSCode" buttons will generate URLs like `vscode://vscode-remote/ssh-remote+user@host/path` that open your local editor and connect to the remote server.
See the [documentation](https://vibekanban.com/docs/settings/general) for detailed setup instructions.
See the [documentation](https://vibekanban.com/docs/configuration-customisation/global-settings#remote-ssh-configuration) for detailed setup instructions.

Binary file not shown.

View File

@@ -1,13 +0,0 @@
[package]
name = "api-types"
version = "0.1.44"
edition = "2024"
[dependencies]
chrono = { version = "0.4", features = ["serde"] }
schemars = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
sqlx = { version = "0.8.6", default-features = false, features = ["postgres", "uuid", "chrono", "derive"] }
ts-rs = { workspace = true }
uuid = { version = "1.0", features = ["v4", "serde"] }

View File

@@ -1,55 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
/// An attachment links a blob to an issue or comment.
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct Attachment {
pub id: Uuid,
pub blob_id: Uuid,
pub issue_id: Option<Uuid>,
pub comment_id: Option<Uuid>,
pub created_at: DateTime<Utc>,
pub expires_at: Option<DateTime<Utc>>,
}
/// An attachment with its associated blob data (for API responses).
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct AttachmentWithBlob {
pub id: Uuid,
pub blob_id: Uuid,
pub issue_id: Option<Uuid>,
pub comment_id: Option<Uuid>,
pub created_at: DateTime<Utc>,
pub expires_at: Option<DateTime<Utc>>,
// Blob fields
pub blob_path: String,
pub thumbnail_blob_path: Option<String>,
pub original_name: String,
pub mime_type: Option<String>,
pub size_bytes: i64,
pub hash: String,
pub width: Option<i32>,
pub height: Option<i32>,
}
/// An attachment with blob data and a presigned file URL.
#[derive(Debug, Serialize, Deserialize)]
pub struct AttachmentWithUrl {
#[serde(flatten)]
pub attachment: AttachmentWithBlob,
pub file_url: Option<String>,
}
/// Response from listing attachments.
#[derive(Debug, Serialize, Deserialize)]
pub struct ListAttachmentsResponse {
pub attachments: Vec<AttachmentWithUrl>,
}
/// Response containing a presigned URL for an attachment file or thumbnail.
#[derive(Debug, Serialize, Deserialize, TS)]
pub struct AttachmentUrlResponse {
pub url: String,
}

View File

@@ -1,26 +0,0 @@
use chrono::{DateTime, Duration, Utc};
use serde::Serialize;
use uuid::Uuid;
#[derive(Debug, Clone, sqlx::FromRow, Serialize)]
pub struct AuthSession {
pub id: Uuid,
pub user_id: Uuid,
pub created_at: DateTime<Utc>,
pub last_used_at: Option<DateTime<Utc>>,
pub revoked_at: Option<DateTime<Utc>>,
pub refresh_token_id: Option<Uuid>,
pub refresh_token_issued_at: Option<DateTime<Utc>>,
pub previous_refresh_token_id: Option<Uuid>,
pub previous_refresh_token_grace_expires_at: Option<DateTime<Utc>>,
}
impl AuthSession {
pub fn last_activity_at(&self) -> DateTime<Utc> {
self.last_used_at.unwrap_or(self.created_at)
}
pub fn inactivity_duration(&self, now: DateTime<Utc>) -> Duration {
now.signed_duration_since(self.last_activity_at())
}
}

View File

@@ -1,20 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct Blob {
pub id: Uuid,
pub project_id: Uuid,
pub blob_path: String,
pub thumbnail_blob_path: Option<String>,
pub original_name: String,
pub mime_type: Option<String>,
pub size_bytes: i64,
pub hash: String,
pub width: Option<i32>,
pub height: Option<i32>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

View File

@@ -1,11 +0,0 @@
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ExportRequest {
pub organization_id: Uuid,
/// If empty, exports all projects in the organization.
pub project_ids: Vec<Uuid>,
pub include_attachments: bool,
}

View File

@@ -1,204 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use serde_json::Value;
use sqlx::Type;
use ts_rs::TS;
use uuid::Uuid;
use crate::some_if_present;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type, TS)]
#[sqlx(type_name = "issue_priority", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum IssuePriority {
Urgent,
High,
Medium,
Low,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS, sqlx::FromRow)]
pub struct Issue {
pub id: Uuid,
pub project_id: Uuid,
pub issue_number: i32,
pub simple_id: String,
pub status_id: Uuid,
pub title: String,
pub description: Option<String>,
pub priority: Option<IssuePriority>,
pub start_date: Option<DateTime<Utc>>,
pub target_date: Option<DateTime<Utc>>,
pub completed_at: Option<DateTime<Utc>>,
pub sort_order: f64,
pub parent_issue_id: Option<Uuid>,
pub parent_issue_sort_order: Option<f64>,
pub extension_metadata: Value,
pub creator_user_id: Option<Uuid>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
#[serde(rename_all = "snake_case")]
pub enum IssueSortField {
SortOrder,
Priority,
CreatedAt,
UpdatedAt,
Title,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
#[serde(rename_all = "snake_case")]
pub enum SortDirection {
Asc,
Desc,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct CreateIssueRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub project_id: Uuid,
pub status_id: Uuid,
pub title: String,
pub description: Option<String>,
pub priority: Option<IssuePriority>,
pub start_date: Option<DateTime<Utc>>,
pub target_date: Option<DateTime<Utc>>,
pub completed_at: Option<DateTime<Utc>>,
pub sort_order: f64,
pub parent_issue_id: Option<Uuid>,
pub parent_issue_sort_order: Option<f64>,
pub extension_metadata: Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct UpdateIssueRequest {
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub status_id: Option<Uuid>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub title: Option<String>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub description: Option<Option<String>>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub priority: Option<Option<IssuePriority>>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub start_date: Option<Option<DateTime<Utc>>>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub target_date: Option<Option<DateTime<Utc>>>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub completed_at: Option<Option<DateTime<Utc>>>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub sort_order: Option<f64>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub parent_issue_id: Option<Option<Uuid>>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub parent_issue_sort_order: Option<Option<f64>>,
#[serde(
default,
deserialize_with = "some_if_present",
skip_serializing_if = "Option::is_none"
)]
pub extension_metadata: Option<Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListIssuesQuery {
pub project_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct SearchIssuesRequest {
pub project_id: Uuid,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub status_id: Option<Uuid>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub status_ids: Option<Vec<Uuid>>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub priority: Option<IssuePriority>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_issue_id: Option<Uuid>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub search: Option<String>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub simple_id: Option<String>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub assignee_user_id: Option<Uuid>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub tag_id: Option<Uuid>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub tag_ids: Option<Vec<Uuid>>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub sort_field: Option<IssueSortField>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub sort_direction: Option<SortDirection>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub limit: Option<i32>,
#[ts(optional)]
#[serde(skip_serializing_if = "Option::is_none")]
pub offset: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListIssuesResponse {
pub issues: Vec<Issue>,
pub total_count: usize,
pub limit: usize,
pub offset: usize,
}

View File

@@ -1,32 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct IssueAssignee {
pub id: Uuid,
pub issue_id: Uuid,
pub user_id: Uuid,
pub assigned_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct CreateIssueAssigneeRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub issue_id: Uuid,
pub user_id: Uuid,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListIssueAssigneesQuery {
pub issue_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListIssueAssigneesResponse {
pub issue_assignees: Vec<IssueAssignee>,
}

View File

@@ -1,46 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
use crate::some_if_present;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct IssueComment {
pub id: Uuid,
pub issue_id: Uuid,
pub author_id: Option<Uuid>,
pub parent_id: Option<Uuid>,
pub message: String,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct CreateIssueCommentRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub issue_id: Uuid,
pub message: String,
pub parent_id: Option<Uuid>,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct UpdateIssueCommentRequest {
#[serde(default, deserialize_with = "some_if_present")]
pub message: Option<String>,
#[serde(default, deserialize_with = "some_if_present")]
pub parent_id: Option<Option<Uuid>>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListIssueCommentsQuery {
pub issue_id: Uuid,
}
#[derive(Debug, Clone, Serialize, TS)]
pub struct ListIssueCommentsResponse {
pub issue_comments: Vec<IssueComment>,
}

View File

@@ -1,41 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
use crate::some_if_present;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct IssueCommentReaction {
pub id: Uuid,
pub comment_id: Uuid,
pub user_id: Uuid,
pub emoji: String,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct CreateIssueCommentReactionRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub comment_id: Uuid,
pub emoji: String,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct UpdateIssueCommentReactionRequest {
#[serde(default, deserialize_with = "some_if_present")]
pub emoji: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListIssueCommentReactionsQuery {
pub comment_id: Uuid,
}
#[derive(Debug, Clone, Serialize, TS)]
pub struct ListIssueCommentReactionsResponse {
pub issue_comment_reactions: Vec<IssueCommentReaction>,
}

View File

@@ -1,30 +0,0 @@
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct IssueFollower {
pub id: Uuid,
pub issue_id: Uuid,
pub user_id: Uuid,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct CreateIssueFollowerRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub issue_id: Uuid,
pub user_id: Uuid,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListIssueFollowersQuery {
pub issue_id: Uuid,
}
#[derive(Debug, Clone, Serialize, TS)]
pub struct ListIssueFollowersResponse {
pub issue_followers: Vec<IssueFollower>,
}

View File

@@ -1,45 +0,0 @@
use chrono::{DateTime, Utc};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use sqlx::Type;
use ts_rs::TS;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type, TS, JsonSchema)]
#[sqlx(type_name = "issue_relationship_type", rename_all = "snake_case")]
#[serde(rename_all = "snake_case")]
pub enum IssueRelationshipType {
Blocking,
Related,
HasDuplicate,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct IssueRelationship {
pub id: Uuid,
pub issue_id: Uuid,
pub related_issue_id: Uuid,
pub relationship_type: IssueRelationshipType,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct CreateIssueRelationshipRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub issue_id: Uuid,
pub related_issue_id: Uuid,
pub relationship_type: IssueRelationshipType,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListIssueRelationshipsQuery {
pub issue_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListIssueRelationshipsResponse {
pub issue_relationships: Vec<IssueRelationship>,
}

View File

@@ -1,30 +0,0 @@
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct IssueTag {
pub id: Uuid,
pub issue_id: Uuid,
pub tag_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct CreateIssueTagRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub issue_id: Uuid,
pub tag_id: Uuid,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListIssueTagsQuery {
pub issue_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListIssueTagsResponse {
pub issue_tags: Vec<IssueTag>,
}

View File

@@ -1,66 +0,0 @@
//! API types shared between local and remote backends.
//!
//! This crate contains:
//! - Row types (e.g., `Issue`, `Project`) - the API representation of database entities
//! - Request types (e.g., `CreateIssueRequest`, `UpdateIssueRequest`) - API input types
//! - Shared enums (e.g., `IssuePriority`, `PullRequestStatus`)
use serde::{Deserialize, Deserializer};
pub mod attachment;
pub mod auth;
pub mod blob;
pub mod export;
pub mod issue;
pub mod issue_assignee;
pub mod issue_comment;
pub mod issue_comment_reaction;
pub mod issue_follower;
pub mod issue_relationship;
pub mod issue_tag;
pub mod notification;
pub mod oauth;
pub mod organization_member;
pub mod organizations;
pub mod project;
pub mod project_status;
pub mod pull_request;
pub mod pull_requests_local;
pub mod response;
pub mod tag;
pub mod user;
pub mod workspace;
pub mod workspaces;
pub use attachment::*;
pub use auth::*;
pub use blob::*;
pub use export::*;
pub use issue::*;
pub use issue_assignee::*;
pub use issue_comment::*;
pub use issue_comment_reaction::*;
pub use issue_follower::*;
pub use issue_relationship::*;
pub use issue_tag::*;
pub use notification::*;
pub use oauth::*;
pub use organization_member::*;
pub use organizations::*;
pub use project::*;
pub use project_status::*;
pub use pull_request::*;
pub use pull_requests_local::*;
pub use response::*;
pub use tag::*;
pub use user::*;
pub use workspace::*;
pub use workspaces::*;
pub fn some_if_present<'de, D, T>(deserializer: D) -> Result<Option<T>, D::Error>
where
D: Deserializer<'de>,
T: Deserialize<'de>,
{
T::deserialize(deserializer).map(Some)
}

View File

@@ -1,87 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::Type;
use ts_rs::TS;
use uuid::Uuid;
use crate::{IssuePriority, some_if_present};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type, TS)]
#[serde(rename_all = "snake_case")]
#[sqlx(type_name = "notification_type", rename_all = "snake_case")]
pub enum NotificationType {
IssueCommentAdded,
IssueStatusChanged,
IssueAssigneeChanged,
IssuePriorityChanged,
IssueUnassigned,
IssueCommentReaction,
IssueDeleted,
IssueTitleChanged,
IssueDescriptionChanged,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, TS)]
#[serde(rename_all = "snake_case")]
pub enum NotificationGroupKind {
Single,
IssueChanges,
StatusChanges,
Comments,
Reactions,
IssueDeleted,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct Notification {
pub id: Uuid,
pub organization_id: Uuid,
pub user_id: Uuid,
pub notification_type: NotificationType,
pub payload: NotificationPayload,
pub issue_id: Option<Uuid>,
pub comment_id: Option<Uuid>,
pub seen: bool,
pub dismissed_at: Option<DateTime<Utc>>,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize, TS)]
pub struct NotificationPayload {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub deeplink_path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub issue_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub issue_simple_id: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub issue_title: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub actor_user_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub comment_preview: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub old_status_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub new_status_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub old_status_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub new_status_name: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub new_title: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub old_priority: Option<IssuePriority>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub new_priority: Option<IssuePriority>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub assignee_user_id: Option<Uuid>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub emoji: Option<String>,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct UpdateNotificationRequest {
#[serde(default, deserialize_with = "some_if_present")]
pub seen: Option<bool>,
}

View File

@@ -1,26 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use sqlx::Type;
use ts_rs::TS;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type, TS)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
#[sqlx(type_name = "member_role", rename_all = "lowercase")]
#[ts(use_ts_enum)]
#[ts(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum MemberRole {
Admin,
Member,
}
/// Organization member as stored in the database / streamed via Electric.
/// This is the full row type with organization_id for shapes.
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct OrganizationMember {
pub organization_id: Uuid,
pub user_id: Uuid,
pub role: MemberRole,
pub joined_at: DateTime<Utc>,
pub last_seen_at: Option<DateTime<Utc>>,
}

View File

@@ -1,66 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
use crate::some_if_present;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct Project {
pub id: Uuid,
pub organization_id: Uuid,
pub name: String,
pub color: String,
pub sort_order: i32,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct CreateProjectRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub organization_id: Uuid,
pub name: String,
pub color: String,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct UpdateProjectRequest {
#[serde(default, deserialize_with = "some_if_present")]
pub name: Option<String>,
#[serde(default, deserialize_with = "some_if_present")]
pub color: Option<String>,
#[serde(default, deserialize_with = "some_if_present")]
pub sort_order: Option<i32>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListProjectsQuery {
pub organization_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListProjectsResponse {
pub projects: Vec<Project>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BulkUpdateProjectItem {
pub id: Uuid,
#[serde(flatten)]
pub changes: UpdateProjectRequest,
}
#[derive(Debug, Clone, Deserialize)]
pub struct BulkUpdateProjectsRequest {
pub updates: Vec<BulkUpdateProjectItem>,
}
#[derive(Debug, Clone, Serialize)]
pub struct BulkUpdateProjectsResponse {
pub data: Vec<Project>,
pub txid: i64,
}

View File

@@ -1,52 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
use crate::some_if_present;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ProjectStatus {
pub id: Uuid,
pub project_id: Uuid,
pub name: String,
pub color: String,
pub sort_order: i32,
pub hidden: bool,
pub created_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct CreateProjectStatusRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub project_id: Uuid,
pub name: String,
pub color: String,
pub sort_order: i32,
pub hidden: bool,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct UpdateProjectStatusRequest {
#[serde(default, deserialize_with = "some_if_present")]
pub name: Option<String>,
#[serde(default, deserialize_with = "some_if_present")]
pub color: Option<String>,
#[serde(default, deserialize_with = "some_if_present")]
pub sort_order: Option<i32>,
#[serde(default, deserialize_with = "some_if_present")]
pub hidden: Option<bool>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListProjectStatusesQuery {
pub project_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListProjectStatusesResponse {
pub project_statuses: Vec<ProjectStatus>,
}

View File

@@ -1,81 +0,0 @@
use chrono::{DateTime, Utc};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use sqlx::Type;
use ts_rs::TS;
use uuid::Uuid;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, Type, TS, JsonSchema)]
#[sqlx(type_name = "pull_request_status", rename_all = "lowercase")]
#[serde(rename_all = "lowercase")]
pub enum PullRequestStatus {
Open,
Merged,
Closed,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct PullRequest {
pub id: Uuid,
pub url: String,
pub number: i32,
pub status: PullRequestStatus,
pub merged_at: Option<DateTime<Utc>>,
pub merge_commit_sha: Option<String>,
pub target_branch_name: String,
pub project_id: Uuid,
#[deprecated(note = "use pull_request_issues join table instead")]
pub issue_id: Uuid,
pub workspace_id: Option<Uuid>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct PullRequestIssue {
pub id: Uuid,
pub pull_request_id: Uuid,
pub issue_id: Uuid,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListPullRequestsQuery {
pub issue_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListPullRequestsResponse {
pub pull_requests: Vec<PullRequest>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListPullRequestIssuesResponse {
pub pull_request_issues: Vec<PullRequestIssue>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct CreatePullRequestIssueRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub issue_id: Uuid,
pub url: String,
pub number: i32,
pub status: PullRequestStatus,
pub merged_at: Option<DateTime<Utc>>,
pub merge_commit_sha: Option<String>,
pub target_branch_name: String,
}
/// Request to update a PR status on the remote server.
#[derive(Debug, Deserialize, Serialize)]
pub struct UpdatePullRequestApiRequest {
pub url: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub status: Option<PullRequestStatus>,
#[serde(skip_serializing_if = "Option::is_none")]
pub merged_at: Option<Option<DateTime<Utc>>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub merge_commit_sha: Option<Option<String>>,
}

View File

@@ -1,18 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use uuid::Uuid;
use crate::PullRequestStatus;
#[derive(Debug, Deserialize, Serialize)]
pub struct UpsertPullRequestRequest {
pub url: String,
pub number: i32,
pub status: PullRequestStatus,
#[serde(skip_serializing_if = "Option::is_none")]
pub merged_at: Option<DateTime<Utc>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub merge_commit_sha: Option<String>,
pub target_branch_name: String,
pub local_workspace_id: Uuid,
}

View File

@@ -1,16 +0,0 @@
use serde::{Deserialize, Serialize};
use ts_rs::TS;
/// Response wrapper for mutation endpoints (create/update).
/// Includes the Postgres transaction ID for Electric sync.
#[derive(Debug, Serialize, Deserialize)]
pub struct MutationResponse<T> {
pub data: T,
pub txid: i64,
}
/// Response wrapper for delete endpoints.
#[derive(Debug, Serialize, Deserialize, TS)]
pub struct DeleteResponse {
pub txid: i64,
}

View File

@@ -1,42 +0,0 @@
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
use crate::some_if_present;
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct Tag {
pub id: Uuid,
pub project_id: Uuid,
pub name: String,
pub color: String,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct CreateTagRequest {
/// Optional client-generated ID. If not provided, server generates one.
/// Using client-generated IDs enables stable optimistic updates.
#[ts(optional)]
pub id: Option<Uuid>,
pub project_id: Uuid,
pub name: String,
pub color: String,
}
#[derive(Debug, Clone, Deserialize, TS)]
pub struct UpdateTagRequest {
#[serde(default, deserialize_with = "some_if_present")]
pub name: Option<String>,
#[serde(default, deserialize_with = "some_if_present")]
pub color: Option<String>,
}
#[derive(Debug, Clone, Deserialize)]
pub struct ListTagsQuery {
pub project_id: Uuid,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct ListTagsResponse {
pub tags: Vec<Tag>,
}

View File

@@ -1,23 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow, TS)]
pub struct User {
pub id: Uuid,
pub email: String,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub username: Option<String>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}
#[derive(Debug, Clone, Serialize, Deserialize, TS)]
pub struct UserData {
pub user_id: Uuid,
pub first_name: Option<String>,
pub last_name: Option<String>,
pub username: Option<String>,
}

View File

@@ -1,21 +0,0 @@
use chrono::{DateTime, Utc};
use serde::{Deserialize, Serialize};
use ts_rs::TS;
use uuid::Uuid;
/// Workspace metadata pushed from local clients
#[derive(Debug, Clone, Serialize, Deserialize, sqlx::FromRow, TS)]
pub struct Workspace {
pub id: Uuid,
pub project_id: Uuid,
pub owner_user_id: Uuid,
pub issue_id: Option<Uuid>,
pub local_workspace_id: Option<Uuid>,
pub name: Option<String>,
pub archived: bool,
pub files_changed: Option<i32>,
pub lines_added: Option<i32>,
pub lines_removed: Option<i32>,
pub created_at: DateTime<Utc>,
pub updated_at: DateTime<Utc>,
}

View File

@@ -1,39 +0,0 @@
use serde::{Deserialize, Serialize};
use uuid::Uuid;
#[derive(Debug, Deserialize, Serialize)]
pub struct DeleteWorkspaceRequest {
pub local_workspace_id: Uuid,
}
#[derive(Debug, Serialize)]
pub struct CreateWorkspaceRequest {
pub project_id: Uuid,
pub local_workspace_id: Uuid,
pub issue_id: Uuid,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub archived: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub files_changed: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lines_added: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lines_removed: Option<i32>,
}
#[derive(Debug, Deserialize, Serialize)]
pub struct UpdateWorkspaceRequest {
pub local_workspace_id: Uuid,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<Option<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub archived: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub files_changed: Option<Option<i32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lines_added: Option<Option<i32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub lines_removed: Option<Option<i32>>,
}

View File

@@ -1,4 +0,0 @@
[package]
name = "client-info"
version = "0.1.44"
edition = "2024"

View File

@@ -1,43 +0,0 @@
use std::{net::SocketAddr, sync::OnceLock};
/// Runtime information about the local server.
#[derive(Clone)]
pub struct ClientInfo {
server_addr: OnceLock<SocketAddr>,
preview_proxy_port: OnceLock<u16>,
}
impl Default for ClientInfo {
fn default() -> Self {
Self::new()
}
}
impl ClientInfo {
pub fn new() -> Self {
Self {
server_addr: OnceLock::new(),
preview_proxy_port: OnceLock::new(),
}
}
pub fn set_server_addr(&self, addr: SocketAddr) -> Result<(), String> {
self.server_addr
.set(addr)
.map_err(|_| "server address already set".to_string())
}
pub fn get_server_addr(&self) -> Option<SocketAddr> {
self.server_addr.get().copied()
}
pub fn set_preview_proxy_port(&self, port: u16) -> Result<(), String> {
self.preview_proxy_port
.set(port)
.map_err(|_| "preview proxy port already set".to_string())
}
pub fn get_preview_proxy_port(&self) -> Option<u16> {
self.preview_proxy_port.get().copied()
}
}

View File

@@ -0,0 +1,50 @@
{
"db_name": "SQLite",
"query": "SELECT r.id as \"id!: Uuid\",\n r.path,\n r.name,\n r.display_name, \n r.created_at as \"created_at!: DateTime<Utc>\",\n r.updated_at as \"updated_at!: DateTime<Utc>\"\n FROM repos r\n JOIN project_repos pr ON r.id = pr.repo_id\n WHERE pr.project_id = $1\n ORDER BY r.display_name ASC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "path",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "name",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "display_name",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
false,
false
]
},
"hash": "04520564f1eaac8491598976ff58b540443f2f1dc35b5a186f243b297089e34b"
}

View File

@@ -0,0 +1,32 @@
{
"db_name": "SQLite",
"query": "SELECT w.id as \"workspace_id!: Uuid\",\n w.task_id as \"task_id!: Uuid\",\n t.project_id as \"project_id!: Uuid\"\n FROM workspaces w\n JOIN tasks t ON w.task_id = t.id\n WHERE w.container_ref = ?",
"describe": {
"columns": [
{
"name": "workspace_id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "task_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "project_id!: Uuid",
"ordinal": 2,
"type_info": "Blob"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false
]
},
"hash": "08573c65e8647ba8ea4e27174fa3f0fa1b2e687469658ad1245a323c8326f8a7"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE tasks SET status = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $1",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "0bf539bafb9c27cb352b0e08722c59a1cca3b6073517c982e5c08f62bc3ef4e4"
}

View File

@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "SELECT pr.id as \"id!: Uuid\",\n pr.project_id as \"project_id!: Uuid\",\n pr.repo_id as \"repo_id!: Uuid\",\n r.name as \"repo_name!\",\n pr.setup_script,\n pr.cleanup_script,\n pr.copy_files,\n pr.parallel_setup_script as \"parallel_setup_script!: bool\"\n FROM project_repos pr\n JOIN repos r ON r.id = pr.repo_id\n WHERE pr.project_id = $1\n ORDER BY r.display_name ASC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "project_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "repo_id!: Uuid",
"ordinal": 2,
"type_info": "Blob"
},
{
"name": "repo_name!",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "setup_script",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "cleanup_script",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "copy_files",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "parallel_setup_script!: bool",
"ordinal": 7,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
true,
true,
true,
false
]
},
"hash": "0c9ee0273c78ae47634deb65dba5c052d7d21810b34bcd0bd498e4fc3de0b259"
}

View File

@@ -0,0 +1,50 @@
{
"db_name": "SQLite",
"query": "SELECT DISTINCT r.id as \"id!: Uuid\",\n r.path,\n r.name,\n r.display_name,\n r.created_at as \"created_at!: DateTime<Utc>\",\n r.updated_at as \"updated_at!: DateTime<Utc>\"\n FROM repos r\n JOIN workspace_repos wr ON r.id = wr.repo_id\n JOIN workspaces w ON wr.workspace_id = w.id\n WHERE w.task_id = $1\n ORDER BY r.display_name ASC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "path",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "name",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "display_name",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
false,
false
]
},
"hash": "0de4c4f0d2e2eb13dad04fe330b452361ab582a973ca51b2272bd2e1829cfd50"
}

View File

@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: Uuid\",\n task_id AS \"task_id!: Uuid\",\n container_ref,\n branch,\n agent_working_dir,\n setup_completed_at AS \"setup_completed_at: DateTime<Utc>\",\n created_at AS \"created_at!: DateTime<Utc>\",\n updated_at AS \"updated_at!: DateTime<Utc>\"\n FROM workspaces\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "task_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "container_ref",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "branch",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "agent_working_dir",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "setup_completed_at: DateTime<Utc>",
"ordinal": 5,
"type_info": "Datetime"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false,
true,
false,
true,
true,
false,
false
]
},
"hash": "0e51827057df2d9e78304623e44487109740a841bdab334bb8b43db60215ecb1"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "DELETE FROM pull_requests WHERE id = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "0ee24d51e7557d9d6c9418a06156c03d7dd49dfe938194cd6951942dfd8f179d"
}

View File

@@ -0,0 +1,86 @@
{
"db_name": "SQLite",
"query": "SELECT\n id as \"id!: Uuid\",\n workspace_id as \"workspace_id!: Uuid\",\n repo_id as \"repo_id!: Uuid\",\n merge_type as \"merge_type!: MergeType\",\n merge_commit,\n pr_number,\n pr_url,\n pr_status as \"pr_status?: MergeStatus\",\n pr_merged_at as \"pr_merged_at?: DateTime<Utc>\",\n pr_merge_commit_sha,\n target_branch_name as \"target_branch_name!: String\",\n created_at as \"created_at!: DateTime<Utc>\"\n FROM merges\n WHERE workspace_id = $1 AND repo_id = $2\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "workspace_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "repo_id!: Uuid",
"ordinal": 2,
"type_info": "Blob"
},
{
"name": "merge_type!: MergeType",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "merge_commit",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "pr_number",
"ordinal": 5,
"type_info": "Integer"
},
{
"name": "pr_url",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "pr_status?: MergeStatus",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "pr_merged_at?: DateTime<Utc>",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "pr_merge_commit_sha",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "target_branch_name!: String",
"ordinal": 10,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 11,
"type_info": "Text"
}
],
"parameters": {
"Right": 2
},
"nullable": [
true,
false,
false,
false,
true,
true,
true,
true,
true,
true,
false,
false
]
},
"hash": "1085d1f8107c7e16fc2058ef610918760d8d420f0fca97adecd76d698f6f3a51"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "SELECT id as \"id!: Uuid\",\n file_path as \"file_path!\",\n original_name as \"original_name!\",\n mime_type,\n size_bytes as \"size_bytes!\",\n hash as \"hash!\",\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"\n FROM attachments\n WHERE id = $1",
"query": "SELECT id as \"id!: Uuid\",\n file_path as \"file_path!\",\n original_name as \"original_name!\",\n mime_type,\n size_bytes as \"size_bytes!\",\n hash as \"hash!\",\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"\n FROM images\n WHERE id = $1",
"describe": {
"columns": [
{
@@ -58,5 +58,5 @@
false
]
},
"hash": "dc5d0ad507cbd962235c9e85c3e43f34c7c38eb2e08ab7899073010a6e77b37d"
"hash": "1280290c78a1f55b3f0074ebcb61d855cab3e9be9ab0bc8c3f678adf4506b9cc"
}

View File

@@ -1,26 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT \n ep.id as \"execution_id!: Uuid\", \n ep.session_id as \"session_id!: Uuid\"\n FROM execution_processes ep\n WHERE EXISTS (\n SELECT 1 FROM execution_process_logs epl WHERE epl.execution_id = ep.id\n )\n ",
"describe": {
"columns": [
{
"name": "execution_id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "session_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false
]
},
"hash": "12a5c0a8b95d8cb87f1c869ff35692a2cee52bc418b06d00a31a4c139e12d18a"
}

View File

@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "SELECT id as \"id!: Uuid\",\n name,\n dev_script,\n dev_script_working_dir,\n default_agent_working_dir,\n remote_project_id as \"remote_project_id: Uuid\",\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"\n FROM projects\n WHERE rowid = $1",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "name",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "dev_script",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "dev_script_working_dir",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "default_agent_working_dir",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "remote_project_id: Uuid",
"ordinal": 5,
"type_info": "Blob"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
true,
true,
true,
true,
false,
false
]
},
"hash": "17789c934ca49a04a85505f1a9c861a575ee8fa2e8b6c9e9f4fc177c09caa53d"
}

View File

@@ -1,86 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT\n id,\n workspace_id AS \"workspace_id: Uuid\",\n repo_id AS \"repo_id: Uuid\",\n pr_url,\n pr_number,\n pr_status AS \"pr_status: MergeStatus\",\n target_branch_name,\n merged_at AS \"merged_at: DateTime<Utc>\",\n merge_commit_sha,\n created_at AS \"created_at!: DateTime<Utc>\",\n updated_at AS \"updated_at!: DateTime<Utc>\",\n synced_at AS \"synced_at: DateTime<Utc>\"\n FROM pull_requests\n WHERE synced_at IS NULL OR synced_at < updated_at",
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "workspace_id: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "repo_id: Uuid",
"ordinal": 2,
"type_info": "Blob"
},
{
"name": "pr_url",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "pr_number",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "pr_status: MergeStatus",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "target_branch_name",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "merged_at: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "merge_commit_sha",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 9,
"type_info": "Datetime"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 10,
"type_info": "Datetime"
},
{
"name": "synced_at: DateTime<Utc>",
"ordinal": 11,
"type_info": "Datetime"
}
],
"parameters": {
"Right": 0
},
"nullable": [
false,
true,
true,
false,
false,
false,
false,
true,
true,
false,
false,
true
]
},
"hash": "1af7a606a64d9662d6edc4fbeaae0d53a4d657a6e29b2561e820085a2d9e3348"
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT COUNT(*) as \"count!: i64\"\n FROM execution_processes ep\n WHERE ep.session_id = $1\n AND ep.status = 'running'\n AND ep.run_reason = 'codingagent'",
"describe": {
"columns": [
{
"name": "count!: i64",
"ordinal": 0,
"type_info": "Integer"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false
]
},
"hash": "1b186dc075846fc1f7270a942afbf82a88806ee6ababdb437ab5e97ddd2122da"
}

View File

@@ -0,0 +1,56 @@
{
"db_name": "SQLite",
"query": "INSERT INTO project_repos (id, project_id, repo_id)\n VALUES ($1, $2, $3)\n RETURNING id as \"id!: Uuid\",\n project_id as \"project_id!: Uuid\",\n repo_id as \"repo_id!: Uuid\",\n setup_script,\n cleanup_script,\n copy_files,\n parallel_setup_script as \"parallel_setup_script!: bool\"",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "project_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "repo_id!: Uuid",
"ordinal": 2,
"type_info": "Blob"
},
{
"name": "setup_script",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "cleanup_script",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "copy_files",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "parallel_setup_script!: bool",
"ordinal": 6,
"type_info": "Integer"
}
],
"parameters": {
"Right": 3
},
"nullable": [
true,
false,
false,
true,
true,
true,
false
]
},
"hash": "1c513f6c9d9025fa5dda8133a1be2d620301068c177d13b12a776c8c88104e2b"
}

View File

@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: Uuid\",\n task_id AS \"task_id!: Uuid\",\n container_ref,\n branch,\n agent_working_dir,\n setup_completed_at AS \"setup_completed_at: DateTime<Utc>\",\n created_at AS \"created_at!: DateTime<Utc>\",\n updated_at AS \"updated_at!: DateTime<Utc>\"\n FROM workspaces\n WHERE rowid = $1",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "task_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "container_ref",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "branch",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "agent_working_dir",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "setup_completed_at: DateTime<Utc>",
"ordinal": 5,
"type_info": "Datetime"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
true,
false,
true,
true,
false,
false
]
},
"hash": "1d84ba9ca8016f7b15d84acb3442c3b84ff344f666c31bb29223b6aaa440091f"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "DELETE FROM repos WHERE id = $1",
"query": "DELETE FROM tasks WHERE id = $1",
"describe": {
"columns": [],
"parameters": {
@@ -8,5 +8,5 @@
},
"nullable": []
},
"hash": "3a9148e9e914d644d4d82a1c2dc8bd0e093d4f4c638afa7fd8f5211892fb6d84"
"hash": "1e339e959f8d2cdac13b3e2b452d2f718c0fd6cf6202d5c9139fb1afda123d29"
}

View File

@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "SELECT id as \"id!: Uuid\",\n name,\n dev_script,\n dev_script_working_dir,\n default_agent_working_dir,\n remote_project_id as \"remote_project_id: Uuid\",\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"\n FROM projects\n WHERE id = $1",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "name",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "dev_script",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "dev_script_working_dir",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "default_agent_working_dir",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "remote_project_id: Uuid",
"ordinal": 5,
"type_info": "Blob"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
true,
true,
true,
true,
false,
false
]
},
"hash": "1eae64d51ea1d81c7239fc3640bb15bb59a51333602e7050bec3f5e8fc7fc782"
}

View File

@@ -0,0 +1,80 @@
{
"db_name": "SQLite",
"query": "SELECT ep.id as \"id!: Uuid\", ep.session_id as \"session_id!: Uuid\", ep.run_reason as \"run_reason!: ExecutionProcessRunReason\", ep.executor_action as \"executor_action!: sqlx::types::Json<ExecutorActionField>\",\n ep.status as \"status!: ExecutionProcessStatus\", ep.exit_code,\n ep.dropped as \"dropped!: bool\", ep.started_at as \"started_at!: DateTime<Utc>\", ep.completed_at as \"completed_at?: DateTime<Utc>\", ep.created_at as \"created_at!: DateTime<Utc>\", ep.updated_at as \"updated_at!: DateTime<Utc>\"\n FROM execution_processes ep\n JOIN sessions s ON ep.session_id = s.id\n JOIN workspaces w ON s.workspace_id = w.id\n JOIN tasks t ON w.task_id = t.id\n WHERE ep.status = 'running' AND ep.run_reason = 'devserver' AND t.project_id = ?\n ORDER BY ep.created_at ASC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "session_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "run_reason!: ExecutionProcessRunReason",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "executor_action!: sqlx::types::Json<ExecutorActionField>",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "status!: ExecutionProcessStatus",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "exit_code",
"ordinal": 5,
"type_info": "Integer"
},
{
"name": "dropped!: bool",
"ordinal": 6,
"type_info": "Integer"
},
{
"name": "started_at!: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "completed_at?: DateTime<Utc>",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 10,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
false,
true,
false,
false,
true,
false,
false
]
},
"hash": "20e4f7163c5fd5bafc868531c03a9a00cc48c21944c486a21529715504065572"
}

View File

@@ -0,0 +1,50 @@
{
"db_name": "SQLite",
"query": "SELECT r.id as \"id!: Uuid\",\n r.path,\n r.name,\n r.display_name,\n r.created_at as \"created_at!: DateTime<Utc>\",\n r.updated_at as \"updated_at!: DateTime<Utc>\"\n FROM repos r\n JOIN workspace_repos wr ON r.id = wr.repo_id\n WHERE wr.workspace_id = $1\n ORDER BY r.display_name ASC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "path",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "name",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "display_name",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
false,
false
]
},
"hash": "23049e8ff32ee491346afb47628d71d1748508dabc033dd2fd059bab8c422630"
}

View File

@@ -1,26 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT\n cat.agent_session_id as \"session_id!\",\n cat.agent_message_id as \"message_id\"\n FROM execution_processes ep\n JOIN coding_agent_turns cat ON ep.id = cat.execution_process_id\n WHERE ep.session_id = $1\n AND ep.run_reason = 'codingagent'\n AND ep.dropped = FALSE\n AND cat.agent_session_id IS NOT NULL\n ORDER BY ep.created_at DESC\n LIMIT 1",
"describe": {
"columns": [
{
"name": "session_id!",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "message_id",
"ordinal": 1,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
true
]
},
"hash": "2547a5d06fd3b17360bff34a04b7d3d929c13ef0d86395a9201834d8fc955295"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "SELECT id as \"id!: Uuid\",\n file_path as \"file_path!\",\n original_name as \"original_name!\",\n mime_type,\n size_bytes as \"size_bytes!\",\n hash as \"hash!\",\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"\n FROM attachments\n WHERE hash = $1",
"query": "SELECT id as \"id!: Uuid\",\n file_path as \"file_path!\",\n original_name as \"original_name!\",\n mime_type,\n size_bytes as \"size_bytes!\",\n hash as \"hash!\",\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"\n FROM images\n WHERE hash = $1",
"describe": {
"columns": [
{
@@ -58,5 +58,5 @@
false
]
},
"hash": "04f17449e3e12785affab91e4eab308103491e34c022199b7b060e04fa8aed0f"
"hash": "2827716b6501cc3c44ec50ae1a3d90f759cc3940c8fe6ffe383d0f741e3a2a78"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "UPDATE pull_requests SET pr_status = ?, merged_at = ?, merge_commit_sha = ?, updated_at = ?, synced_at = NULL WHERE pr_url = ?",
"describe": {
"columns": [],
"parameters": {
"Right": 5
},
"nullable": []
},
"hash": "2948ec0606a39ee67bdbaf4f2cf82592448a1d91a66d7d89cf7204f71f706274"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE tasks SET parent_workspace_id = $2, updated_at = CURRENT_TIMESTAMP WHERE id = $1",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "29c57b844d1370f68c16cf8684ced36df57dddcf0ccafa6dd8ab168eceb07752"
}

View File

@@ -1,98 +0,0 @@
{
"db_name": "SQLite",
"query": "INSERT INTO repos (id, path, name, display_name)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT(path) DO UPDATE SET updated_at = updated_at\n RETURNING id as \"id!: Uuid\",\n path,\n name,\n display_name,\n setup_script,\n cleanup_script,\n archive_script,\n copy_files,\n parallel_setup_script as \"parallel_setup_script!: bool\",\n dev_server_script,\n default_target_branch,\n default_working_dir,\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "path",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "name",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "display_name",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "setup_script",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "cleanup_script",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "archive_script",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "copy_files",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "parallel_setup_script!: bool",
"ordinal": 8,
"type_info": "Integer"
},
{
"name": "dev_server_script",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "default_target_branch",
"ordinal": 10,
"type_info": "Text"
},
{
"name": "default_working_dir",
"ordinal": 11,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 12,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 13,
"type_info": "Text"
}
],
"parameters": {
"Right": 4
},
"nullable": [
true,
false,
false,
false,
true,
true,
true,
true,
false,
true,
true,
true,
false,
false
]
},
"hash": "2b253f92ac5daa4864e7335fde1b82625f504fd73d19b21992497219a9c3170a"
}

View File

@@ -1,20 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT w.name AS \"name: String\"\n FROM workspaces w\n JOIN workspace_repos wr ON wr.workspace_id = w.id\n WHERE wr.repo_id = $1\n AND w.archived = FALSE",
"describe": {
"columns": [
{
"name": "name: String",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true
]
},
"hash": "2c71bf4dd5683e0dedf2341e52880ff2c0765659d3cf53d62faa54adc91071dd"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "UPDATE sessions SET executor = $1, updated_at = CURRENT_TIMESTAMP WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "2cb5a269045f23da9f4ee0ee679ccb7fffc39d4b37b1b58357b11a7abfdba125"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE tasks\n SET shared_task_id = NULL\n WHERE project_id IN (\n SELECT id FROM projects WHERE remote_project_id = $1\n )",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "31cbff397a2c2c116f9f7558d04ad2de76b4b3aaa072253172f40ef378998451"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "SELECT\n id as \"id!: Uuid\",\n execution_process_id as \"execution_process_id!: Uuid\",\n agent_session_id,\n agent_message_id,\n prompt,\n summary,\n seen as \"seen!: bool\",\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"\n FROM coding_agent_turns\n WHERE execution_process_id = $1",
"query": "SELECT\n id as \"id!: Uuid\",\n execution_process_id as \"execution_process_id!: Uuid\",\n agent_session_id,\n prompt,\n summary,\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"\n FROM coding_agent_turns\n WHERE execution_process_id = $1",
"describe": {
"columns": [
{
@@ -19,33 +19,23 @@
"type_info": "Text"
},
{
"name": "agent_message_id",
"name": "prompt",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "prompt",
"name": "summary",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "summary",
"name": "created_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "seen!: bool",
"ordinal": 6,
"type_info": "Integer"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 8,
"ordinal": 6,
"type_info": "Text"
}
],
@@ -58,11 +48,9 @@
true,
true,
true,
true,
false,
false,
false
]
},
"hash": "fc90f4dd7a408d6129aff95538de22c3a1ca018bc7837e3dc1c5aa0007844887"
"hash": "3237245fad0281421e131ee97de37464a7077cce1224fd45de95eb16a07fff79"
}

View File

@@ -1,86 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT\n id,\n workspace_id AS \"workspace_id: Uuid\",\n repo_id AS \"repo_id: Uuid\",\n pr_url,\n pr_number,\n pr_status AS \"pr_status: MergeStatus\",\n target_branch_name,\n merged_at AS \"merged_at: DateTime<Utc>\",\n merge_commit_sha,\n created_at AS \"created_at!: DateTime<Utc>\",\n updated_at AS \"updated_at!: DateTime<Utc>\",\n synced_at AS \"synced_at: DateTime<Utc>\"\n FROM pull_requests\n WHERE workspace_id = $1 AND repo_id = $2\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "workspace_id: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "repo_id: Uuid",
"ordinal": 2,
"type_info": "Blob"
},
{
"name": "pr_url",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "pr_number",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "pr_status: MergeStatus",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "target_branch_name",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "merged_at: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "merge_commit_sha",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 9,
"type_info": "Datetime"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 10,
"type_info": "Datetime"
},
{
"name": "synced_at: DateTime<Utc>",
"ordinal": 11,
"type_info": "Datetime"
}
],
"parameters": {
"Right": 2
},
"nullable": [
false,
true,
true,
false,
false,
false,
false,
true,
true,
false,
false,
true
]
},
"hash": "32c74a54fdd72a6b167583228f2c30944837b7cc29c9cc4acfbb4f047cea7118"
}

View File

@@ -1,26 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT id as \"id!: Uuid\",\n container_ref as \"container_ref!\"\n FROM workspaces\n WHERE container_ref IS NOT NULL",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "container_ref!",
"ordinal": 1,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
true
]
},
"hash": "3634e2bab8fef106721bb64a791edd81d3d49eb34fbabd34e4feadfb5f229a6e"
}

View File

@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "\n SELECT p.id as \"id!: Uuid\", p.name, p.dev_script, p.dev_script_working_dir,\n p.default_agent_working_dir,\n p.remote_project_id as \"remote_project_id: Uuid\",\n p.created_at as \"created_at!: DateTime<Utc>\", p.updated_at as \"updated_at!: DateTime<Utc>\"\n FROM projects p\n WHERE p.id IN (\n SELECT DISTINCT t.project_id\n FROM tasks t\n INNER JOIN workspaces w ON w.task_id = t.id\n ORDER BY w.updated_at DESC\n )\n LIMIT $1\n ",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "name",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "dev_script",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "dev_script_working_dir",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "default_agent_working_dir",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "remote_project_id: Uuid",
"ordinal": 5,
"type_info": "Blob"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
true,
true,
true,
true,
false,
false
]
},
"hash": "368d9be9608fea5002627625bf8abd3e9073e06bb72fc75f11c2af13f1d43a10"
}

View File

@@ -1,44 +0,0 @@
{
"db_name": "SQLite",
"query": "\n SELECT\n workspace_id as \"workspace_id!: Uuid\",\n execution_process_id as \"execution_process_id!: Uuid\",\n session_id as \"session_id!: Uuid\",\n status as \"status!: ExecutionProcessStatus\",\n completed_at as \"completed_at?: DateTime<Utc>\"\n FROM (\n SELECT\n s.workspace_id,\n ep.id as execution_process_id,\n ep.session_id,\n ep.status,\n ep.completed_at,\n ROW_NUMBER() OVER (\n PARTITION BY s.workspace_id\n ORDER BY ep.created_at DESC\n ) as rn\n FROM execution_processes ep\n JOIN sessions s ON ep.session_id = s.id\n JOIN workspaces w ON s.workspace_id = w.id\n WHERE w.archived = $1\n AND ep.run_reason IN ('codingagent', 'setupscript', 'cleanupscript')\n AND ep.dropped = FALSE\n )\n WHERE rn = 1\n ",
"describe": {
"columns": [
{
"name": "workspace_id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "execution_process_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "session_id!: Uuid",
"ordinal": 2,
"type_info": "Blob"
},
{
"name": "status!: ExecutionProcessStatus",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "completed_at?: DateTime<Utc>",
"ordinal": 4,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
true,
false,
false,
true
]
},
"hash": "3d34580933bc02a168f4a7c483460a6ad13ef72b508532f5a6cd5e53aff04a69"
}

View File

@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "\n SELECT\n w.id as \"id!: Uuid\",\n w.task_id as \"task_id!: Uuid\",\n w.container_ref,\n w.branch as \"branch!\",\n w.agent_working_dir,\n w.setup_completed_at as \"setup_completed_at: DateTime<Utc>\",\n w.created_at as \"created_at!: DateTime<Utc>\",\n w.updated_at as \"updated_at!: DateTime<Utc>\"\n FROM workspaces w\n LEFT JOIN sessions s ON w.id = s.workspace_id\n LEFT JOIN execution_processes ep ON s.id = ep.session_id AND ep.completed_at IS NOT NULL\n WHERE w.container_ref IS NOT NULL\n AND w.id NOT IN (\n SELECT DISTINCT s2.workspace_id\n FROM sessions s2\n JOIN execution_processes ep2 ON s2.id = ep2.session_id\n WHERE ep2.completed_at IS NULL\n )\n GROUP BY w.id, w.container_ref, w.updated_at\n HAVING datetime('now', '-72 hours') > datetime(\n MAX(\n CASE\n WHEN ep.completed_at IS NOT NULL THEN ep.completed_at\n ELSE w.updated_at\n END\n )\n )\n ORDER BY MAX(\n CASE\n WHEN ep.completed_at IS NOT NULL THEN ep.completed_at\n ELSE w.updated_at\n END\n ) ASC\n ",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "task_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "container_ref",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "branch!",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "agent_working_dir",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "setup_completed_at: DateTime<Utc>",
"ordinal": 5,
"type_info": "Datetime"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
true,
true,
true,
true,
true,
true,
true
]
},
"hash": "3d4cd3c4749b2b39e1d18f3a8e7fd38f5b5a165c796616a904acd820190df731"
}

View File

@@ -0,0 +1,86 @@
{
"db_name": "SQLite",
"query": "INSERT INTO merges (\n id, workspace_id, repo_id, merge_type, pr_number, pr_url, pr_status, created_at, target_branch_name\n ) VALUES ($1, $2, $3, 'pr', $4, $5, 'open', $6, $7)\n RETURNING\n id as \"id!: Uuid\",\n workspace_id as \"workspace_id!: Uuid\",\n repo_id as \"repo_id!: Uuid\",\n merge_type as \"merge_type!: MergeType\",\n merge_commit,\n pr_number,\n pr_url,\n pr_status as \"pr_status?: MergeStatus\",\n pr_merged_at as \"pr_merged_at?: DateTime<Utc>\",\n pr_merge_commit_sha,\n created_at as \"created_at!: DateTime<Utc>\",\n target_branch_name as \"target_branch_name!: String\"\n ",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "workspace_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "repo_id!: Uuid",
"ordinal": 2,
"type_info": "Blob"
},
{
"name": "merge_type!: MergeType",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "merge_commit",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "pr_number",
"ordinal": 5,
"type_info": "Integer"
},
{
"name": "pr_url",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "pr_status?: MergeStatus",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "pr_merged_at?: DateTime<Utc>",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "pr_merge_commit_sha",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 10,
"type_info": "Text"
},
{
"name": "target_branch_name!: String",
"ordinal": 11,
"type_info": "Text"
}
],
"parameters": {
"Right": 7
},
"nullable": [
true,
false,
false,
false,
true,
true,
true,
true,
true,
true,
false,
false
]
},
"hash": "3d85256618729c1c0bf2758ffab9cdb4ec2af0751e3a37db4009c02f95f6f556"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE tasks SET parent_workspace_id = NULL WHERE parent_workspace_id = $1",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "3d8b99f27ed12c313c11188e8fa3bb9a8a73f718e1afbd906e61e4f61714b346"
}

View File

@@ -0,0 +1,20 @@
{
"db_name": "SQLite",
"query": "SELECT cat.agent_session_id\n FROM execution_processes ep\n JOIN coding_agent_turns cat ON ep.id = cat.execution_process_id\n WHERE ep.session_id = $1\n AND ep.run_reason = 'codingagent'\n AND ep.dropped = FALSE\n AND cat.agent_session_id IS NOT NULL\n ORDER BY ep.created_at DESC\n LIMIT 1",
"describe": {
"columns": [
{
"name": "agent_session_id",
"ordinal": 0,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true
]
},
"hash": "42c7c5f202c30f5661d9fc271e6c854454b03c01f68c08c7867e5b909795f202"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE projects\n SET default_agent_working_dir = ''\n WHERE id = $1",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "43c10bbafd851c362132fd0657eaef5e16eb58d847540b1d998a741996967643"
}

View File

@@ -1,56 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT s.id AS \"id!: Uuid\",\n s.workspace_id AS \"workspace_id!: Uuid\",\n s.name,\n s.executor,\n s.agent_working_dir,\n s.created_at AS \"created_at!: DateTime<Utc>\",\n s.updated_at AS \"updated_at!: DateTime<Utc>\"\n FROM sessions s\n LEFT JOIN (\n SELECT ep.session_id, MAX(ep.created_at) as last_used\n FROM execution_processes ep\n WHERE ep.run_reason != 'devserver' AND ep.dropped = FALSE\n GROUP BY ep.session_id\n ) latest_ep ON s.id = latest_ep.session_id\n WHERE s.workspace_id = $1\n ORDER BY COALESCE(latest_ep.last_used, s.created_at) DESC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "workspace_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "name",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "executor",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "agent_working_dir",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
true,
true,
true,
false,
false
]
},
"hash": "4506eeeb85b49f00143a9d23636c976159e1c72c9cfce005599c5eb52dc15095"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "DELETE FROM attachments WHERE id = $1",
"describe": {
"columns": [],
"parameters": {
"Right": 1
},
"nullable": []
},
"hash": "4ac35216ead7e5be9cc2de504a06b6e375e23ca2ed14493ec991f53e458a6a34"
}

View File

@@ -0,0 +1,86 @@
{
"db_name": "SQLite",
"query": "SELECT\n id as \"id!: Uuid\",\n workspace_id as \"workspace_id!: Uuid\",\n repo_id as \"repo_id!: Uuid\",\n merge_type as \"merge_type!: MergeType\",\n merge_commit,\n pr_number,\n pr_url,\n pr_status as \"pr_status?: MergeStatus\",\n pr_merged_at as \"pr_merged_at?: DateTime<Utc>\",\n pr_merge_commit_sha,\n target_branch_name as \"target_branch_name!: String\",\n created_at as \"created_at!: DateTime<Utc>\"\n FROM merges\n WHERE workspace_id = $1\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "workspace_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "repo_id!: Uuid",
"ordinal": 2,
"type_info": "Blob"
},
{
"name": "merge_type!: MergeType",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "merge_commit",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "pr_number",
"ordinal": 5,
"type_info": "Integer"
},
{
"name": "pr_url",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "pr_status?: MergeStatus",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "pr_merged_at?: DateTime<Utc>",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "pr_merge_commit_sha",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "target_branch_name!: String",
"ordinal": 10,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 11,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
true,
true,
true,
true,
true,
true,
false,
false
]
},
"hash": "4b59b958807be54c7c9949d96ced96e4ab1498f1056e7d0d7956aff46352d90f"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE execution_process_repo_states\n SET merge_commit = $1, updated_at = $2\n WHERE execution_process_id = $3\n AND repo_id = $4",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "4d84b308a2cc7677da65111d080bf02e5e35c052048360d3dbea656bbbcd3edb"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "UPDATE workspaces SET archived = $1, updated_at = datetime('now', 'subsec') WHERE id = $2",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "4d86dcbf754f971ff3acffe9e85b5c2f455e77c40c624e09736be9480238110b"
}

View File

@@ -0,0 +1,62 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: Uuid\",\n task_id AS \"task_id!: Uuid\",\n container_ref,\n branch,\n agent_working_dir,\n setup_completed_at AS \"setup_completed_at: DateTime<Utc>\",\n created_at AS \"created_at!: DateTime<Utc>\",\n updated_at AS \"updated_at!: DateTime<Utc>\"\n FROM workspaces\n WHERE task_id = $1\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "task_id!: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "container_ref",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "branch",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "agent_working_dir",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "setup_completed_at: DateTime<Utc>",
"ordinal": 5,
"type_info": "Datetime"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
true,
false,
true,
true,
false,
false
]
},
"hash": "4f2044112bb23e40b229804cfa535a6b631d95ff7b5596706711f76a50516b40"
}

View File

@@ -1,92 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT\n w.id AS \"id!: Uuid\",\n w.task_id AS \"task_id: Uuid\",\n w.container_ref,\n w.branch,\n w.setup_completed_at AS \"setup_completed_at: DateTime<Utc>\",\n w.created_at AS \"created_at!: DateTime<Utc>\",\n w.updated_at AS \"updated_at!: DateTime<Utc>\",\n w.archived AS \"archived!: bool\",\n w.pinned AS \"pinned!: bool\",\n w.name,\n w.worktree_deleted AS \"worktree_deleted!: bool\",\n\n CASE WHEN EXISTS (\n SELECT 1\n FROM sessions s\n JOIN execution_processes ep ON ep.session_id = s.id\n WHERE s.workspace_id = w.id\n AND ep.status = 'running'\n AND ep.run_reason IN ('setupscript','cleanupscript','codingagent')\n LIMIT 1\n ) THEN 1 ELSE 0 END AS \"is_running!: i64\",\n\n CASE WHEN (\n SELECT ep.status\n FROM sessions s\n JOIN execution_processes ep ON ep.session_id = s.id\n WHERE s.workspace_id = w.id\n AND ep.run_reason IN ('setupscript','cleanupscript','codingagent')\n ORDER BY ep.created_at DESC\n LIMIT 1\n ) IN ('failed','killed') THEN 1 ELSE 0 END AS \"is_errored!: i64\"\n\n FROM workspaces w\n ORDER BY w.updated_at DESC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "task_id: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "container_ref",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "branch",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "setup_completed_at: DateTime<Utc>",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "archived!: bool",
"ordinal": 7,
"type_info": "Integer"
},
{
"name": "pinned!: bool",
"ordinal": 8,
"type_info": "Integer"
},
{
"name": "name",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "worktree_deleted!: bool",
"ordinal": 10,
"type_info": "Bool"
},
{
"name": "is_running!: i64",
"ordinal": 11,
"type_info": "Integer"
},
{
"name": "is_errored!: i64",
"ordinal": 12,
"type_info": "Integer"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
true,
true,
false,
true,
false,
false,
false,
false,
true,
false,
false,
false
]
},
"hash": "5154289c5a9ddbc061d42de2baf129e03a75061b8b305110921688f01d112de1"
}

View File

@@ -0,0 +1,50 @@
{
"db_name": "SQLite",
"query": "INSERT INTO repos (id, path, name, display_name)\n VALUES ($1, $2, $3, $4)\n ON CONFLICT(path) DO UPDATE SET updated_at = updated_at\n RETURNING id as \"id!: Uuid\",\n path,\n name,\n display_name,\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "path",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "name",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "display_name",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
}
],
"parameters": {
"Right": 4
},
"nullable": [
true,
false,
false,
false,
false,
false
]
},
"hash": "522e135173280ab71f7ba25b35efccb3eaeb7dc5fe778595887f70f62aae8df7"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "UPDATE merges\n SET pr_status = $1,\n pr_merge_commit_sha = $2,\n pr_merged_at = $3\n WHERE id = $4",
"describe": {
"columns": [],
"parameters": {
"Right": 4
},
"nullable": []
},
"hash": "5785a10c3d51ff9b001aa455f6296a4dcba61cec700a4b72031c5c643b273938"
}

View File

@@ -1,12 +0,0 @@
{
"db_name": "SQLite",
"query": "UPDATE workspaces SET\n archived = COALESCE($1, archived),\n pinned = COALESCE($2, pinned),\n name = CASE WHEN $3 THEN $4 ELSE name END,\n updated_at = datetime('now', 'subsec')\n WHERE id = $5",
"describe": {
"columns": [],
"parameters": {
"Right": 5
},
"nullable": []
},
"hash": "57d6335c98deb608cf961836f73a67163af6484f91954c0577aea1cc2fddac4f"
}

View File

@@ -1,98 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT id as \"id!: Uuid\",\n path,\n name,\n display_name,\n setup_script,\n cleanup_script,\n archive_script,\n copy_files,\n parallel_setup_script as \"parallel_setup_script!: bool\",\n dev_server_script,\n default_target_branch,\n default_working_dir,\n created_at as \"created_at!: DateTime<Utc>\",\n updated_at as \"updated_at!: DateTime<Utc>\"\n FROM repos\n WHERE name = '__NEEDS_BACKFILL__'",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "path",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "name",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "display_name",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "setup_script",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "cleanup_script",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "archive_script",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "copy_files",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "parallel_setup_script!: bool",
"ordinal": 8,
"type_info": "Integer"
},
{
"name": "dev_server_script",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "default_target_branch",
"ordinal": 10,
"type_info": "Text"
},
{
"name": "default_working_dir",
"ordinal": 11,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 12,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 13,
"type_info": "Text"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
false,
false,
false,
true,
true,
true,
true,
false,
true,
true,
true,
false,
false
]
},
"hash": "5884e7baa4a061166cb2911f717d3fd92852d62975a910dd9cb05e7908fdf8b6"
}

View File

@@ -1,92 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT\n w.id AS \"id!: Uuid\",\n w.task_id AS \"task_id: Uuid\",\n w.container_ref,\n w.branch,\n w.setup_completed_at AS \"setup_completed_at: DateTime<Utc>\",\n w.created_at AS \"created_at!: DateTime<Utc>\",\n w.updated_at AS \"updated_at!: DateTime<Utc>\",\n w.archived AS \"archived!: bool\",\n w.pinned AS \"pinned!: bool\",\n w.name,\n w.worktree_deleted AS \"worktree_deleted!: bool\",\n\n CASE WHEN EXISTS (\n SELECT 1\n FROM sessions s\n JOIN execution_processes ep ON ep.session_id = s.id\n WHERE s.workspace_id = w.id\n AND ep.status = 'running'\n AND ep.run_reason IN ('setupscript','cleanupscript','codingagent')\n LIMIT 1\n ) THEN 1 ELSE 0 END AS \"is_running!: i64\",\n\n CASE WHEN (\n SELECT ep.status\n FROM sessions s\n JOIN execution_processes ep ON ep.session_id = s.id\n WHERE s.workspace_id = w.id\n AND ep.run_reason IN ('setupscript','cleanupscript','codingagent')\n ORDER BY ep.created_at DESC\n LIMIT 1\n ) IN ('failed','killed') THEN 1 ELSE 0 END AS \"is_errored!: i64\"\n\n FROM workspaces w\n WHERE w.id = $1",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "task_id: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "container_ref",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "branch",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "setup_completed_at: DateTime<Utc>",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "archived!: bool",
"ordinal": 7,
"type_info": "Integer"
},
{
"name": "pinned!: bool",
"ordinal": 8,
"type_info": "Integer"
},
{
"name": "name",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "worktree_deleted!: bool",
"ordinal": 10,
"type_info": "Bool"
},
{
"name": "is_running!: i64",
"ordinal": 11,
"type_info": "Null"
},
{
"name": "is_errored!: i64",
"ordinal": 12,
"type_info": "Null"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
true,
true,
false,
true,
false,
false,
false,
false,
true,
false,
null,
null
]
},
"hash": "592656b17a5f78d117365909e47afba7d3df545ac1078c307c6b968e75c8e2ba"
}

View File

@@ -1,6 +1,6 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: Uuid\",\n workspace_id AS \"workspace_id!: Uuid\",\n name,\n executor,\n agent_working_dir,\n created_at AS \"created_at!: DateTime<Utc>\",\n updated_at AS \"updated_at!: DateTime<Utc>\"\n FROM sessions\n WHERE id = $1",
"query": "SELECT id AS \"id!: Uuid\",\n workspace_id AS \"workspace_id!: Uuid\",\n executor,\n created_at AS \"created_at!: DateTime<Utc>\",\n updated_at AS \"updated_at!: DateTime<Utc>\"\n FROM sessions\n WHERE workspace_id = $1\n ORDER BY created_at DESC\n LIMIT 1",
"describe": {
"columns": [
{
@@ -14,28 +14,18 @@
"type_info": "Blob"
},
{
"name": "name",
"name": "executor",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "executor",
"name": "created_at!: DateTime<Utc>",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "agent_working_dir",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 6,
"ordinal": 4,
"type_info": "Text"
}
],
@@ -46,11 +36,9 @@
true,
false,
true,
true,
true,
false,
false
]
},
"hash": "db39f7ab7391c1289299e7f8aa7e1f642874eed0179e91a9558f9df534db797c"
"hash": "5a8d67e33c564f02faef85a19503f204332b8353974718c96f23b2942c1fb142"
}

View File

@@ -0,0 +1,12 @@
{
"db_name": "SQLite",
"query": "DELETE FROM project_repos WHERE project_id = $1 AND repo_id = $2",
"describe": {
"columns": [],
"parameters": {
"Right": 2
},
"nullable": []
},
"hash": "5ee85b756876291353e65af979c25041b25e55abdd3bc87e322b096cd3045c41"
}

View File

@@ -0,0 +1,56 @@
{
"db_name": "SQLite",
"query": "SELECT r.id as \"id!: Uuid\",\n r.path,\n r.name,\n r.display_name,\n r.created_at as \"created_at!: DateTime<Utc>\",\n r.updated_at as \"updated_at!: DateTime<Utc>\",\n wr.target_branch\n FROM repos r\n JOIN workspace_repos wr ON r.id = wr.repo_id\n WHERE wr.workspace_id = $1\n ORDER BY r.display_name ASC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "path",
"ordinal": 1,
"type_info": "Text"
},
{
"name": "name",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "display_name",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "target_branch",
"ordinal": 6,
"type_info": "Text"
}
],
"parameters": {
"Right": 1
},
"nullable": [
true,
false,
false,
false,
false,
false,
false
]
},
"hash": "64b5d84eb77fbd8dd733cd92faad07842b1093ec3e8120fefac5c5f7cd7f2f8e"
}

View File

@@ -1,86 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT\n t.id,\n t.workspace_id AS \"workspace_id: Uuid\",\n t.repo_id AS \"repo_id: Uuid\",\n t.pr_url,\n t.pr_number,\n t.pr_status AS \"pr_status: MergeStatus\",\n t.target_branch_name,\n t.merged_at AS \"merged_at: DateTime<Utc>\",\n t.merge_commit_sha,\n t.created_at AS \"created_at!: DateTime<Utc>\",\n t.updated_at AS \"updated_at!: DateTime<Utc>\",\n t.synced_at AS \"synced_at: DateTime<Utc>\"\n FROM pull_requests t\n INNER JOIN (\n SELECT workspace_id, MAX(created_at) as max_created_at\n FROM pull_requests\n WHERE workspace_id IS NOT NULL\n GROUP BY workspace_id\n ) latest ON t.workspace_id = latest.workspace_id AND t.created_at = latest.max_created_at\n INNER JOIN workspaces w ON t.workspace_id = w.id\n WHERE t.workspace_id IS NOT NULL AND w.archived = $1",
"describe": {
"columns": [
{
"name": "id",
"ordinal": 0,
"type_info": "Text"
},
{
"name": "workspace_id: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "repo_id: Uuid",
"ordinal": 2,
"type_info": "Blob"
},
{
"name": "pr_url",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "pr_number",
"ordinal": 4,
"type_info": "Integer"
},
{
"name": "pr_status: MergeStatus",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "target_branch_name",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "merged_at: DateTime<Utc>",
"ordinal": 7,
"type_info": "Text"
},
{
"name": "merge_commit_sha",
"ordinal": 8,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 9,
"type_info": "Datetime"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 10,
"type_info": "Datetime"
},
{
"name": "synced_at: DateTime<Utc>",
"ordinal": 11,
"type_info": "Datetime"
}
],
"parameters": {
"Right": 1
},
"nullable": [
false,
true,
true,
false,
false,
false,
false,
true,
true,
false,
false,
true
]
},
"hash": "64cdb811a6d15a483fc0413dc49ba8aa093b1892831699c456afe0507ac51aa3"
}

View File

@@ -1,80 +0,0 @@
{
"db_name": "SQLite",
"query": "SELECT id AS \"id!: Uuid\",\n task_id AS \"task_id: Uuid\",\n container_ref,\n branch,\n setup_completed_at AS \"setup_completed_at: DateTime<Utc>\",\n created_at AS \"created_at!: DateTime<Utc>\",\n updated_at AS \"updated_at!: DateTime<Utc>\",\n archived AS \"archived!: bool\",\n pinned AS \"pinned!: bool\",\n name,\n worktree_deleted AS \"worktree_deleted!: bool\"\n FROM workspaces\n ORDER BY created_at DESC",
"describe": {
"columns": [
{
"name": "id!: Uuid",
"ordinal": 0,
"type_info": "Blob"
},
{
"name": "task_id: Uuid",
"ordinal": 1,
"type_info": "Blob"
},
{
"name": "container_ref",
"ordinal": 2,
"type_info": "Text"
},
{
"name": "branch",
"ordinal": 3,
"type_info": "Text"
},
{
"name": "setup_completed_at: DateTime<Utc>",
"ordinal": 4,
"type_info": "Text"
},
{
"name": "created_at!: DateTime<Utc>",
"ordinal": 5,
"type_info": "Text"
},
{
"name": "updated_at!: DateTime<Utc>",
"ordinal": 6,
"type_info": "Text"
},
{
"name": "archived!: bool",
"ordinal": 7,
"type_info": "Integer"
},
{
"name": "pinned!: bool",
"ordinal": 8,
"type_info": "Integer"
},
{
"name": "name",
"ordinal": 9,
"type_info": "Text"
},
{
"name": "worktree_deleted!: bool",
"ordinal": 10,
"type_info": "Bool"
}
],
"parameters": {
"Right": 0
},
"nullable": [
true,
true,
true,
false,
true,
false,
false,
false,
false,
true,
false
]
},
"hash": "64f31710ab7ba14047f31cce44ad36c60a53624f9bcb03a5eaff5d61ca8cc9cf"
}

Some files were not shown because too many files have changed in this diff Show More