From 6915bbf6919b810cf62cfad542972bbf76e71f09 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Sun, 3 May 2026 20:23:01 +0100 Subject: [PATCH 01/39] build: remove linux deb and rpm release artifacts --- .github/workflows/build-and-upload.yml | 70 ++------------------------ 1 file changed, 5 insertions(+), 65 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 9121c5f7..92c0c471 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -667,17 +667,15 @@ jobs: } appimage=$(find_one "*.AppImage") - deb=$(find_one "*.deb") - rpm=$(find_one "*.rpm") + fallback_bin="$SEARCH_ROOT/release/codenomad-tauri" - if [ -z "$appimage" ] || [ -z "$deb" ] || [ -z "$rpm" ]; then - echo "Missing bundle(s): appimage=${appimage:-none} deb=${deb:-none} rpm=${rpm:-none}" >&2 + if [ -z "$appimage" ] || [ ! -f "$fallback_bin" ]; then + echo "Missing bundle(s): appimage=${appimage:-none} binary=$fallback_bin" >&2 exit 1 fi cp "$appimage" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.AppImage" - cp "$deb" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.deb" - cp "$rpm" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.rpm" + zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.zip" "$fallback_bin" - name: Upload Actions artifacts (Tauri Linux) if: ${{ inputs.upload_actions_artifacts }} @@ -776,7 +774,7 @@ jobs: rm -rf "$ARTIFACT_DIR" mkdir -p "$ARTIFACT_DIR" shopt -s nullglob globstar - first_artifact=$(find "$SEARCH_ROOT" -type f \( -name "*.AppImage" -o -name "*.deb" -o -name "*.rpm" -o -name "*.tar.gz" \) | head -n1) + first_artifact=$(find "$SEARCH_ROOT" -type f \( -name "*.AppImage" -o -name "*.tar.gz" \) | head -n1) fallback_bin="$SEARCH_ROOT/release/codenomad-tauri" if [ -n "$first_artifact" ]; then zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.zip" "$first_artifact" @@ -797,61 +795,3 @@ jobs: echo "Uploading $file" gh release upload "$TAG" "$file" --clobber done - - - build-linux-rpm: - runs-on: ubuntu-24.04 - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - VERSION: ${{ inputs.version }} - TAG: ${{ inputs.tag }} - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - ref: ${{ inputs.ref || github.ref }} - - - name: Setup Node - uses: actions/setup-node@v4 - with: - node-version: ${{ env.NODE_VERSION }} - cache: npm - - - name: Install rpm packaging dependencies - run: | - sudo apt-get update - sudo apt-get install -y rpm ruby ruby-dev build-essential - sudo gem install --no-document fpm - - - name: Set workspace versions - if: ${{ inputs.set_versions && inputs.version != '' }} - run: npm version ${VERSION} --workspaces --include-workspace-root --no-git-tag-version --allow-same-version - - - name: Install project dependencies - run: npm ci --workspaces --include=optional - - - name: Ensure rollup native binary - run: npm install @rollup/rollup-linux-x64-gnu --no-save - - - name: Build Linux RPM binaries - run: npm run build:linux-rpm --workspace @neuralnomads/codenomad-electron-app - - - name: Upload RPM release assets - if: ${{ inputs.upload && inputs.tag != '' }} - run: | - set -euo pipefail - shopt -s nullglob - for file in packages/electron-app/release/*.rpm; do - [ -f "$file" ] || continue - echo "Uploading $file" - gh release upload "$TAG" "$file" --clobber - done - - - name: Upload Actions artifacts (Electron Linux RPM) - if: ${{ inputs.upload_actions_artifacts }} - uses: actions/upload-artifact@v4 - with: - name: ${{ inputs.actions_artifacts_name_prefix }}electron-linux-rpm - path: packages/electron-app/release/*.rpm - retention-days: ${{ inputs.actions_artifacts_retention_days }} - if-no-files-found: error From 3e9c152046381b54675fdd89fafa6273c49d0ec5 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Sun, 3 May 2026 20:29:42 +0100 Subject: [PATCH 02/39] build: normalize release artifact filenames --- .github/workflows/build-and-upload.yml | 18 +++++++++--------- packages/electron-app/package.json | 6 +++--- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/.github/workflows/build-and-upload.yml b/.github/workflows/build-and-upload.yml index 92c0c471..5ad815eb 100644 --- a/.github/workflows/build-and-upload.yml +++ b/.github/workflows/build-and-upload.yml @@ -162,7 +162,7 @@ jobs: arch="arm64" fi - out_zip="$release_root/CodeNomad-${VERSION_TO_USE}-mac-${arch}.zip" + out_zip="$release_root/CodeNomad-Electron-macos-${arch}-${VERSION_TO_USE}.zip" rm -f "$out_zip" echo "ditto -ck: $app -> $out_zip" ditto -ck --sequesterRsrc --keepParent "$app" "$out_zip" @@ -177,7 +177,7 @@ jobs: tmp_dir=$(mktemp -d) trap 'rm -rf "$tmp_dir"' EXIT - zips=(packages/electron-app/release/CodeNomad-*-mac-*.zip) + zips=(packages/electron-app/release/CodeNomad-Electron-macos-*.zip) if [ "${#zips[@]}" -eq 0 ]; then echo "No Electron macOS zip artifacts found to validate" >&2 exit 1 @@ -391,7 +391,7 @@ jobs: rm -rf "$ARTIFACT_DIR" mkdir -p "$ARTIFACT_DIR" if [ -d "$BUNDLE_ROOT/macos/CodeNomad.app" ]; then - ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-x64.zip" + ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-macos-x64-${VERSION}.zip" fi - name: Upload Actions artifacts (Tauri macOS) @@ -475,7 +475,7 @@ jobs: rm -rf "$ARTIFACT_DIR" mkdir -p "$ARTIFACT_DIR" if [ -d "$BUNDLE_ROOT/macos/CodeNomad.app" ]; then - ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-macos-arm64.zip" + ditto -ck --sequesterRsrc --keepParent "$BUNDLE_ROOT/macos/CodeNomad.app" "$ARTIFACT_DIR/CodeNomad-Tauri-macos-arm64-${VERSION}.zip" fi - name: Upload Actions artifacts (Tauri macOS arm64) @@ -563,7 +563,7 @@ jobs: New-Item -ItemType Directory -Path $artifactDir | Out-Null $exe = Get-ChildItem -Path $bundleRoot -Recurse -File -Filter *.exe | Select-Object -First 1 if ($null -ne $exe) { - $dest = Join-Path $artifactDir ("CodeNomad-Tauri-$env:VERSION-windows-x64.zip") + $dest = Join-Path $artifactDir ("CodeNomad-Tauri-windows-x64-$env:VERSION.zip") Compress-Archive -Path $exe.Directory.FullName -DestinationPath $dest -Force } @@ -674,8 +674,8 @@ jobs: exit 1 fi - cp "$appimage" "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.AppImage" - zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.zip" "$fallback_bin" + cp "$appimage" "$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION}.AppImage" + zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION}.zip" "$fallback_bin" - name: Upload Actions artifacts (Tauri Linux) if: ${{ inputs.upload_actions_artifacts }} @@ -777,9 +777,9 @@ jobs: first_artifact=$(find "$SEARCH_ROOT" -type f \( -name "*.AppImage" -o -name "*.tar.gz" \) | head -n1) fallback_bin="$SEARCH_ROOT/release/codenomad-tauri" if [ -n "$first_artifact" ]; then - zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.zip" "$first_artifact" + zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-linux-arm64-${VERSION}.zip" "$first_artifact" elif [ -f "$fallback_bin" ]; then - zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-${VERSION}-linux-x64.zip" "$fallback_bin" + zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-linux-arm64-${VERSION}.zip" "$fallback_bin" else echo "No bundled artifact found under $SEARCH_ROOT and no binary at $fallback_bin" >&2 exit 1 diff --git a/packages/electron-app/package.json b/packages/electron-app/package.json index 4311124f..79b96a53 100644 --- a/packages/electron-app/package.json +++ b/packages/electron-app/package.json @@ -103,7 +103,7 @@ ] } ], - "artifactName": "CodeNomad-${version}-${os}-${arch}.${ext}", + "artifactName": "CodeNomad-Electron-macos-${arch}-${version}.${ext}", "icon": "electron/resources/icon.icns" }, "dmg": { @@ -130,7 +130,7 @@ ] } ], - "artifactName": "CodeNomad-${version}-${os}-${arch}.${ext}", + "artifactName": "CodeNomad-Electron-windows-${arch}-${version}.${ext}", "icon": "electron/resources/icon.ico" }, "nsis": { @@ -156,7 +156,7 @@ ] } ], - "artifactName": "CodeNomad-${version}-${os}-${arch}.${ext}", + "artifactName": "CodeNomad-Electron-linux-${arch}-${version}.${ext}", "category": "Development", "icon": "electron/resources/icon.png" } From 0168703d4fcc4e971139eb25b73ce6e46fdf1d64 Mon Sep 17 00:00:00 2001 From: Shantur Rathore Date: Sun, 3 May 2026 22:00:05 +0100 Subject: [PATCH 03/39] fix(ui): make idle session badges transient Keep idle status visible only briefly after active work finishes so the session list and headers emphasize meaningful state changes instead of permanent idle noise. --- packages/ui/src/components/instance-tab.tsx | 41 ++++++++++------ .../components/instance/instance-shell2.tsx | 6 ++- packages/ui/src/components/session-list.tsx | 21 +++++---- packages/ui/src/stores/session-api.ts | 17 ++++--- packages/ui/src/stores/session-events.ts | 9 ++++ packages/ui/src/stores/session-state.ts | 3 +- packages/ui/src/stores/session-status.test.ts | 27 +++++++++++ packages/ui/src/stores/session-status.ts | 47 ++++++++++++++++++- packages/ui/src/types/session.ts | 19 ++++++++ 9 files changed, 156 insertions(+), 34 deletions(-) diff --git a/packages/ui/src/components/instance-tab.tsx b/packages/ui/src/components/instance-tab.tsx index f345f637..a2c80299 100644 --- a/packages/ui/src/components/instance-tab.tsx +++ b/packages/ui/src/components/instance-tab.tsx @@ -1,4 +1,4 @@ -import { Component, createMemo } from "solid-js" +import { Component, Show, createEffect, createMemo, createSignal, onCleanup } from "solid-js" import type { Instance } from "../types/instance" import { getInstanceSessionIndicatorStatus } from "../stores/session-status" import { FolderOpen, ShieldAlert, X } from "lucide-solid" @@ -20,9 +20,18 @@ function getPathBasename(path: string): string { const InstanceTab: Component = (props) => { const { t } = useI18n() - const aggregatedStatus = createMemo(() => getInstanceSessionIndicatorStatus(props.instance.id)) + const [now, setNow] = createSignal(Date.now()) + + createEffect(() => { + if (typeof window === "undefined") return + const timer = window.setInterval(() => setNow(Date.now()), 1000) + onCleanup(() => window.clearInterval(timer)) + }) + + const aggregatedStatus = createMemo(() => getInstanceSessionIndicatorStatus(props.instance.id, now())) const statusClassName = createMemo(() => { const status = aggregatedStatus() + if (!status) return null return status === "permission" ? "session-permission" : `session-${status}` }) const statusTitle = createMemo(() => { @@ -33,8 +42,10 @@ const InstanceTab: Component = (props) => { return t("instanceTab.status.compacting") case "working": return t("instanceTab.status.working") - default: + case "idle": return t("instanceTab.status.idle") + default: + return null } }) @@ -51,17 +62,19 @@ const InstanceTab: Component = (props) => { {getPathBasename(props.instance.folder)} - - {aggregatedStatus() === "permission" ? ( -