Skip to content

Fix packaged updater ASAR extraction handoff - #314

Closed
IgorWarzocha wants to merge 5 commits into
devfrom
fix/updater-asar-handoff
Closed

Fix packaged updater ASAR extraction handoff#314
IgorWarzocha wants to merge 5 commits into
devfrom
fix/updater-asar-handoff

Conversation

@IgorWarzocha

Copy link
Copy Markdown
Owner

Summary

  • extract update archives out of process with the OS tar executable so Electron does not swallow app.asar
  • remove only the exact obsolete Linux wrapper that shadows npm/Bun launchers
  • bump launcher publish versions to 0.1.72 and 0.1.72-dev.0
  • add updater archive and legacy-wrapper migration contracts to the umbrella test suite

Root cause

Electron patches Node filesystem handling for paths ending in app.asar. The in-app updater used the JavaScript tar library inside Electron, which extracted the executable but silently omitted the packaged app.asar. Separately, old Linux installs generated ~/.local/bin/howcode as a frozen direct-app wrapper, shadowing bunx and global package launchers.

Validation

  • exact released Linux archive and old tar package extract correctly under stock Node
  • exact archive under Electron + JavaScript tar reproduces { executable: true, asar: false }
  • committed OS-tar helper under Electron produces { executable: true, asar: true }
  • full gate: 47 test files / 114 tests, React Doctor 100
  • launcher dry-runs clean for 0.1.72 and 0.1.72-dev.0

IgorWarzocha and others added 5 commits May 28, 2026 14:56
* Replace image in README with new source

Updated the image in the README to a new source.

* Prepare 0.1.66 hotfixes (#277)

* fix linux command launcher detach

* Prepare 0.1.67 worktree fixes

* Polish worktree branch creation

* Patch sidebar shell updates in place

* Clean up stale docs

* Fix markdown diff theme text

* Use Pierre system diff themes

* Fix markdown artifact editor loading

* Hide raw reasoning previews

* Align empty project work panel

* Prepare 0.1.66 hotfixes

* Use default branch for diff baseline

* Fix worktree parent persistence

* Limit child worktree creation to active branch

* Add top dividers for worktree groups

* Show pending state for sidebar work actions

* Add custom worktree icon

* Tighten hotfix changelog copy

* Harden diff baseline branch detection

* Align AWS SDK overrides with Pi runtime
* Replace image in README with new source

Updated the image in the README to a new source.

* Prepare 0.1.66 hotfixes (#277)

* fix linux command launcher detach

* Prepare 0.1.67 worktree fixes

* Polish worktree branch creation

* Patch sidebar shell updates in place

* Clean up stale docs

* Fix markdown diff theme text

* Use Pierre system diff themes

* Fix markdown artifact editor loading

* Hide raw reasoning previews

* Align empty project work panel

* Prepare 0.1.66 hotfixes

* Use default branch for diff baseline

* Fix worktree parent persistence

* Limit child worktree creation to active branch

* Add top dividers for worktree groups

* Show pending state for sidebar work actions

* Add custom worktree icon

* Tighten hotfix changelog copy

* Harden diff baseline branch detection

* Align AWS SDK overrides with Pi runtime

* Add native Smart BTW extension (#282)

* Add native smart btw extension

* Refine smart btw session slots

* Fix smart btw takeover behavior

* Restore smart btw sessions from hidden messages

* Project smart btw state from thread history

* Stabilize smart btw gui projection

* Seed restored smart btw follow-ups

* Remove auto trees from smart btw branch

* Update changelog for smart btw

* Fix smart btw runtime settings edges

* Bump version for smart btw release

* Fix pages changelog section parsing

* Add composer session tree navigation (#283)

* feat(composer): add always-visible session tree popover for UI review

* fix(composer): narrow session tree popover to btw inset

* fix(composer): simplify session tree panel chrome

* fix(composer): align session tree row labels vertically

* fix(composer): restore line height on session tree row text

* fix(composer): baseline-align session tree row with shared small type

* fix(composer): use meta/small type on session tree rows

* feat(composer): fold session tree subtrees with chevrons

* fix(composer): use size-12 tree chevrons like sidebar folds

* fix(composer): center-align session tree chevron and text rows

* fix(composer): tighten session tree row height

* fix(composer): add inner padding on session tree row body

* feat(composer): apply Pi no-tools filter to session tree

* feat(composer): session tree from Pi JSONL with filtered rows and panel UX

Load session tree via service host (getSessionTreeList), map getTree() to
shared rows with Pi-aligned no-tools filtering, omit bookkeeping entries,
and show branch summaries in accent. Panel: sticky header, aligned highlight
columns, branch/compact labels.

* fix(composer): refresh session tree on watch and fix active path

* feat(composer): session tree navigate with inline confirm

Wire Pi-style navigateTree on the live thread runtime via
composer.session-tree.navigate, mutation lock, and IPC host routing.

Composer tree rows show a hover History control and sidebar-style
inline confirm (Cancel / No / Sum). Confirm closes the tree panel;
errors use composer errorMessage; restored editor text applies to draft.
Branch summarize reuses existing isCompacting / compaction pill UI.

Add session-tree CSS, navigate confirm component, and navigate hook.
Extend desktop action contracts and payload helpers.

* fix(composer): icon-only session tree navigate confirm

Use Undo2 and ListCollapse in the inline strip with top tooltips.
Show History tooltip on the right.

* fix(composer): show compaction UI during branch tree summarize

Pi branch summary sets isCompacting but does not emit compaction_start.
Publish compaction-start and compaction thread updates when summarize
navigate begins and settles, matching /compact behavior.

* fix(composer): route tree navigate compaction events to UI

Session tree navigate ran in the thread runtime host but published
thread updates through the main-process thread publisher, so the
renderer never saw compaction-start. Use live-thread-publisher like
/compact and sendComposerPrompt.

* fix(composer): session tree leaf, /tree, and active-path reveal

Use live runtime leafId when building the tree list so active-path
highlighting stays correct after branch summarize.

Wire /tree slash command and open-on-demand panel (remove dev always-on).
Refetch tree on navigate; disable background session-watch refresh.

Click active-path rows to scroll the thread via session-tree-reveal.

* feat(composer): session tree browse, anchor mark, esc to close

* feat(composer): read-only session tree branch preview

Load thread messages along the parent chain to a selected tree entry
via getThreadPreviewAtEntry on the service host. Composer browse dispatches
preview events; the main timeline overlays preview data until the panel
closes or the user returns to the anchor.

* fix(composer): reopen session tree after navigate without summary

* fix(composer): push thread up for session tree; dismiss tree on typing

* feat(composer): label session tree navigation

* Revert "feat(composer): label session tree navigation"

This reverts commit 2be2ac4.

* fix(composer): display pi tree labels

* Reapply "feat(composer): label session tree navigation"

This reverts commit 1dedd61.

* fix(composer): portal session tree label field

* fix(composer): portal session tree confirm controls

* fix(composer): persist session tree navigation labels

* fix(composer): fallback label new branch summary

* fix(composer): await tree summary before labelling

* fix(composer): forward tree label through host

* feat(composer): label session tree entries inline

* docs: note session tree labelling

* fix(composer): harden session tree popover state

* fix(composer): address session tree review edge cases

* Bump Pi SDK and rework extension UI (#289)

* Bump Pi SDK packages to 0.79.0

* Show Pi prompt cache hit rate

* Wire Pi project trust backend

* Add project trust composer prompt

* Trim project trust prompt UI

* Tighten project trust prompt layout

* Right align project trust actions

* Use Pi-native extension UI dialogs

* Wire ask questions through Pi settings

* Fix trusted global extension loading

* Move extension dialog submit action into header

* Stop rendering extension notifications as cards

* Render extension notifications inline

* Simplify extension notification label

* Style extension notifications by severity

* Keep extension notifications standalone

* Render extension statusline under composer

* Tighten extension statusline styling

* Apply extension statusline typography directly

* Align extension statusline rail

* Polish extension statusline layout

* Nudge extension statusline text

* Use generic Pi extension widgets and shortcuts

* Polish Pi extension widget rendering

* Use Geist Mono as primary app mono font

* Polish Pi extension overlay surfaces

* Raise context popover above extension UI

* Remove Howcode native extension settings

* Move ask questions extension out of Howcode

* Remove stale native extension cleanup entries

* Fix Pi extension UI review issues

* Fix Pi dialog timeout handling

* Keep dialog runtimes alive while waiting

* Save project trust by requested cwd

* Handle unsupported Pi component widgets

* Bridge Pi extension editor shortcuts

* Fix Pi extension review issues

* Scope Howcode keybindings by UI mode

* Keep Pi TUI terminal focus stable

* Separate Pi TUI and terminal drawer shortcuts

* Split Pi TUI and shell terminal viewports

* Allow drawer terminal clear in Pi TUI mode

* Prevent hidden composer stealing Pi TUI focus

* Update changelog for Pi SDK UI work

* Restore Smart BTW changelog entry

* Bump Pi SDK packages to 0.79.1

* Open session tree on idle double Escape

* Tighten Pi SDK changelog notes

* Bump app and build dependencies (#290)

* Add headless LAN browser mode (#291)

* Enable headless dev server host config

* Add packaged headless server mode

* Keep browser bridge API in sync

* Avoid Chromium headless flag collision

* Use headless Ozone platform

* Pass headless Ozone switch from launcher

* Support browser file uploads in headless mode

* Shorten browser upload attachment label

* Tune browser upload attachment control

* Use smaller browser device attachment label

* Use existing tiny upload control styling

* Use composer footer text for device attach control

* Clean up browser upload temp files

* Stream browser upload attachments

* Add headless access token auth

* Add TanStack Router URL sync

* Fix router URL state feedback loop

* Harden headless router and upload errors

* Align footer controls with extension status

* Clarify headless run docs

* Update changelog for headless mode

* Bump dev launcher publish version

* Refresh project README

* Fix macOS window controls (#292)

* Fix macOS window controls

* Vouch macOS contributor

* Tighten macOS drag zones

* Support WSL project terminals on Windows (#293)

* Support WSL UNC terminal cwd on Windows

Why:
- Native Windows terminal launch currently treats WSL UNC project paths as host Windows paths.
- Opening a WSL project through \\wsl$ or \\wsl.localhost should start the embedded shell inside that distro and Linux cwd.

Verified:
- bun vitest run desktop/terminal/terminal-command.helpers.test.ts
- bun x biome check desktop/terminal/terminal-command.helpers.ts desktop/terminal/terminal-process.ts desktop/terminal/terminal-command.helpers.test.ts
- bun run typecheck:desktop
- git diff --check

(cherry picked from commit 7657ccc)

* Clarify custom Pi directory WSL use

* Route WSL Pi terminal sessions through wsl

---------

Co-authored-by: Christian Scherkl <christianscherkl79@gmail.com>

* Fix mac drag zone and sidebar spacing

* Polish composer overlay stacking

* Stabilize composer overlay polish

* Unify workspace rail layout

* Refine thread rail alignment

* Update changelog for composer rail polish

* Polish website and docs copy

* Fix session view refresh and worktree routing (#298)

* Isolate Pi extension UI refreshes

* Preserve worktree context across Chat and Inbox

* Tighten worktree thread scoping

* Honor inbox thread modes

* Unstick thread URL sync

* Tighten inbox worktree routing

* Trim low-signal regression tests [skip ci]

* Bump Pi SDK and add session rename (#301)

* Bump Pi SDK packages to 0.79.6

* Add session rename support

* Keep compaction pill above composer overlays

* Update changelog for SDK bump and session rename

* Fix right Alt extension shortcuts (#303)

* Align isolated loader with Pi SDK (#304)

* Update dependencies to latest compatible releases

* Refresh React Doctor configuration

* Reshape repository agent guidance

* Reach 100 React Doctor score

* Harden reviewed React Doctor fixes

* Add Effect v4 engineering skill

* Migrate terminal transport to Effect RPC

* Restore awaited terminal shutdown

* Move terminal lifecycle into Effect

* Close terminal lifecycle races

* Move live runtime ownership into Effect

* Harden runtime registry ownership

* Release runtimes after dialog expiry

* Replace skill creator session with bundled skill

* Expose bundled skills to every runtime

* Remove obsolete in-process runtime stack

* Move runtime host broker into Effect

* Preserve broker recovery during lifecycle races

* Move desktop service lifecycle into Effect

* Finish desktop service shutdown ownership

* Document Effect migration status

* Decode Effect process boundaries with Schema

* Modularize Effect runtime ownership

* Harden app and launcher updates

* Modularize update runners

* Finish updater concern split

* Close updater failure paths

* Protect live updater bundles

* Document updater rollout order

* Close updater review gaps

* Close updater handoff gaps

* Update dependencies and native runtime contracts

* Localize sidebar branch actions

* Share inline popover dismissal

* Unify sidebar project action menus

* Centralize sidebar row menu ownership

* Document sidebar ownership boundaries

* Split sidebar project work model

* Share sidebar branch group rendering

* Keep nested worktree sessions visible

* Harden sidebar action and rendering boundaries

* Harden composer ownership boundaries

* Align composer model popover rows

* Harden terminal viewport boundaries

* Harden thread timeline mechanics

* Harden inbox composer boundaries

* Harden settings ownership boundaries

* Serialize Pi settings writes

* Keep Pi draft flushes current

* Harden resource and session screens

* Harden app shell control plane

* Document Pierre GitOps redesign

* Establish GitOps review domain

* Gate changes on React Doctor

* Let Pierre own diff review selection

* Modularize GitOps composer

* Emit canonical GitOps patches

* Contain diff stream cancellation

* Add explicit GitOps review actions

* Hydrate complete GitOps file context

* Edit GitOps files safely

* Mirror Pierre hunk review controls

* Narrow GitOps to agent review

* Reset local GitOps review decisions

* Expand GitOps context in one step

* Align trailing context with review actions

* Restore context action typography

* Style context action across Pierre boundary

* Restore Pierre gutter comment interactions

* Make GitOps hunk undo durable

* Route rejected hunks to agent review

* Map agent-native hardening phases

* Remove local dependency cycles

* Unify desktop request handling

* Narrow AppShell feature ownership

* Enforce architecture boundaries

* Split transport host responsibilities

* Complete hardening feedback loops

* Split static site route ownership

* Prune feature and UI tests

* Prune launch-obvious tests

* Complete Effect lifecycle ownership

* Scope runtime polling races

* Unify desktop shutdown ownership

* Scope stock Node probing

* Decode external payload boundaries

* Harden persistence and payload boundaries

* Harden runtime and persistence boundaries

* Prune implementation-confirmation tests

* Remove completed implementation docs

* Update 0.1.68 changelog

* Refresh dependencies before merge

* Align Biome schema version

* Use fixed CSS highlight parser

* Keep disposable builds isolated

* Make Pi takeover session ownership exclusive

* Await Pi terminal exit during handoff

* End Pi takeover promptly on return

* Repair CDP debugging workflow

* Finish 0.1.68 release notes

* Render reasoning summaries once

* Harden worktree lifecycle ownership

* Close worktree lifecycle review gaps

* Close lifecycle race gaps

* Finish worktree lifecycle hardening

* Close remaining worktree race gaps

* Serialize worktree repository mutations

* Close worktree ownership gaps

* Correct footer guidance filename

* Cull feature-oriented tests

* Refine phone-browser roadmap scope

* Bump npm launcher publish versions

* Highlight React Doctor release milestone

* Reorient 0.1.68 release notes

* Fix Windows Electron archive extraction

* Install Bun in release publisher jobs

---------

Co-authored-by: Christian Scherkl <christianscherkl79@gmail.com>
@IgorWarzocha

Copy link
Copy Markdown
Owner Author

@codex please review this PR and give me 10-20 issues if any. Categorize findings as required, recommended, or optional.

@IgorWarzocha

Copy link
Copy Markdown
Owner Author

Closing this lineage-heavy comparison; replacing it with the same commit cleanly cherry-picked onto current dev.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: baa97a7751

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

request: http.IncomingMessage,
response: http.ServerResponse,
) {
const { pathname } = new URL(request.url ?? '/', 'http://headless')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge [Required] Reject malformed headless request targets safely

When an unauthenticated client sends an invalid absolute-form request target such as http://[, this new URL call throws synchronously. The http.createServer callback in headless/server.ts has no error boundary or clientError handling, so one malformed request can become an uncaught exception and terminate the remotely exposed headless app; parse failures should return a 400 response instead.

Useful? React with 👍 / 👎.

Comment on lines +121 to +123
const versionDiff = compareVersions(release.version, currentVersion)
if (versionDiff > 0) return true
if (versionDiff < 0) return false

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge [Required] Honor switches from dev to an older stable build

When a user disables the dev update channel while the dev build has a higher version than the current main-channel release, this unconditional rejection prevents both recovery and installation of the selected stable bundle. The app consequently continues running the dev build until main eventually overtakes its version, so channel changes need to compare the running channel as well as version/hash rather than treating the selected-channel bundle as an ordinary downgrade.

AGENTS.md reference: src/electron/main/updater/AGENTS.md:L2-L2

Useful? React with 👍 / 👎.

Comment on lines +84 to +89
try {
const newPath = resolveFileDiffPath(fileDiff)
const oldPath =
fileDiff.type === 'new' || fileDiff.type === 'rename-pure'
? null
: resolveDiffFilePath(fileDiff.prevName ?? fileDiff.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge [Recommended] Load deleted diffs without a worktree file

For a deleted file, resolveFileDiffPath(fileDiff) still supplies the deleted path as newPath; the desktop loader then unconditionally reads that nonexistent worktree file and returns missing, even though the baseline contents were loaded successfully. Expanding collapsed context on a large deleted file therefore fails; deleted diffs should pass/return a null new side while loading the complete old side.

AGENTS.md reference: src/app/native/gitops/AGENTS.md:L6-L6

Useful? React with 👍 / 👎.

updatedAt: nowIso(),
}
record.suppressOutputVisibilityUntilInput = true
rmSync(record.transcriptPath, { force: true })

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge [Recommended] Make transcript clearing part of the write queue

If the 40 ms persistence timer has already queued or started a transcript write when the user submits clear, cancelling persistTimer does not cancel persistPromise; this synchronous removal can run first and the pending write then recreates the file with the pre-clear history. A crash or reopen before another write can consequently restore supposedly cleared output, so deletion must be ordered after the existing persistence promise.

AGENTS.md reference: desktop/AGENTS.md:L8-L8

Useful? React with 👍 / 👎.

Comment on lines +39 to +40
await moveSessionTranscript(input.record, nextTranscriptPath).catch(reportTranscriptWriteFailure)
input.store.delete(previousSessionId)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge [Recommended] Abort terminal rebinding when transcript moves fail

When the transcript write or rename fails—for example because the destination is locked or storage becomes unavailable—the error is only reported and rebinding continues. moveSessionTranscript updates record.transcriptPath only after a successful rename, so the record is registered under the new session ID while later history writes still target the old unbound-session file, causing the new session to lose its transcript on reopen; either fail the rebind or explicitly repair the transcript path.

Useful? React with 👍 / 👎.

Comment on lines +77 to +80
const flushPiSettings = useCallback(async () => {
const dirtyKeys = [...dirtyKeysRef.current]
dirtyKeysRef.current.clear()
const snapshot = draftPiSettingsRef.current

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge [Recommended] Retain dirty Pi settings after failed writes

Dirty keys are cleared before any desktop action completes, and the non-theme write path neither checks an { ok: false } result nor restores keys when the promise rejects. During a desktop-service restart, validation failure, or filesystem error, closing Settings therefore discards the only retry state while the optimistic draft still showed the new value; failed keys should remain dirty or be reverted, and the unmount flush should not leave a rejected promise unhandled.

AGENTS.md reference: src/app/settings/settings/AGENTS.md:L4-L4

Useful? React with 👍 / 👎.

Comment on lines +39 to +40
const response = await fetch(addCacheBust(url), {
signal: AbortSignal.timeout(DOWNLOAD_TIMEOUT_MS),

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge [Recommended] Use an idle timeout for update downloads

On a slow but active connection, AbortSignal.timeout(5 minutes) measures the entire archive transfer rather than inactivity, so a large Electron bundle that cannot finish within five minutes is aborted on every retry from byte zero and can never update. The launcher downloader already resets its timeout on progress; the in-app transport needs the same idle-timeout behavior.

AGENTS.md reference: src/electron/main/updater/AGENTS.md:L2-L2

Useful? React with 👍 / 👎.

options.authRequired && options.accessToken
? `#token=${encodeURIComponent(options.accessToken)}`
: ''
return `http://${accessHost}:${options.port}${tokenFragment}`

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge [Recommended] Bracket IPv6 hosts in headless access URLs

When headless mode is bound to an IPv6 literal such as ::1 or a LAN IPv6 address, this produces an invalid URL like http://::1:5173. Manually adding brackets does not fully rescue loopback mode because createHostTrust records ::1:5173 while browsers send [::1]:5173, causing config/auth requests to be rejected; IPv6 literals need one canonical bracketed representation for both URL generation and host trust.

Useful? React with 👍 / 👎.

Comment thread docs/changelog.md
### 0.1.67


- Added **server** headless/browser mode. Use `howcode --server --host 0.0.0.0 --token xyz`. The app is usable in a mobile browser if you wish to do so.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge [Recommended] Advertise the implemented headless flag

The changelog tells users to run howcode --server, but a repository-wide search finds no --server handling: parseHeadlessServerOptions accepts only --headless, --howcode-headless, or HOWCODE_HEADLESS=1, and the launcher only recognizes --headless. Following this release note launches the normal GUI instead of the advertised server, so the command should use --headless or the alias must be implemented end to end.

Useful? React with 👍 / 👎.

Comment on lines +363 to +365
async function pumpBody() {
const boundaryIndex = buffer.indexOf(partBoundary)
if (boundaryIndex < 0) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P3 Badge [Optional] Validate multipart boundary suffixes before splitting

If uploaded file bytes contain \r\n--<boundary> followed by ordinary non-delimiter bytes, indexOf treats it as a boundary and consumeBoundarySuffix throws Malformed multipart upload instead of retaining it as file data. Browser boundaries make accidental collisions rare, but valid or deliberately constructed binary attachments can still be rejected; only matches followed by CRLF or -- should terminate a part.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant