Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
49 commits
Select commit Hold shift + click to select a range
0531a64
refactor: Extract platform-independent scanner into CodexTpsFloat.Core
Craun718 Oct 15, 2026
5a6bcf2
fix: Detect rewritten logs without relying on Windows file creation time
Craun718 Oct 15, 2026
96adc08
refactor: Move settings persistence and report export into the core l…
Craun718 Oct 15, 2026
7a3a71d
refactor: Move the headless snapshot command into the core library
Craun718 Oct 15, 2026
5b571ec
feat: Add cross-platform autostart and platform path services
Craun718 Oct 15, 2026
4c1eb4d
feat: Add cross-platform Avalonia application shell
Craun718 Oct 15, 2026
c66a7be
build: Make the Avalonia app the release entry point
Craun718 Oct 15, 2026
480a093
build: Publish the Avalonia app through cross-platform build scripts
Craun718 Oct 15, 2026
9e77169
ci: Build tagged releases for Windows, macOS, and Linux
Craun718 Oct 15, 2026
cff32fb
fix: Cover cross-platform single-instance wakeups
Craun718 Oct 15, 2026
ca9332a
docs: Document cross-platform installation and builds
Craun718 Oct 15, 2026
612397c
style: Remove trailing blank line from Avalonia entry point
Craun718 Oct 15, 2026
bc09772
docs: Add cross-platform third-party notices
Craun718 Oct 15, 2026
07a974b
ci: Match release runners to target architectures
Craun718 Oct 15, 2026
f1fc837
fix: Keep the tray app hidden on startup
Craun718 Oct 15, 2026
ce51eb3
fix: Use process-safe single-instance locking on Unix
Craun718 Oct 15, 2026
4190f67
fix: Use the XDG autostart directory on Linux
Craun718 Oct 15, 2026
ce451e4
fix: Show the panel when no tray is available
Craun718 Oct 15, 2026
d689ae4
fix: Use platform-native config directories on macOS
Craun718 Oct 15, 2026
196f0f9
fix: Hide the macOS Dock icon for the tray app
Craun718 Oct 15, 2026
59ae60d
fix: Show the panel on macOS startup
Craun718 Oct 15, 2026
1d3a198
feat: Make the whole macOS panel draggable
Craun718 Oct 15, 2026
4f717d6
fix: Replace toolbar text symbols with vector icons
Craun718 Oct 15, 2026
0bb1a78
Replace panel symbols with vector icons
Craun718 Oct 15, 2026
a009f2a
fix: Upgrade Tmds.DBus.Protocol to patched version
Craun718 Oct 15, 2026
15a2f77
fix: Render a transparent macOS tray icon
Craun718 Oct 15, 2026
8ed1cb8
ci: Add cross-platform build workflow
Craun718 Oct 15, 2026
e0d3deb
fix: Restore refresh cadence selection and refine window segments
Craun718 Oct 15, 2026
64a606e
fix: Exclude Avalonia sources from the legacy WPF project
Craun718 Oct 15, 2026
d2830e1
fix: Start the Avalonia UI on an STA thread
Craun718 Oct 15, 2026
fcf2071
fix: Preserve the legacy Windows wake event name
Craun718 Oct 15, 2026
bbd40ca
fix: Keep capture sessions from persisting settings
Craun718 Oct 15, 2026
8c3fac2
fix: Preserve saved coordinates when exiting an unopened panel
Craun718 Oct 15, 2026
a84e6db
fix: Handle snapshot and single-instance startup errors
Craun718 Oct 15, 2026
c024272
fix: Keep the Linux panel accessible without a tray host
Craun718 Oct 15, 2026
702fe64
fix: Refresh the selected directory after in-flight scans
Craun718 Oct 15, 2026
5e2ba79
ci: Label tray lifecycle checks as legacy WPF coverage
Craun718 Oct 15, 2026
9de23d5
fix: Escape Linux desktop autostart executable paths
Craun718 Oct 15, 2026
7f1740f
fix: Propagate Unix lock failures unrelated to contention
Craun718 Oct 15, 2026
714a409
fix: Exclude interactive event sources from panel dragging
Craun718 Oct 15, 2026
d00ea1a
fix: Wait for the panel to be ready before handling wake requests
Craun718 Oct 15, 2026
3837432
fix: Acknowledge wake requests and report failed handoffs
Craun718 Oct 15, 2026
2897448
fix: Drain Windows wake callbacks before closing acknowledgment handles
Craun718 Oct 15, 2026
f61435a
fix: allow equals in Linux autostart paths
Craun718 Oct 15, 2026
a733edb
fix: honor selected report export format
Craun718 Oct 15, 2026
b145ca5
fix: align autostart tests and escape report model
Craun718 Oct 15, 2026
c4441c2
ci: Upgrade artifact actions for Node.js 24
Craun718 Oct 15, 2026
54064e1
Fix export overwrite confirmation and log replacement detection
jinghero Oct 16, 2026
5856d9c
Check log prefixes when replacement timestamps collide
jinghero Oct 16, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 85 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
name: CI

on:
pull_request:
push:
branches:
- '**'

permissions:
contents: read

concurrency:
group: ci-${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 20
steps:
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up .NET 10
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: '10.0.x'

- name: Run core regression tests
run: dotnet run --project tests/CoreTests.csproj -c Release --nologo

- name: Run Avalonia regression tests
run: dotnet run --project tests/AppTests.csproj -c Release --nologo

build:
needs: test
strategy:
fail-fast: false
matrix:
include:
- runner: windows-latest
runtime: win-x64
shell: pwsh
- runner: macos-15
runtime: osx-arm64
shell: bash
- runner: macos-15-intel
runtime: osx-x64
shell: bash
- runner: ubuntu-latest
runtime: linux-x64
shell: bash
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- name: Check out source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up .NET 10
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: '10.0.x'

- name: Run legacy WPF hidden tray lifecycle tests (Windows only)
if: runner.os == 'Windows'
run: dotnet run --project tests/TrayLifecycleTests.csproj -c Release --nologo -- --never-show
Comment thread
sourcery-ai[bot] marked this conversation as resolved.
Resolved

- name: Build portable application
if: runner.os == 'Windows'
run: ./build.ps1 -Runtime ${{ matrix.runtime }} -Portable

- name: Build portable application
if: runner.os != 'Windows'
run: PORTABLE=1 ./build.sh ${{ matrix.runtime }}

- name: Upload build artifact
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: CodexTpsFloat-ci-${{ matrix.runtime }}-${{ github.sha }}
path: app
if-no-files-found: error
retention-days: 14
130 changes: 112 additions & 18 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
name: Release portable Windows app
name: Release cross-platform app

on:
push:
Expand All @@ -13,12 +13,9 @@ concurrency:
cancel-in-progress: false

jobs:
release:
runs-on: windows-latest
validate:
runs-on: ubuntu-latest
timeout-minutes: 20
defaults:
run:
shell: pwsh
steps:
- name: Check out tagged source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
Expand All @@ -30,38 +27,97 @@ jobs:

- name: Check tag matches application version
run: |
[xml]$project = Get-Content src/CodexTpsFloat.csproj -Raw
$version = $project.Project.PropertyGroup.Version
if ($env:GITHUB_REF_NAME -cne "v$version") {
throw "Tag $env:GITHUB_REF_NAME does not match application version v$version."
}
version="$(dotnet msbuild src/App/CodexTpsFloat.App.csproj -nologo -getProperty:Version)"
test "$GITHUB_REF_NAME" = "v$version"

- name: Run core regression tests
run: dotnet run --project tests/CoreTests.csproj -c Release --nologo

- name: Run hidden tray lifecycle tests
- name: Run Avalonia regression tests
run: dotnet run --project tests/AppTests.csproj -c Release --nologo

release:
needs: validate
strategy:
fail-fast: false
matrix:
include:
- runner: windows-latest
runtime: win-x64
shell: pwsh
- runner: macos-15
runtime: osx-arm64
shell: bash
- runner: macos-15-intel
runtime: osx-x64
shell: bash
- runner: ubuntu-latest
runtime: linux-x64
shell: bash
runs-on: ${{ matrix.runner }}
timeout-minutes: 30
defaults:
run:
shell: ${{ matrix.shell }}
steps:
- name: Check out tagged source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1

- name: Set up .NET 10
uses: actions/setup-dotnet@a98b56852c35b8e3190ac28c8c2271da59106c68 # v6.0.0
with:
dotnet-version: '10.0.x'

- name: Run core regression tests
run: dotnet run --project tests/CoreTests.csproj -c Release --nologo

- name: Run legacy WPF hidden tray lifecycle tests (Windows only)
if: runner.os == 'Windows'
run: dotnet run --project tests/TrayLifecycleTests.csproj -c Release --nologo -- --never-show

- name: Build portable application
run: ./build.ps1 -Portable
if: runner.os == 'Windows'
run: ./build.ps1 -Runtime ${{ matrix.runtime }} -Portable

- name: Build portable application
if: runner.os != 'Windows'
run: PORTABLE=1 ./build.sh ${{ matrix.runtime }}

- name: Package portable ZIP and checksum
- name: Package portable archive and checksum
id: package
if: runner.os == 'Windows'
run: |
$assetDirectory = Join-Path $env:RUNNER_TEMP 'release-assets'
$stagingDirectory = Join-Path $env:RUNNER_TEMP 'portable-package'
New-Item -ItemType Directory -Path $assetDirectory, $stagingDirectory | Out-Null
Copy-Item -LiteralPath 'app/CodexTpsFloat.exe', 'README.md', 'PRIVACY.md', 'THIRD-PARTY-NOTICES.md' -Destination $stagingDirectory
Copy-Item -LiteralPath 'licenses' -Destination $stagingDirectory -Recurse
$archiveName = "CodexTpsFloat-$env:GITHUB_REF_NAME-win-x64-portable.zip"
$archiveName = "CodexTpsFloat-$env:GITHUB_REF_NAME-${{ matrix.runtime }}-portable.zip"
$archivePath = Join-Path $assetDirectory $archiveName
Compress-Archive -Path "$stagingDirectory/*" -DestinationPath $archivePath -CompressionLevel Optimal
$hash = (Get-FileHash -LiteralPath $archivePath -Algorithm SHA256).Hash.ToLowerInvariant()
"$hash $archiveName" | Set-Content -LiteralPath "$archivePath.sha256" -Encoding ascii
"directory=$assetDirectory" >> $env:GITHUB_OUTPUT
"archive=$archivePath" >> $env:GITHUB_OUTPUT

- name: Package portable archive and checksum
id: package_unix
if: runner.os != 'Windows'
run: |
asset_directory="$RUNNER_TEMP/release-assets"
staging_directory="$RUNNER_TEMP/portable-package"
mkdir -p "$asset_directory" "$staging_directory"
cp app/CodexTpsFloat README.md PRIVACY.md THIRD-PARTY-NOTICES.md "$staging_directory/"
cp -R licenses "$staging_directory/"
archive_name="CodexTpsFloat-$GITHUB_REF_NAME-${{ matrix.runtime }}-portable.tar.gz"
archive_path="$asset_directory/$archive_name"
tar -czf "$archive_path" -C "$staging_directory" .
(cd "$asset_directory" && shasum -a 256 "$archive_name" > "$archive_name.sha256")
echo "directory=$asset_directory" >> "$GITHUB_OUTPUT"
echo "archive=$archive_path" >> "$GITHUB_OUTPUT"

- name: Smoke test extracted portable application
if: runner.os == 'Windows'
env:
PORTABLE_ARCHIVE: ${{ steps.package.outputs.archive }}
run: |
Expand All @@ -88,11 +144,49 @@ jobs:
throw 'Portable snapshot incorrectly reports an aborted session as unsupported.'
}

- name: Smoke test extracted portable application
if: runner.os != 'Windows'
env:
PORTABLE_ARCHIVE: ${{ steps.package_unix.outputs.archive }}
run: |
smoke_directory="$RUNNER_TEMP/portable-smoke"
fixture_directory="$RUNNER_TEMP/fixture-codex-home"
session_directory="$fixture_directory/sessions"
mkdir -p "$smoke_directory" "$session_directory"
tar -xzf "$PORTABLE_ARCHIVE" -C "$smoke_directory"
cat > "$session_directory/aborted.jsonl" <<'EOF'
{"type":"event_msg","timestamp":"2026-10-14T00:00:00Z","payload":{"type":"task_started","turn_id":"smoke-turn"}}
{"type":"event_msg","timestamp":"2026-10-14T00:00:01Z","payload":{"type":"token_count","info":null}}
{"type":"event_msg","timestamp":"2026-10-14T00:00:02Z","payload":{"type":"turn_aborted","turn_id":"smoke-turn"}}
EOF
snapshot_path="$RUNNER_TEMP/portable-snapshot.json"
"$smoke_directory/CodexTpsFloat" --codex-home "$fixture_directory" --snapshot-json "$snapshot_path"
python3 - "$snapshot_path" <<'PY'
import json, sys
snapshot = json.load(open(sys.argv[1]))
if (snapshot["Files"], snapshot["InvalidLines"], snapshot["UnsupportedFiles"], snapshot["ReadFailures"]) != (1, 0, 0, 0):
raise SystemExit("Portable snapshot incorrectly reports an aborted session as unsupported.")
PY

- name: Upload release artifact
uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9 # v7.0.2
with:
name: CodexTpsFloat-${{ github.ref_name }}-${{ matrix.runtime }}
path: ${{ runner.os == 'Windows' && steps.package.outputs.directory || steps.package_unix.outputs.directory }}

publish:
needs: release
runs-on: ubuntu-latest
steps:
- name: Download release artifacts
uses: actions/download-artifact@9000827ccba6bdab643e8b6fd33ac0654aef8333 # v8.0.2
with:
path: release-assets
merge-multiple: true

- name: Publish GitHub Release
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_REPO: ${{ github.repository }}
RELEASE_ASSET_DIRECTORY: ${{ steps.package.outputs.directory }}
run: |
$assets = @(Get-ChildItem -LiteralPath $env:RELEASE_ASSET_DIRECTORY -File | Select-Object -ExpandProperty FullName)
gh release create $env:GITHUB_REF_NAME @assets --verify-tag --title "Codex TPS $env:GITHUB_REF_NAME" --notes-from-tag --latest
gh release create "$GITHUB_REF_NAME" release-assets/* --verify-tag --title "Codex TPS $GITHUB_REF_NAME" --notes-from-tag --latest
5 changes: 5 additions & 0 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<Project>
<PropertyGroup>
<Version>1.1.3</Version>
</PropertyGroup>
</Project>
2 changes: 1 addition & 1 deletion PRIVACY.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

应用在本机只读 Codex 日志,不调用网络服务,也不上传提示词、回答正文或凭据。日志解析只保留统计所需的结构字段和用量计数。运行过程会短暂读取日志文本,但不会将正文保存在报告或本工具的持久化数据中。

本机 `app/data/settings.json` 包含日志目录、窗口位置和界面偏好,可能含用户名或目录信息。程序目录中的真实预览图和实机验收记录可能包含使用时间、用量、文件数量等信息。
本机设置文件包含日志目录、窗口位置和界面偏好,可能含用户名或目录信息。Windows 便携版的设置位于程序旁的 `data/settings.json`;macOS 和 Linux 使用用户的应用程序数据目录下的 `CodexTpsFloat/settings.json`。源码目录中的真实预览图和实机验收记录可能包含使用时间、用量、文件数量等信息。

这些内容已由 `.gitignore` 排除:运行程序与本机配置、构建缓存、真实预览图、实机验收记录、日志、数据库及导出报告。仓库只保存源码、依赖锁文件、构建脚本、通用测试和文档。

Expand Down
Loading
Loading