Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
3141452
Min server 0.18.0
shantur Jun 21, 2026
e8623d3
fix(ui): SSE connection status indicators for mobile network drops (#…
JDis03 Jun 27, 2026
275770b
fix(ui): remove scope=project from session list requests (#572)
JDis03 Jul 8, 2026
bce975d
fix(ui): stabilize strict follow autoscroll (#576)
pascalandr Jul 8, 2026
ca06bd9
feat(yolo): move permission auto-accept (Yolo) to the server (#561)
heunghingwan Jul 8, 2026
3470622
feat(ui): implement recursive nested session expansion (#478)
detain Jul 11, 2026
56d1e00
PR: Extend delete overlays and clean companion parts during tool dele…
VooDisss Jul 12, 2026
c8d4a50
build: publish deb and portable tar.gz Linux artifacts (#492)
shantur Jul 12, 2026
f34e807
feat: detect realpath when open workspace (#486)
bluelovers Jul 12, 2026
483fac2
feat(speech): separate STT/TTS provider configuration (#579)
heunghingwan Jul 13, 2026
f900af1
fix(ui): cancel pending microphone acquisition on early release (#550…
heunghingwan Jul 13, 2026
07e8ce6
feat(ui): support bracket math delimiters (#588)
jollyxenon Jul 14, 2026
b1d6421
feat(ui): focus messages after prompt submission (#577)
pascalandr Jul 15, 2026
1efe5e9
feat(desktop): persist and restore UI state across restarts (#578)
pascalandr Jul 15, 2026
f31d8c1
fix(ui): persist environment variable removal (#581)
pascalandr Jul 15, 2026
e815eba
fix(ui): refresh worktrees from OpenCode ready events (#583)
pascalandr Jul 15, 2026
709557d
feat(usage): show provider quota in session status (#584)
pascalandr Jul 15, 2026
40cee07
feat(ui): add line wrap toggle to markdown code blocks (#599)
shantur Jul 15, 2026
f6cf6c5
fix(ui): surface session loading failures (#582)
pascalandr Jul 17, 2026
e169c0a
feat(settings): add OpenCode update action (#585)
pascalandr Jul 17, 2026
18564d4
fix(ui): temporarily raise project session list ceiling (#604)
pascalandr Jul 17, 2026
7584ccf
feat(ui): make open workspace action explicit (#600)
pascalandr Jul 17, 2026
e586ea3
feat(windows): update CodeNomad from the in-app notification (#597)
pascalandr Jul 17, 2026
0bab9e3
fix(restore): simplify persistence and harden cross-platform cleanup …
pascalandr Jul 17, 2026
b766b76
fix(tauri): add native About and Help menus (#596)
pascalandr Jul 17, 2026
8f6aa44
perf(ui): virtualize nested session list (#586)
pascalandr Jul 17, 2026
47ae662
fix(ui): polish provider usage status panel ( and Yolo panel ) (#610)
pascalandr Jul 19, 2026
24a2680
feat(restore): persist Yolo and session expansion state (#611)
pascalandr Jul 19, 2026
4c50829
fix(ui): defer session virtualization for closed left panel (#612)
pascalandr Jul 19, 2026
d74a8a3
fix(ui): label new local instance action (#613)
pascalandr Jul 20, 2026
25af76c
feat(settings): reorganize settings and add chat block visibility (#614)
pascalandr Jul 26, 2026
30563a2
feat(ui): add right panel customization (#615)
pascalandr Jul 26, 2026
b81044f
fix(auth): submit the displayed default username (#620)
pascalandr Jul 26, 2026
b1bb8a7
fix(ui): avoid mobile selection action overlap (#622)
pascalandr Jul 26, 2026
9d47f73
fix(files): search large workspaces beyond candidate cap (#623)
pascalandr Jul 26, 2026
70c9548
fix(permissions): ignore stale permission updates (#621)
pascalandr Jul 26, 2026
269cff6
fix(server): prevent Linux workspace launch identity timeouts (#625)
pascalandr Jul 26, 2026
1d4f778
refactor(server): route background-process prompt through the instanc…
heunghingwan Jul 30, 2026
946bd8e
feat(ui): load right panel plugin manifests (#617)
pascalandr Aug 3, 2026
598353d
fix(ui): keep relevant file path segments visible (#628)
pascalandr Aug 3, 2026
a628fa8
fix(server): retry Windows reserved auto ports (#632)
pascalandr Aug 3, 2026
e7a27c9
fix(tauri): restore macOS quit shortcut (#633)
pascalandr Aug 3, 2026
afb09d9
fix(ui): preserve failed prompts in input history (#634)
pascalandr Aug 3, 2026
c16cc00
fix(workspaces): surface invalid OpenCode configuration (#635)
pascalandr Aug 3, 2026
67cb394
fix(ui): resolve stuck/duplicated messages after mobile background (#…
JDis03 Aug 7, 2026
7f16057
fix(ui): stop active-session load effect from re-firing on every sess…
JDis03 Aug 12, 2026
31390cb
fix(ui): preserve assistant output after raw HTML (#646)
pascalandr Aug 13, 2026
6d10050
fix(tauri): restore notifications in remote windows (#641)
pascalandr Aug 13, 2026
b8e7b16
feat(desktop): open workspaces in native apps (#638)
pascalandr Aug 13, 2026
3bf3f58
feat(ui): manage visible provider models (#637)
pascalandr Aug 13, 2026
ac15331
Bump version to 0.19.0
shantur Aug 13, 2026
9185b38
fix(ui): contain provider card actions (#651)
pascalandr Aug 13, 2026
719dccf
docs: fix broken star history chart (#654)
OctoBored Aug 17, 2026
074aaa9
fix(ui): stop reasoning output scroll jitter (#653)
pascalandr Aug 24, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
232 changes: 205 additions & 27 deletions .github/workflows/build-and-upload.yml
Original file line number Diff line number Diff line change
Expand Up @@ -317,25 +317,106 @@ jobs:
- name: Verify bundled Node resource (Electron Linux)
run: node scripts/verify-bundled-node.cjs packages/electron-app/electron/resources@linux-x64

- name: Verify Electron portable archive
run: |
set -euo pipefail
shopt -s nullglob
archives=(packages/electron-app/release/*.tar.gz)
if [ "${#archives[@]}" -ne 1 ]; then
echo "Expected one Electron Linux tar.gz, found ${#archives[@]}" >&2
exit 1
fi

extract_dir=$(mktemp -d)
trap 'rm -rf "$extract_dir"' EXIT
tar -xzf "${archives[0]}" -C "$extract_dir"

binary=$(find "$extract_dir" -type f -name CodeNomad -print -quit)
if [ -z "$binary" ] || [ ! -x "$binary" ]; then
echo "Electron portable archive does not contain an executable CodeNomad binary" >&2
exit 1
fi

resources_dir="$(dirname "$binary")/resources"
node scripts/smoke-packaged-resources.cjs \
--resources "$resources_dir" \
--target linux-x64

app_asar="$resources_dir/app.asar"
if [ ! -f "$app_asar" ]; then
echo "Electron portable archive does not contain resources/app.asar" >&2
exit 1
fi
node - "$app_asar" <<'NODE'
const asar = require("@electron/asar")
const path = require("node:path")
const archive = process.argv[2]
const files = asar.listPackage(archive).map((file) =>
file.replace(/^[/\\]/, "").replaceAll("\\", "/"),
)
const fileSet = new Set(files)
const loadingPath = "dist/renderer/loading.html"
if (!fileSet.has(loadingPath)) {
throw new Error("Electron app.asar is missing dist/renderer/loading.html")
}
if (!files.some((file) => file.startsWith("dist/renderer/assets/") && !file.endsWith("/"))) {
throw new Error("Electron app.asar does not contain renderer assets")
}
const html = asar.extractFile(archive, loadingPath).toString("utf8")
for (const match of html.matchAll(/\b(?:src|href)=["']([^"']+)["']/g)) {
const reference = match[1]
if (/^(?:[a-z]+:|#)/i.test(reference)) continue
const cleanReference = reference.split(/[?#]/, 1)[0]
const target = cleanReference.startsWith("/")
? cleanReference.slice(1)
: path.posix.normalize(path.posix.join(path.posix.dirname(loadingPath), cleanReference))
if (!fileSet.has(target)) {
throw new Error(`Electron app.asar is missing renderer reference ${target}`)
}
}
NODE

ldd "$binary" | tee /tmp/codenomad-electron-ldd.txt
if grep -q "not found" /tmp/codenomad-electron-ldd.txt; then
echo "Electron portable binary has unresolved shared libraries" >&2
exit 1
fi

- name: Upload release assets
if: ${{ inputs.upload && inputs.tag != '' }}
run: |
set -euo pipefail
shopt -s nullglob
for file in packages/electron-app/release/*.zip packages/electron-app/release/*.AppImage; do
[ -f "$file" ] || continue
echo "Uploading $file"
gh release upload "$TAG" "$file" --clobber
files=(packages/electron-app/release/*.tar.gz)
if [ "${#files[@]}" -ne 1 ]; then
echo "Expected one Electron Linux release asset, found ${#files[@]}" >&2
exit 1
fi

file="${files[0]}"
asset_name=$(basename "$file")
echo "Uploading $file"
gh release upload "$TAG" "$file" --clobber
if ! gh release view "$TAG" --json assets --jq '.assets[].name' | grep -Fxq -- "$asset_name"; then
echo "Uploaded Electron Linux asset is missing from release: $asset_name" >&2
exit 1
fi

gh release view "$TAG" --json assets --jq '.assets[].name' | while IFS= read -r asset; do
case "$asset" in
CodeNomad-Electron-linux-*.zip|CodeNomad-Electron-linux-*.AppImage|CodeNomad-Electron-linux-*.flatpak)
echo "Deleting obsolete Linux asset $asset"
gh release delete-asset "$TAG" "$asset" --yes
;;
esac
done

- name: Upload Actions artifacts (Electron Linux)
if: ${{ inputs.upload_actions_artifacts }}
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.actions_artifacts_name_prefix }}electron-linux
path: |
packages/electron-app/release/*.zip
packages/electron-app/release/*.AppImage
path: packages/electron-app/release/*.tar.gz
retention-days: ${{ inputs.actions_artifacts_retention_days }}
if-no-files-found: error

Expand Down Expand Up @@ -364,6 +445,9 @@ jobs:
if: ${{ inputs.set_versions && inputs.version != '' }}
run: npm version ${VERSION} --workspaces --include-workspace-root --no-git-tag-version --allow-same-version

- name: Sync Tauri package version
run: npm run sync:version --workspace @codenomad/tauri-app

- name: Install dependencies
run: npm ci --workspaces --include=optional

Expand Down Expand Up @@ -451,6 +535,9 @@ jobs:
if: ${{ inputs.set_versions && inputs.version != '' }}
run: npm version ${VERSION} --workspaces --include-workspace-root --no-git-tag-version --allow-same-version

- name: Sync Tauri package version
run: npm run sync:version --workspace @codenomad/tauri-app

- name: Install dependencies
run: npm ci --workspaces --include=optional

Expand Down Expand Up @@ -539,6 +626,9 @@ jobs:
run: npm version ${{ env.VERSION }} --workspaces --include-workspace-root --no-git-tag-version --allow-same-version
shell: bash

- name: Sync Tauri package version
run: npm run sync:version --workspace @codenomad/tauri-app

- name: Install dependencies
run: npm ci --workspaces --include=optional

Expand Down Expand Up @@ -643,6 +733,9 @@ jobs:
if: ${{ inputs.set_versions && inputs.version != '' }}
run: npm version ${VERSION} --workspaces --include-workspace-root --no-git-tag-version --allow-same-version

- name: Sync Tauri package version
run: npm run sync:version --workspace @codenomad/tauri-app

- name: Install dependencies
run: npm ci --workspaces --include=optional

Expand All @@ -669,53 +762,135 @@ jobs:
echo "Tauri CLI failed to load after retries" >&2
exit 1

- name: Build Linux bundle (Tauri)
- name: Build Debian package (Tauri)
working-directory: packages/tauri-app
run: npm exec -- tauri build
run: npm exec -- tauri build --bundles deb

- name: Package Tauri artifacts (Linux)
- name: Package and verify Tauri deb
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
run: |
set -euo pipefail
SEARCH_ROOT="packages/tauri-app/target"
BUNDLE_ROOT="packages/tauri-app/target/release/bundle/deb"
ARTIFACT_DIR="packages/tauri-app/release-tauri"
VERSION_TO_USE="${VERSION:-}"
if [ -z "$VERSION_TO_USE" ]; then
VERSION_TO_USE=$(node -p "require('./packages/tauri-app/package.json').version")
fi
rm -rf "$ARTIFACT_DIR"
mkdir -p "$ARTIFACT_DIR"
shopt -s nullglob globstar
shopt -s nullglob

find_one() {
find "$SEARCH_ROOT" -type f -iname "$1" | head -n1
}
debs=("$BUNDLE_ROOT"/*.deb)
if [ "${#debs[@]}" -ne 1 ]; then
echo "Expected one Tauri deb under $BUNDLE_ROOT, found ${#debs[@]}" >&2
exit 1
fi

appimage=$(find_one "*.AppImage")
fallback_bin="$SEARCH_ROOT/release/codenomad-tauri"
artifact="$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION_TO_USE}.deb"
cp "${debs[0]}" "$artifact"
dpkg-deb --info "$artifact"
dpkg-deb --contents "$artifact"
package_version=$(dpkg-deb --field "$artifact" Version)
if [ "$package_version" != "$VERSION_TO_USE" ]; then
echo "Tauri deb version mismatch: expected $VERSION_TO_USE, found $package_version" >&2
exit 1
fi

extract_dir=$(mktemp -d)
trap 'rm -rf "$extract_dir"' EXIT
dpkg-deb --extract "$artifact" "$extract_dir"

if [ -z "$appimage" ] || [ ! -f "$fallback_bin" ]; then
echo "Missing bundle(s): appimage=${appimage:-none} binary=$fallback_bin" >&2
binary="$extract_dir/usr/bin/codenomad-tauri"
resources_dir="$extract_dir/usr/lib/CodeNomad/resources"
desktop_file="$extract_dir/usr/share/applications/CodeNomad.desktop"
for required in "$binary" "$resources_dir" "$desktop_file"; do
if [ ! -e "$required" ]; then
echo "Missing path in Tauri deb: $required" >&2
exit 1
fi
done

grep -Fx "Exec=codenomad-tauri" "$desktop_file"
if grep -Fxq "NoDisplay=true" "$desktop_file"; then
echo "Generated Tauri desktop entry must be visible" >&2
exit 1
fi
node scripts/smoke-packaged-resources.cjs \
--resources "$resources_dir" \
--loading "$resources_dir/ui-loading" \
--target linux-x64

ldd "$binary" | tee /tmp/codenomad-tauri-ldd.txt
if grep -q "not found" /tmp/codenomad-tauri-ldd.txt; then
echo "Tauri deb binary has unresolved shared libraries" >&2
exit 1
fi

cp "$appimage" "$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION}.AppImage"
zip -j "$ARTIFACT_DIR/CodeNomad-Tauri-linux-x64-${VERSION}.zip" "$fallback_bin"
- name: Verify Tauri deb installation on Ubuntu 24.04
if: ${{ inputs.upload || inputs.upload_actions_artifacts }}
run: |
set -euo pipefail
shopt -s nullglob
debs=(packages/tauri-app/release-tauri/*.deb)
if [ "${#debs[@]}" -ne 1 ]; then
echo "Expected one packaged Tauri deb, found ${#debs[@]}" >&2
exit 1
fi

artifact=$(realpath "${debs[0]}")
docker run --rm \
--volume "$artifact:/tmp/codenomad.deb:ro" \
ubuntu:24.04 \
bash -euc '
export DEBIAN_FRONTEND=noninteractive
apt-get update >/dev/null
apt-get install -y /tmp/codenomad.deb >/dev/null
test -x /usr/bin/codenomad-tauri
test -x /usr/lib/CodeNomad/resources/node/linux-x64/bin/node
test -f /usr/lib/CodeNomad/resources/server/dist/bin.js
test -f /usr/lib/CodeNomad/resources/ui-loading/loading.html
/usr/lib/CodeNomad/resources/node/linux-x64/bin/node \
/usr/lib/CodeNomad/resources/server/dist/bin.js --version
ldd /usr/bin/codenomad-tauri | tee /tmp/codenomad-tauri-ldd.txt
! grep -q "not found" /tmp/codenomad-tauri-ldd.txt
'

- name: Upload Actions artifacts (Tauri Linux)
if: ${{ inputs.upload_actions_artifacts }}
uses: actions/upload-artifact@v4
with:
name: ${{ inputs.actions_artifacts_name_prefix }}tauri-linux
path: packages/tauri-app/release-tauri/*
path: packages/tauri-app/release-tauri/*.deb
retention-days: ${{ inputs.actions_artifacts_retention_days }}
if-no-files-found: warn
if-no-files-found: error

- name: Upload Tauri release assets (Linux)
if: ${{ inputs.upload && inputs.tag != '' }}
run: |
set -euo pipefail
shopt -s nullglob
for file in packages/tauri-app/release-tauri/*; do
[ -f "$file" ] || continue
echo "Uploading $file"
gh release upload "$TAG" "$file" --clobber
files=(packages/tauri-app/release-tauri/*.deb)
if [ "${#files[@]}" -ne 1 ]; then
echo "Expected one Tauri Linux release asset, found ${#files[@]}" >&2
exit 1
fi

file="${files[0]}"
asset_name=$(basename "$file")
echo "Uploading $file"
gh release upload "$TAG" "$file" --clobber
if ! gh release view "$TAG" --json assets --jq '.assets[].name' | grep -Fxq -- "$asset_name"; then
echo "Uploaded Tauri Linux asset is missing from release: $asset_name" >&2
exit 1
fi

gh release view "$TAG" --json assets --jq '.assets[].name' | while IFS= read -r asset; do
case "$asset" in
CodeNomad-Tauri-linux-*.zip|CodeNomad-Tauri-linux-*.AppImage|CodeNomad-Tauri-linux-*.rpm|CodeNomad-Tauri-linux-*.flatpak)
echo "Deleting obsolete Linux asset $asset"
gh release delete-asset "$TAG" "$asset" --yes
;;
esac
done

build-tauri-linux-arm64:
Expand Down Expand Up @@ -772,6 +947,9 @@ jobs:
- name: Set workspace versions
run: npm version ${VERSION} --workspaces --include-workspace-root --no-git-tag-version --allow-same-version

- name: Sync Tauri package version
run: npm run sync:version --workspace @codenomad/tauri-app

- name: Install dependencies
run: npm ci --workspaces --include=optional

Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/comment-pr-artifacts.yml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ jobs:
return;
}

if (matchedRun.conclusion !== 'success') {
core.setFailed(`PR Build Validation run ${matchedRun.id} concluded ${matchedRun.conclusion}.`);
return;
}

const artifacts = await github.paginate(
github.rest.actions.listWorkflowRunArtifacts,
{ owner, repo, run_id: matchedRun.id, per_page: 100 }
Expand Down
Loading
Loading