diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ee35299..963e12d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,6 +12,9 @@ concurrency: jobs: test: strategy: + # One platform's failure must not cancel the other's: the Windows leg is where the + # installer payload check runs, and it is worth seeing even when macOS is red. + fail-fast: false matrix: os: [xcode-27, windows-latest] runs-on: ${{ matrix.os }} @@ -22,11 +25,61 @@ jobs: version: 11.19.0 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: pnpm - run: pnpm install --frozen-lockfile - run: pnpm typecheck - run: pnpm test + # Tests spawn real Electron binaries, and a process killed by a signal reports no + # message of its own. macOS writes the reason to a crash report instead, so decode it + # here rather than leaving a future failure as the bare word "SIGABRT". + - name: Report macOS crash logs + if: failure() && runner.os == 'macOS' + run: | + sleep 10 + python3 - <<'PY' + import glob, json, os + paths = glob.glob(os.path.expanduser('~/Library/Logs/DiagnosticReports/*.ips')) + paths += glob.glob('/Library/Logs/DiagnosticReports/*.ips') + paths.sort(key=os.path.getmtime) + if not paths: + print('no crash reports found') + for path in paths[-3:]: + print('=' * 70) + print(path) + head, _, rest = open(path, errors='replace').read().partition('\n') + try: + meta, body = json.loads(head), json.loads(rest) + except ValueError: + print(head) + print(rest[:4000]) + continue + print('process ', meta.get('app_name'), meta.get('app_version'), meta.get('bug_type')) + print('exception ', body.get('exception')) + print('termination', body.get('termination')) + images = body.get('usedImages', []) + faulting = body.get('faultingThread') + for index, thread in enumerate(body.get('threads', [])): + if faulting is not None and index != faulting: + continue + print('faulting thread', index, thread.get('name') or thread.get('queue') or '') + for frame in thread.get('frames', [])[:30]: + where = frame.get('imageIndex', -1) + image = images[where] if 0 <= where < len(images) else {} + print(' ', image.get('name', '?'), hex(frame.get('imageOffset', 0)), frame.get('symbol', '')) + PY - run: pnpm build - name: Package (without publishing) - run: pnpm exec electron-builder --dir + if: runner.os != 'Windows' + run: node scripts/electron-builder.cjs --dir + # Build the real installers on Windows rather than just the unpacked app: an app payload + # the NSIS extractor cannot unpack is invisible until an installer exists to inspect, + # which is what scripts/verify-windows-installer.cjs checks. arm64 is packaged here on an + # x64 runner on purpose — the payload is inspected, never executed, so no ARM runner is + # needed to catch a filter the installer could not decode. + - name: Package Windows x64 installer (without publishing) + if: runner.os == 'Windows' + run: node scripts/electron-builder.cjs --win nsis --x64 --publish never + - name: Package Windows arm64 installer (without publishing) + if: runner.os == 'Windows' + run: node scripts/electron-builder.cjs --win nsis --arm64 --publish never diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 2c523c4..a1e4ed5 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -31,7 +31,7 @@ jobs: version: 11.19.0 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: pnpm - run: pnpm install --frozen-lockfile - name: Verify tag @@ -40,10 +40,10 @@ jobs: - run: pnpm typecheck - run: pnpm test - run: pnpm build - # Native modules are rebuilt in-place. Building both architectures in one - # workspace can mutate a hard-linked module after the first app was signed. + # Each arch is signed in its own workspace, so one build can never touch an + # app another job already signed. - name: Package macOS (${{ matrix.electron_arch }}) - run: pnpm exec electron-builder --mac dmg --${{ matrix.electron_arch }} --publish never + run: node scripts/electron-builder.cjs --mac dmg --${{ matrix.electron_arch }} --publish never - name: Verify packaged macOS app shell: bash env: @@ -87,7 +87,7 @@ jobs: version: 11.19.0 - uses: actions/setup-node@v4 with: - node-version: 22 + node-version: 24 cache: pnpm - run: pnpm install --frozen-lockfile - name: Verify tag @@ -96,7 +96,7 @@ jobs: - run: pnpm typecheck - run: pnpm test - run: pnpm build - - run: pnpm exec electron-builder --win nsis --x64 --publish never + - run: node scripts/electron-builder.cjs --win nsis --x64 --publish never - uses: actions/upload-artifact@v4 with: name: windows-x64 diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index a234d28..8212904 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ Thanks for helping improve Clean My Codex. Bug reports, focused fixes and well-s ## Development -The project requires Node.js 22 and pnpm 11.19. +The project requires Node.js 24 and pnpm 11.19. ```bash pnpm install diff --git a/README.md b/README.md index 8388829..eca572b 100644 --- a/README.md +++ b/README.md @@ -152,7 +152,7 @@ Cleanup is written to a log — cache and leftover removals record the path and ## Development -Requires Node.js 22 and pnpm 11.19. +Requires Node.js 24 and pnpm 11.19. Built on Electron 43, which runs on macOS 12+ and Windows 10+. ```bash pnpm install @@ -170,6 +170,7 @@ Packaging: ```bash pnpm build:mac pnpm build:win +pnpm build:win-arm64 ``` ## Contributing and support diff --git a/README_CN.md b/README_CN.md index a354920..0223fc1 100644 --- a/README_CN.md +++ b/README_CN.md @@ -152,7 +152,7 @@ Clean My Codex 跟随当前 Codex Desktop 的存储布局,建议同时使用 ## 开发 -需要 Node.js 22 和 pnpm 11.19。 +需要 Node.js 24 和 pnpm 11.19。基于 Electron 43,运行环境为 macOS 12+ 与 Windows 10+。 ```bash pnpm install @@ -170,6 +170,7 @@ pnpm check ```bash pnpm build:mac pnpm build:win +pnpm build:win-arm64 ``` ## 贡献与支持 diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 32e66a8..59b2eb8 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -1,30 +1,28 @@ -# Clean My Codex 0.1.6 - -## New - -- **Plan output is now tracked as a session asset.** Codex's plan mode writes revisions under `~/.codex/plans//`; each conversation's revisions collapse into one row named by the H1 of its newest revision, tagged `Plan`, and removed with the conversation that owned it. -- **Workspace cleanup can delete related conversations.** A workspace folder deletion now offers the same "also delete related conversations and their session assets" option that worktrees already had, so removing `~/Documents/Codex/xxx` can take the conversations that ran there with it. The rule is unchanged — session assets follow their session; the workspace does not. -- **Windows is now supported end-to-end, not just packaged.** Releases publish a Windows NSIS installer (`.exe`) alongside the macOS `.dmg`, and the app now works there: - - **Desktop detection** recognizes the Codex/ChatGPT desktop app on Windows — the MSIX install under `Program Files\WindowsApps\OpenAI.Codex__\`, the legacy `Program Files\Codex` and `AppData\Local\Programs\Codex` layouts, including Electron helper processes — and excludes the crashpad handler the way macOS does. Because the Windows desktop app is tray-resident (the window × button does not quit it), the blocker now points to **File → Exit**; the auto-quit checkbox, which relies on macOS AppleScript, stays hidden off-darwin. - - **CLI location** finds `codex` inside the desktop app's versioned/hash install directories (newest copy first when an upgrade left several), then falls back to `where.exe` and `PATH`, with case-insensitive environment lookup. A failed lookup is no longer cached for the session — install or upgrade Codex while Clean My Codex is open and the next operation finds it. - - **Scheduled cleanup** on Windows moved off locale-dependent `schtasks /Query` output to PowerShell `Get-ScheduledTaskInfo`, which emits one invariant ISO timestamp for the next run. The repair pass that re-aligns an out-of-date launch agent on macOS now also reinstalls a Windows task whose executable, arguments, daily interval, or missed-run/battery settings no longer match this build. - - **Update checks** run on Windows too (previously macOS-only), with a Windows-specific prompt to download and run the x64 installer. The Software Update row is now shown on every platform. - - **Path identity** is handled through a shared layer that normalizes extended-length (`\\?\`) and UNC paths, resolves filesystem aliases through the nearest existing ancestor, and compares Windows paths case-insensitively — and is recognized on every host so the Windows behavior is regression-tested on macOS. -- **Crash and exception logging.** Every IPC handler is wrapped to record failures before Electron serializes them, the main process observes fatal exceptions, and the renderer forwards uncaught errors, unhandled rejections, and React recoverable/uncaught errors to the main process. Worker errors now carry their stack. All of it lands in the existing `cleanup.log` alongside the normal operation record, so a submitted diagnostic log can explain both a wrong result and a failure that happened before one. -- **Linux builds have been retired.** The AppImage target, bundled icon, `build:linux` script, and XDG fallback paths are gone; the location layer now refuses unsupported platforms explicitly rather than silently guessing. - -## Improvements - -- **Resource detail views are unified.** Sessions, Session Assets, Plugins, Workspace, and Worktrees now share one set of list controls: - - **Sortable columns** — click a header to sort by it, click again to flip direction; an arrow shows the active direction and a faint double-chevron marks every sortable column. The old sort dropdowns are gone. - - **Per-column funnel filters** replace the type/status `