Commit graph

109 commits

Author SHA1 Message Date
Teknium
1398cc40cd
fix(install): ASCII-only comment in install.ps1 voice-deps helper
test_install_ps1_is_pure_ascii guards against PowerShell 5.1 ANSI
codepage misdecoding (issues #66994/#67000); the Install-DesktopVoiceDeps
comment had an em-dash.
2026-07-28 07:59:34 -07:00
Teknium
a832139ba3
feat(wake): eager-install voice deps with the desktop; wake probes never run pip
Two fixes from live testing (Teknium):

1. Desktop installs now ship the wake/voice stacks up front.
   install.sh + install.ps1 desktop stages run 'uv pip install
   -e .[wake,voice]' (best-effort, lazy-install remains the fallback)
   before building the app, so the first ear-click arms instantly
   instead of sitting through a multi-minute onnxruntime download.
   CLI-only installs keep the lazy path — [all] curation unchanged.

2. The vanished ear: the STT/TTS gate made wake.status call
   check_tts_requirements(), whose edge path runs _import_edge_tts →
   lazy_deps.ensure — a synchronous PIP INSTALL inside a status poll.
   On a venv without edge-tts that blew the desktop's 30s RPC timeout,
   armWakeWord caught the error, the atom never learned enabled=true,
   and the ear unmounted. _tts_ready is now a pure probe: deps missing
   + lazy installs allowed counts as ready (installs at first speak)
   WITHOUT touching pip; check_tts_requirements only runs once deps
   are present. Regression test asserts the probe never calls it while
   deps are missing.
2026-07-28 07:59:34 -07:00
kael-odin
44649e69f4 test(install): add UTF-8 regression guard for skills_sync child path
Addresses hermes-sweeper review on PR #54866: the installer runs
tools/skills_sync.py as a child python.exe whose PYTHONIOENCODING /
PYTHONUTF8 the scoped install.ps1 block sets, but there was no
regression test for this child-Python UTF-8 path. The existing
test_child_process_inherits_utf8_mode covers a different (bootstrap
entry-point) flow.

Add TestSkillsSyncUtf8Guard: three subprocess tests that import
skills_sync (triggering its import-time stdout/stderr reconfigure)
and assert the checkmark/up-arrow glyphs the script prints at
tools/skills_sync.py:596,675 emit valid UTF-8 and exit 0 even when
the child env is left unset or explicitly hostile (gbk). A third
test proves the guard is load-bearing by reproducing the crash
without it.

Also keep the new install.ps1 comment ASCII-only (the checkmark
spelled out as U+2713) per the file's PS 5.1 parser-compatibility
contract at scripts/install.ps1:79-80; the literal glyph in the
comment violated that contract.
2026-07-24 17:10:39 -07:00
kael-odin
3f1de1fc4a fix(install): emit UTF-8 from skills_sync on non-UTF-8 Windows locales
On Windows with a non-UTF-8 system locale (e.g. CP936/GBK on zh-CN),
Python defaults stdout/stderr to the active codepage. tools/skills_sync.py
prints glyphs such as checkmark (U+2713) and up-arrow (U+2191) that GBK
cannot encode, raising UnicodeEncodeError mid-run.

The installer (scripts/install.ps1) captures this script's stdout and the
Rust bootstrap parses it as UTF-8 expecting a JSON result frame. A GBK
byte stream (or the traceback it triggers) surfaces as:

  WARN stdout read error: stream did not contain valid UTF-8
  stage=config-templates state=Failed
  error=install.ps1 -Stage config-templates produced no JSON result frame
        (exit=Some(0))

i.e. the stage fails even though the script exits 0. install.ps1 already
sets [Console]::OutputEncoding = UTF8, but that does not propagate to the
python.exe child (Python reads PYTHONIOENCODING / locale, not the console
encoding).

Fix in two places for defense in depth:
- tools/skills_sync.py: reconfigure sys.stdout/stderr to UTF-8 at import so
  output is valid UTF-8 regardless of caller or active codepage.
- scripts/install.ps1: set PYTHONIOENCODING=utf-8 and PYTHONUTF8=1 (scoped
  to the call, restored afterwards) around the skills_sync.py invocation.
2026-07-24 17:10:39 -07:00
Austin Pickett
bc6839aa37
fix(desktop): stop hard-failing pack on non-git checkouts + fix ZIP-path autocrlf (supersedes #67643) (#67730)
* fix(desktop): allow write-build-stamp from non-git checkouts

Stop hard-failing npm pack when neither GITHUB_SHA nor git HEAD is
available (ZIP installs / broken .git). Emit an explicit fallback stamp
instead so local Windows desktop builds can finish (#50823).

* fix(desktop): treat fallback stamps as unpinned; harden Windows install

Keep all-zero fallback commits out of -Commit/--commit pins and fetch
install.ps1 by branch instead. After bootstrap, pin the marker to the
checkout HEAD so isBootstrapComplete accepts it. On Windows, force ZIP
checkout, seed GITHUB_SHA (ASCII-only install.ps1), and avoid the pack
stamp failure.

* fix(install): pin core.autocrlf=false before ZIP-path checkout (#50823 review)

The ZIP-fallback path added in #67643 runs `git checkout -f FETCH_HEAD`
before core.autocrlf gets pinned (which only happened later, on the
shared clone-path config). On Git for Windows -- where core.autocrlf
defaults to true -- that renormalizes the repo's LF text files to CRLF in
the working tree during checkout, leaving the freshly-created managed
checkout dirty versus HEAD and aborting the next `hermes update`. That is
the exact "dirty tree the user never touched" failure the surrounding
code already guards against (install.ps1:1461-1469, 1750-1753).

Move the `config core.autocrlf false` pin to run immediately after
`git init`, before the fetch/checkout. The later idempotent pin on the
shared clone path is retained so git-clone installs are unaffected.

Addresses teknium1's review on #67643 and supersedes it, preserving the
original author's two commits.

Co-authored-by: HexLab98 <8422520+HexLab98@users.noreply.github.com>

* chore(contributors): map austinpickett commit email for attribution

The check-attribution CI gate flagged austinpickett@users.noreply.github.com
as an unmapped commit-author email (introduced by the autocrlf fix commit
on this PR). Add the per-email mapping file as the gate instructs (the
legacy AUTHOR_MAP in scripts/release.py is frozen).

---------

Co-authored-by: HexLab98 <liruixinch@outlook.com>
Co-authored-by: austinpickett <austinpickett@users.noreply.github.com>
Co-authored-by: HexLab98 <8422520+HexLab98@users.noreply.github.com>
2026-07-19 19:29:36 -04:00
PRATHAMESH75
97249cfc8a fix(install): keep install.ps1 pure ASCII so Windows PowerShell 5.1 doesn't misparse it
A commit added a bullet and an em-dash inside two Write-Host/Write-Info string
literals in scripts/install.ps1. The file has no UTF-8 BOM, so Windows
PowerShell 5.1 (which the bootstrap runs the cached script under) reads it in
the system ANSI code page (CP1252), not UTF-8. The em-dash's UTF-8 tail byte
decodes to a smart close-quote (U+201D) that the tokenizer treats as a string
delimiter, prematurely closing the string and desyncing the parser -- surfacing
as the reported cascade of syntax errors at lines 1619/1770 and aborting the
Windows GUI installer before it does anything.

Non-ASCII bytes in '#' comments are harmless (skipped to end-of-line), so the
file carried em-dashes in comments for months; only the two chars in code
broke it. Convert all non-ASCII to ASCII equivalents (em-dash -> '--', already
this file's own comment convention; bullet -> '-') and add a source-level test
locking the pure-ASCII invariant, since Linux CI cannot run the PS installer.

Fixes #66994
Fixes #67000
2026-07-18 18:40:59 -04:00
konsisumer
de90f5831b fix(install): continue after autostash restore conflicts 2026-07-18 19:18:03 +05:30
HexLab98
e53d2dfccb fix(desktop): recover Windows GPU sandbox 0x80000003 startup crashes
Grant ALL APPLICATION PACKAGES RX on the unpacked app and stick a boot
marker so fatal Chromium sandbox deaths relaunch with --no-sandbox
(#38216).
2026-07-18 02:26:19 -07:00
Gille
1f7d2be22f
fix(install): detect Git Bash Mandatory ASLR failures (#64651) 2026-07-16 22:46:46 -04:00
ethernet
39d09453f9 feat(desktop): ts-ify everything 2026-07-08 16:24:16 -07:00
HexLab98
2bc6e1a74b fix(installer): put node.exe on PATH for Windows npm lifecycle scripts
npm postinstall hooks spawn cmd.exe child processes that could not resolve
`node` even when the installer found npm — causing desktop workspace npm
install to fail with exit 1 (#48130).
2026-07-07 21:17:34 +07:00
konsisumer
18e840469f fix(install): guard Windows desktop installs against broken web_server 2026-07-05 19:35:30 -07:00
Teknium
87ae4ae94b
fix(update): harden #57659 follow-ups — task restore on failure, --force-venv split, trampoline detection, managed-install health (#57680)
Five follow-ups to #57659 from post-merge review:

1. install.ps1: gateway scheduled-task re-enable now runs in a finally
   (a thrown Remove-Item/uv venv failure previously stranded the user's
   gateway autostart disabled), and tasks that were already disabled
   before the install are no longer blindly re-enabled.
2. The venv-python holder guard is no longer bypassed by plain --force
   (which the desktop bootstrap passes on every update while its lock
   probe only checks hermes.exe/app.asar). New explicit --force-venv is
   the escape hatch; --force keeps bypassing only the hermes.exe shim
   guard.
3. _detect_venv_python_processes now also catches uv/base-interpreter
   trampolines whose exe is outside the venv, via cmdline (venv path or
   '-m hermes_cli.main' tied to this install root) and cwd.
4. Missing venv python is now UNHEALTHY on managed installs
   (.hermes-bootstrap-complete / .update-incomplete markers) so the
   repair lane runs instead of 'Already up to date!'; the repair branch
   recreates the venv first when it's gone entirely. Dev checkouts keep
   reporting healthy.
5. install.ps1 comment no longer claims a Startup-folder disarm the
   code doesn't perform (logon-only, not a mid-install respawner).
2026-07-03 04:08:37 -07:00
Teknium
b14d75f8af
fix(update): prevent and self-heal half-updated venvs on Windows (#57659)
Root-causes the July 2026 Windows incident chain (locked _brotlicffi.pyd /
_sodium.pyd during install, then 'No module named annotated_doc' with
'hermes update' insisting 'Already up to date!'):

- hermes update: probe venv core imports even when the checkout is current;
  a half-updated venv (dep sync killed mid-flight by a locked .pyd) is now
  detected and repaired instead of being reported as up to date
- hermes update (Windows): after pausing gateways, refuse to mutate the venv
  while other processes run from the venv interpreter (the Desktop backend
  runs as python.exe so the hermes.exe shim guard never saw it); --force
  keeps the old behavior
- install.ps1 venv stage: disarm gateway autostart Scheduled Tasks before
  the kill sweep (they respawn the gateway inside the kill->delete window),
  make the sweep a bounded loop requiring 3 clean passes, and rename-then-
  delete the old venv (a rename succeeds even with mapped DLLs) with stale-
  dir cleanup on the next run
- desktop updater: 'venv shim still locked after 15s' now ABORTS the update
  hand-off (restarting our backend, surfacing the holder to the user)
  instead of 'proceeding anyway (force)' into guaranteed venv corruption;
  the unlock wait also re-kills respawned backends each poll tick
2026-07-03 03:24:08 -07:00
xxxigm
a40f22798e fix(installer): reset managed clone when ff-only pull fails
Bootstrap and desktop updates run install.ps1/install.sh, which aborted
with exit 128 when the managed checkout had diverged from origin/main.
Mirror the hermes update recovery path: reset to origin/$BRANCH instead
of failing the repository stage.
2026-06-30 20:11:01 +07:00
HexLab98
95994bbc56 fix(windows): repair missing hermes.exe after pip install (#52931)
On Windows, uv pip install -e . can register hermes.exe in package metadata
while the launcher never lands on disk. Detect missing [project.scripts]
shims and reinstall entry points under the existing quarantine path in
hermes update and install.ps1.
2026-06-28 17:01:31 -05:00
konsisumer
3cf900eb67 fix(install): discard managed lockfile churn before stashing 2026-06-25 23:49:11 -07:00
Teknium
411faf08bd
fix(soul): installers seed the real default persona, upgrade legacy empty templates (#52246)
The desktop bootstrap (and curl/PowerShell/docker installs) seeded
~/.hermes/SOUL.md with a comment-only scaffold that contained no persona
text. That shadowed the runtime default (_ensure_default_soul_md ->
DEFAULT_SOUL_MD), since seeding is guarded by 'if SOUL.md doesn't exist'.
Result: every fresh installer install got the empty template instead of
the documented Hermes persona; desktop just made it visible in onboarding.

- install.sh / install.ps1 / docker/SOUL.md now write DEFAULT_SOUL_MD.
- _ensure_default_soul_md() upgrades a SOUL.md still matching the known
  legacy scaffold in place; customized files (any deviation, incl. a
  persona appended below the comment) are never touched.
- Detection normalizes CRLF/BOM so Windows-installer drift still matches.
2026-06-24 18:56:26 -07:00
emozilla
6638199c53 fix(install): harden venv-resident process sweep on Windows
Follow-up to the salvaged venv-recreate fix. Three changes to the
Install-Venv pre-delete sweep:

- Match the venv path with a case-insensitive StartsWith instead of the
  PowerShell -like operator. A venv path containing wildcard
  metacharacters ('[', ']') — legal in a Windows user name — silently
  fails to match under -like, which would let the locking process slip
  through and reintroduce the exact access-denied failure this fix
  closes.
- Retry Remove-Item once after a short pause. A force-killed process can
  take a moment to release its file handles, so the first delete may
  still hit a locked .pyd; retry before failing the stage.
- Note in a comment that the gateway autostart task runs at LIMITED
  integrity as the current user, so the installer always runs at
  equal-or-higher integrity and can read the process executable path,
  and that Get-CimInstance is preferred over Get-Process because it
  returns a null path for an uninspectable process instead of throwing.

Adds a regression test asserting the recreate branch sweeps by venv path
prefix, uses StartsWith rather than -like, and runs the sweep before
Remove-Item.

Covers issues #47036, #47557, #47910.
2026-06-24 13:25:44 -04:00
Dana Moverman
7e55b934ea fix(install): kill gateway running from venv before recreating it (Windows)
The Windows venv-recreate guard only runs `taskkill /IM hermes.exe`, but the
gateway that a scheduled task or watchdog autostarts runs as
`pythonw.exe -m hermes_cli.main gateway run` straight out of venv\Scripts\.
Its image name is python/pythonw, so taskkill never matches it; it keeps the
venv's native extensions (e.g. tornado\speedups.pyd) loaded, and the following
Remove-Item fails with "Access to the path is denied" -- aborting boot at the
venv stage so the desktop app never loads.

Additionally stop any process whose executable lives under this venv, matched
by path so the image name is irrelevant and a global/system python outside the
venv is never touched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 13:22:36 -04:00
xxxigm
23683c3353 fix(installer): re-resolve Python fallback at venv stage on Windows (#50769)
The Windows installer runs each -Stage NAME in its own powershell.exe under
Hermes-Setup.exe. Test-Python records a detected fallback (e.g. 3.12 when 3.11
is absent) via an in-memory $script:PythonVersion = $fallbackVer mutation,
which dies with the python stage's process. The fresh venv stage starts with
$PythonVersion back at its "3.11" default, so it logged "Creating virtual
environment with Python 3.11..." and ran uv venv venv --python 3.11, failing
with exit 2 on machines that only had the fallback installed.

Add a cross-process-safe Resolve-AvailablePythonVersion helper (preferring the
requested version, then the shared $PythonFallbackVersions list, probed via
uv python find) and call it at the top of Install-Venv before creating the
venv. Test-Python's fallback loop now iterates the same shared constant so
detection and venv creation can't drift.
2026-06-23 21:33:08 -07:00
xxxigm
97888fed48 fix(install): drop system-browser fallback + auto-repair stale snap override
The installer scanned PATH/well-known locations for a Chrome/Chromium binary
and, when found, skipped the bundled Playwright Chromium download and wrote that
path into ~/.hermes/.env as AGENT_BROWSER_EXECUTABLE_PATH. On Snap-based systems
`command -v chromium` resolves to /snap/bin/chromium, whose sandbox blocks
agent-browser's control socket under /tmp -- so every browser_navigate hung
until the 60s timeout fired ("opening web page failed").

Drop the system-browser fallback entirely (per maintainer direction):
find_system_browser()/Find-SystemBrowser now honor ONLY an explicit, user-set
AGENT_BROWSER_EXECUTABLE_PATH override -- no PATH scan, no well-known-path scan.
A /snap/* path is rejected even when set explicitly, since its confinement is
the bug. Applied to both install.sh (Linux/macOS) and install.ps1 (Windows).

Crucially, also auto-repair already-affected installs: the bad snap path
persists in .env and is read directly by the runtime, and the installer skips
re-config when AGENT_BROWSER_EXECUTABLE_PATH is already set ("already
configured"), so a plain reinstall/update never recovered an existing user. New
strip_snap_browser_override() removes a snap-pointing AGENT_BROWSER_EXECUTABLE_PATH
(and its auto-written comment) from .env on every install/update, run from both
browser-setup paths (install_node_deps and ensure_browser), so updating is
enough to recover. A deliberately-set non-snap override is left untouched.

docker/stage2-hook.sh is intentionally untouched: it discovers the bundled
Playwright Chromium, not a system browser.
2026-06-23 10:38:15 -07:00
xxxigm
ac83365d96 fix(install): expand 8.3 short %TEMP% so Windows Node/Electron stages don't abort
On a Windows profile whose folder name contains a space (e.g. "First Last"),
Windows can expose %TEMP%/%TMP% as an 8.3 short path
(C:\Users\FIRST~1.LAS\AppData\Local\Temp). PowerShell's FileSystem provider
mishandles the "~1.ext" component when the path reaches a provider cmdlet such
as `Tee-Object -FilePath`, throwing:

  An object at the specified path C:\Users\FIRST~1.LAS does not exist.

Every Node/Electron install+build stage streams its log to %TEMP% via
Tee-Object, so they all abort with that error (browser-tools npm, Playwright,
TUI npm, and the hard-failing desktop build), while the Python/uv stages --
which never write a side log to %TEMP% through a provider cmdlet -- succeed.

Normalize %TEMP%/%TMP% to their long form once, up front, so every downstream
cmdlet and child process sees a path the provider can resolve.

Fixes #39308
2026-06-20 16:24:52 -07:00
xxxigm
e74033b39b test(install): add ConvertTo-LongPath helper for 8.3 short paths
Adds a ConvertTo-LongPath helper to install.ps1 that expands a Windows 8.3
short path (e.g. C:\Users\FIRST~1.LAS) back to its long form via
Scripting.FileSystemObject. Paths without a "~<digit>" component are returned
unchanged (no COM round-trip), and any COM failure falls back to the input.

Adds an AST-loaded unit test that exercises the helper without executing the
installer body (pass-through, null/empty, and graceful fallback).
2026-06-20 16:24:52 -07:00
kshitij
2fa16ec2d2
Merge pull request #48529 from kshitijk4poor/salvage-48372-eap
fix(install): relax EAP=Stop around native git/uv calls + fail-fast on uv venv failure (#48352, salvage of #48372)
2026-06-18 22:17:53 +05:30
kshitijk4poor
fd12e59e6b fix(install): fail fast when uv venv genuinely fails under relaxed EAP
PR #48372 relaxes EAP=Stop around the uv venv call so PowerShell 5.1
doesn't mistake uv's 'Using CPython ...' stderr for a terminating
NativeCommandError. But relaxing EAP also means a *genuine* uv venv
failure (exit != 0) no longer aborts on its own — Install-Venv would
continue and print 'Virtual environment ready', and in stage mode
Invoke-Stage would report ok=true, even though no venv was created.

Capture $LASTEXITCODE immediately after the relaxed call and throw on
non-zero (Pop-Location first, matching the function's other exit paths),
so the venv stage fails fast instead of falsely succeeding. This is the
explicit guard originally proposed in #48463 (devorun), composed on top
of #48372's reusable helper + regression test.

Adds a regression test asserting the uv venv exit-code capture + throw.
2026-06-18 22:11:35 +05:30
Tranquil-Flow
67316fdc94 fix(install): relax native stderr handling in install.ps1 (#48352) 2026-06-18 12:06:29 +02:00
xxxigm
a14bae6bcc fix(install): resolve PowerShell host instead of bare powershell for uv
The Windows installer's Install-Uv spawned the astral uv installer with a
hardcoded bare `powershell -ExecutionPolicy ByPass -c "irm .../uv | iex"`.
That name resolves only to Windows PowerShell, and only when its System32
directory is on PATH. Run under PowerShell 7+ (`pwsh`) — or any session where
`powershell` isn't on PATH — the spawn dies with "The term 'powershell' is not
recognized", and uv installation aborts (the installer then appears stuck).

Add Get-PowerShellHostExe, which prefers the absolute path of the host we're
already running in (PATH-independent), then falls back to powershell/pwsh via
Get-Command, then to the bare name. Install-Uv now invokes that resolved exe.
2026-06-18 16:26:34 +07:00
brooklyn!
c1f9eb0ec4
fix(desktop): resolve electronDist dynamically + self-heal blocked installs (supersedes #48081/#48082) (#48091)
* fix(desktop): resolve electronDist dynamically + self-heal blocked installs

Supersedes the static-path approach (#48081) and the install-step self-heal
(#48082) with a fix that removes the whole failure class instead of chasing each
symptom. Three distinct faults converged into the June desktop-build outage; this
closes all three.

Root cause (the part #48081 left open — "Gap B"):
  build.electronDist was a static relative path in apps/desktop/package.json, but
  npm workspace hoisting is NOT deterministic — depending on the npm version and
  what else is installed, npm nests the workspace-only electron devDep under
  apps/desktop/node_modules/electron OR hoists it to the repo root. A static path
  matches only one layout, so a clean install intermittently fails with "The
  specified electronDist does not exist". #48081 re-pointed the path at the
  nested layout (correct today) but electron-builder reads electronDist
  STATICALLY, so any future hoist change silently breaks it again — only caught
  by a CI invariant, never self-corrected.

Fix:
- scripts/run-electron-builder.cjs: resolve electron the way Node's runtime does
  — require.resolve("electron/package.json") walks node_modules from the desktop
  project upward and finds electron wherever npm actually put it. The path can
  never drift out of sync with the install layout again, on any OS/npm version.
    * dist present -> pass -c.electronDist=<abs>/dist so electron-builder reuses
      the unpacked runtime (keeps the #38673 fast path that dodges the 26.8.x
      missing-binary re-unpack bug).
    * dist absent  -> omit electronDist; electron-builder fetches Electron itself
      via @electron/get honoring electronVersion + ELECTRON_MIRROR.
  package.json: builder script now runs the wrapper; the static build.electronDist
  is removed (the resolver owns it).
- main.py / install.sh / install.ps1: on a dependency-install failure where the
  electron package staged but its dist is missing (electron's install.js
  process.exit(1) on a blocked/throttled binary download — #47266/#47917/#48021),
  repopulate the dist via electron's downloader (canonical, then npmmirror.com)
  and CONTINUE to the build instead of aborting. npm runs postinstall LAST, so
  the only casualty is electron/dist; bailing here is what made the pack-time
  mirror self-heal unreachable on a blocked network. Hard-fail only when electron
  never staged at all (a genuine dependency error).
- The pack-time mirror fallback now retries the build even when the pre-fetch
  can't populate the dist: the wrapper lets electron-builder download Electron
  itself via the mirror, so the retry is no longer a no-op (it was, when
  electronDist was a static path).

The exact 40.10.2 pin (already on main) keeps the third mode — the native
@electron-internal/extract-zip win32 binding that 40.10.3/40.10.4 ship without a
published prebuild — from recurring.

Tests:
- test_desktop_electron_pin.py: replace the static-path-matches-lockfile
  invariant with contracts that there is no hardcoded electronDist to drift, the
  builder script routes through the resolver, and the resolver uses Node module
  resolution + injects -c.electronDist.
- test_gui_command.py: install-failure self-heal continues to build; genuine
  (electron-never-staged) install failure still hard-fails; pack retries under
  the mirror even when the pre-fetch is blocked.

Salvages/supersedes the overlapping community work in #48003 (sitkarev),
#48012 (omegazheng), #48033 (james47kjv), and #48082.

Co-authored-by: sitkarev <59806492+sitkarev@users.noreply.github.com>
Co-authored-by: omegazheng <zheng@omegasys.eu>
Co-authored-by: james47kjv <220877172+james47kjv@users.noreply.github.com>

* fix(desktop): narrow Electron self-heal to real missing-dist failures

Follow-up on #48091 to remove the remaining misdiagnosis risk from the
installer/build fallback path (#46785 concern): only take the Electron
repair/retry path when Electron's package files are staged and dist is actually
missing/corrupt.

- main.py: add _electron_pkg_staged_missing_dist() and use it to gate install
  failure recovery; fail fast for unrelated npm install errors.
- main.py/install.sh/install.ps1: run cache purge + retry only when dist is
  missing; do not retry unrelated tsc/vite/build failures under an
  Electron-specific narrative.
- install.sh/install.ps1: tighten install-stage self-heal guard to require both
  package.json + install.js and missing dist.
- tests: add coverage that install failure hard-fails when Electron dist already
  exists, and update retry test to reflect the tightened recovery condition.

Validation:
- Python tests: 64 passed
- install.sh-related tests included in the run
- Real mac build on this machine:
  - npm ci at repo root: success
  - cd apps/desktop && npm run pack: success
  - electron-builder packaged darwin arm64 and used custom unpacked Electron dist

* refactor(desktop): trim electron self-heal helpers and comments

Deduplicate mirror-retry into _try_redownload_electron_dist / shell
counterparts; shorten wrapper and install-script commentary without
changing recovery semantics.

---------

Co-authored-by: sitkarev <59806492+sitkarev@users.noreply.github.com>
Co-authored-by: omegazheng <zheng@omegasys.eu>
Co-authored-by: james47kjv <220877172+james47kjv@users.noreply.github.com>
2026-06-17 18:48:35 -05:00
Teknium
f8098c6b6f
fix(desktop): resolve electronDist to the actual electron install location (#48081)
After the June lockfile regeneration (#46652) floated electron and reshuffled
npm workspace hoisting, the desktop pack fails with "The specified electronDist
does not exist". apps/desktop/package.json pointed electronDist at the repo
root (../../node_modules/electron/dist) while npm now installs electron nested
under apps/desktop/node_modules/electron. The two contradict, so a clean
install can never package the app (Windows + macOS).

- electronDist -> node_modules/electron/dist (resolved relative to apps/desktop,
  i.e. the workspace-local install npm actually produces).
- hermes_cli/main.py, scripts/install.sh, scripts/install.ps1: add a runtime
  electron-dir resolver that prefers apps/desktop/node_modules/electron and
  falls back to the root hoist, so dist checks + the mirror re-download work
  under either npm layout.
- patch-electron-builder-mac-binary.cjs: try the workspace-local Electron.app
  before the root hoist in the macOS binary-restore fallback (sibling site no
  PR touched).
- test: assert build.electronDist resolves to where the lockfile installs
  electron, so a future hoist change (root <-> nested) can't silently break it.

Salvages the overlapping work in #48003 (sitkarev), #48012 (omegazheng), and
#48033 (james47kjv).

Co-authored-by: sitkarev <59806492+sitkarev@users.noreply.github.com>
Co-authored-by: omegazheng <zheng@omegasys.eu>
Co-authored-by: james47kjv <220877172+james47kjv@users.noreply.github.com>
2026-06-17 18:08:01 -05:00
xxxigm
d1ecebcbfd
fix(desktop): re-download Electron binary via mirror when pack fails (#47266) (#47276)
* fix(desktop): re-download Electron binary via mirror when pack fails (#47266)

Since #38673 pinned build.electronDist to node_modules/electron/dist,
electron-builder reads the Electron binary straight from there and never
downloads it during `npm run pack`. That dist tree is only produced by the
electron package's postinstall (install.js) during `npm ci`. When that
download is blocked or throttled (GitHub's release host is unreachable in
some regions), the dist is missing and the build dies with:

    The specified electronDist does not exist: .../node_modules/electron/dist

The existing ELECTRON_MIRROR fallback in all three desktop-build paths
(scripts/install.ps1, scripts/install.sh, and `hermes desktop` in
hermes_cli/main.py) re-ran `npm run pack` with ELECTRON_MIRROR set — but
pack never downloads Electron anymore, so the mirror was never used and the
retry re-read the same missing dist. The fallback was effectively dead.

Drive the mirror through electron's own downloader instead:

- Add a dist-presence check + a downloader helper (Test-ElectronDist /
  Restore-ElectronDist, _electron_dist_ok / _restore_electron_dist,
  _electron_dist_ok / _redownload_electron_dist) that wipes a partial dist
  + the path.txt version marker (electron's install.js short-circuits on it)
  and re-runs `node install.js`, optionally via a mirror.
- On the first retry, repopulate a missing dist from the canonical source;
  on the mirror retry, re-fetch through npmmirror.com, then pack.
- Gate the re-download on the dist check so an unrelated build failure
  (tsc/vite) doesn't trigger a pointless ~200 MB refetch, and skip the final
  pack when the binary still can't be fetched instead of failing the same way.

* test(desktop): cover Electron dist re-download mirror fallback (#47266)

Add behavior coverage for the electronDist re-download fix:

- _electron_dist_ok across linux/win32/darwin, including the partial-dist
  case (dir present but binary missing) that makes the pinned electronDist
  fail.
- _redownload_electron_dist: no-op when the binary is present, bail when
  install.js is absent, wipe a stale dist + path.txt marker and run
  electron's downloader with ELECTRON_MIRROR injected, and report failure
  when the download still produces no binary.
- `hermes desktop`: the mirror fallback now drives electron's own downloader
  before re-running pack, and skips the final pack entirely when the binary
  can't be fetched.

Replaces the old mirror test that asserted the (now-fixed) dead behavior of
re-running `npm run pack` with ELECTRON_MIRROR set — pack never downloads
Electron under the pinned electronDist, so that retry could never help.
2026-06-16 15:40:55 -05:00
leo4226
f795513782
fix(windows): kill hermes before recreating venv to release _bcrypt.pyd lock (#45120)
On Windows, native Python extensions such as _bcrypt.pyd are loaded as
DLLs by any running hermes process. When the installer tries to recreate
the venv (Remove-Item -Recurse -Force "venv"), Windows denies the delete
because the DLL is still mapped into the running process.

Add a taskkill /F /T /IM hermes.exe call before the Remove-Item so any
hermes process tree is stopped first, releasing the file lock. A short
sleep gives the OS time to unload the image before deletion proceeds.

This mirrors the existing force_kill_other_hermes() guard already present
in the --update flow (update.rs), applying the same pattern to the full
reinstall/repair path through install.ps1.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-15 02:27:00 +00:00
xxxigm
573b964dc7 fix(installer): clear an unmerged git index before stashing on update
When an existing install at $INSTALL_DIR has an unmerged index (files in a
"needs merge" state left by a previously interrupted update), the update path
ran `git stash` then `git checkout <branch>`. On a conflicted index `git stash`
aborts with "could not write index" and `git checkout` then aborts with "you
need to resolve your current index first" — surfacing to desktop/bootstrap
users as `git checkout main failed (exit 1)` and failing the whole install at
the repository stage.

Mirror the `hermes update` Python path (#4735): detect unmerged entries with
`git ls-files --unmerged` and clear the conflict state with `git reset` before
stashing. Working-tree changes are still captured by the subsequent stash, so
nothing is discarded; only the index-level conflict markers are dropped, which
lets the checkout proceed.

Fixed in both installers (install.sh and install.ps1) so the Windows GUI
installer and the POSIX one share the same recovery behavior.
2026-06-13 05:19:44 -07:00
kshitijk4poor
9662b76d59 fix(install/windows): merge PATH in Update-ProcessPathForPackages instead of overwriting
Follow-up to the winget stale-registration fix. Update-ProcessPathForPackages
rebuilt $env:Path wholesale from the persisted User+Machine hives (plus winget's
Links dir), discarding any process-only PATH entries added earlier in the
installer run. Since the helper now runs after every package manager, that
wholesale replace is more likely to clobber a process-local entry than the
original winget-branch-only version was.

Merge instead: seed from the current process PATH, then append hive and
winget-Links entries not already present, with a case-insensitive,
order-preserving dedupe. Behaviour on a clean box is unchanged (the hive entries
are simply appended); the difference is that pre-existing process-only entries
now survive the refresh.
2026-06-11 12:49:58 +05:30
xxxigm
899acfe42f fix(install/windows): repair stale winget registration; refresh PATH after every package manager
When ripgrep/ffmpeg is missing, `winget install <id>` on a package winget
already has registered is treated as an upgrade: it finds no newer version and
exits 0x8A15002B (-1978335189, APPINSTALLER_CLI_ERROR_UPDATE_NOT_APPLICABLE)
without ensuring the binary is actually present. The installer only logged that
code and judged success by `Get-Command rg`, so a stale registration (files
removed outside winget, or a missing alias shim) became a permanent dead-end —
winget kept reporting "already installed" and the user could never reinstall.

Detect that exit code and retry once with `--force` to repair the registration
so the shim reappears.

Also refresh the process PATH after the choco and scoop fallbacks (not just
winget) via a shared helper, so a successful fallback install — or any install
on a box without winget — is no longer misreported as "not installed".
2026-06-11 12:47:59 +05:30
xxxigm
02f878ec5a
docs(windows): correct native data dir to %LOCALAPPDATA%\hermes (#42856)
* docs(windows): correct native data dir to %LOCALAPPDATA%\hermes

The Windows-native guide claimed a deliberate split where config, auth,
skills, and sessions live under %USERPROFILE%\.hermes. That is not what
the installer does: scripts/install.ps1 sets HERMES_HOME=%LOCALAPPDATA%\hermes,
so data actually lives in %LOCALAPPDATA%\hermes alongside the disposable
install (the hermes-agent\, git\, node\, bin\ subdirectories) — `hermes
config` confirms config.yaml/.env resolve there, not under %USERPROFILE%.

Update the data-layout table, the "split is deliberate" note, the env-var
and uninstall sections to describe the real layout: data and install share
the %LOCALAPPDATA%\hermes root, reinstall only replaces hermes-agent\, and
a full wipe targets %LOCALAPPDATA%\hermes (with %USERPROFILE%\.hermes kept
only as a legacy/WSL cleanup). Mention HERMES_HOME as the override knob.

* docs(windows): fix PATH + bin layout to match installer

The installer adds hermes-agent\venv\Scripts (where hermes.exe lives) to
User PATH and sets HERMES_HOME — not %LOCALAPPDATA%\hermes\bin. The \bin
dir holds Hermes's managed uv.exe, not a hermes.cmd shim. Correct the
install-step list and the data-layout table accordingly.

* fix(install): show real HERMES_HOME path in setup messages

The native Windows installer wrote config/env/skills under $HermesHome
(%LOCALAPPDATA%\hermes) but its success messages claimed ~/.hermes,
which doesn't exist on native Windows. Print the actual paths so a new
user can find their config, .env, and skills.
2026-06-09 14:11:20 -05:00
brooklyn!
ba44de06da
fix(install): self-heal a stuck Electron download (salvage of #42894) (#42998)
* fix(install): self-heal a stuck Electron download on the desktop build

The desktop build downloads Electron (~114MB) from GitHub. A corrupt cached
zip, or a blocked/throttled GitHub release host (the repeating "retrying" log),
hard-failed the install — and install.sh had no recovery at all while
install.ps1 / `hermes desktop` only purged the cache.

All three build paths now escalate on a failed `npm run pack`:
GitHub → purge corrupt electron-*.zip + stale *-unpacked and retry → one retry
via a public Electron mirror (npmmirror.com). @electron/get SHASUM-verifies the
download, and a user-pinned ELECTRON_MIRROR is always respected (never
overridden). Adds a bash clear_electron_build_cache()/_desktop_pack() to mirror
the existing PowerShell/Python helpers.

* test(install): cover the Electron mirror fallback

Verify `hermes desktop` falls back to a mirror when the cache purge finds
nothing, and that a user-pinned ELECTRON_MIRROR is respected (no extra attempt,
not overridden).

* docs(desktop): troubleshoot a stuck Electron download

Document the automatic cache-purge + mirror fallback, how to pin your own
ELECTRON_MIRROR, and how to clear a corrupt cached zip by hand.

* docs(install): correct the Electron mirror trust framing

The mirror-fallback comments and the desktop troubleshooting doc implied
`@electron/get`'s SHASUM check makes the npmmirror.com download safe against
tampering. It doesn't: the SHASUMS256.txt is fetched from the same mirror, so
the check guards against a corrupt/partial download, not a compromised mirror.

Reframe all four surfaces (install.sh, install.ps1, `hermes desktop`, and the
docs) to state the trust trade-off honestly — npmmirror.com is the de-facto
Electron community mirror, we only fall back to it after the canonical GitHub
download fails, and a user-pinned ELECTRON_MIRROR is never overridden. No
behavior change.

---------

Co-authored-by: xxxigm <tuancanhnguyen706@gmail.com>
2026-06-09 18:19:14 +00:00
Brooklyn Nicholson
e88116256c fix(update): scope git fetch to target branch
A bare `git fetch origin` (and `git fetch upstream`) pulls every ref. The
repo carries thousands of auto-generated branches, so on any
non-single-branch checkout the installer's update path and `hermes update`
spend minutes downloading the full branch list — long enough to stall the
desktop installer or trip the follow-up `git pull --ff-only`.

Scope every update-path fetch to the branch we actually compare/merge
against:
- scripts/install.sh: collapse the remote to single-branch and fetch only
  $BRANCH on the "existing install, updating" path.
- hermes_cli/main.py: fetch the resolved branch in the apply path, the
  --check path (upstream + origin), and the fork upstream-sync.

Tracking-ref updates still happen via git's opportunistic refspec, so the
later origin/<branch> rev-parse/rev-list checks are unaffected.

Tests assert the apply-path fetch is branch-scoped and never bare.
2026-06-08 15:24:31 -04:00
xxxigm
a5c12f5f59 fix(install): move broken checkout aside instead of deleting it
Review feedback (#40998): `rm -rf` / `Remove-Item -Recurse -Force` on the
install dir is destructive -- a user might still want whatever is there.
Rename the broken checkout to a timestamped `<dir>.broken-<ts>` backup and
re-clone fresh, so nothing is ever deleted. Transient cleanup of a clone
attempt that fails within the same run is left as-is.
2026-06-08 02:18:21 -07:00
xxxigm
fc0900d120 fix(install): re-clone interrupted (commit-less) checkout instead of failing
An interrupted previous clone leaves the install dir's .git present but with
no initial commit. rev-parse --is-inside-work-tree and git status both still
succeed there, so the installer entered the update path and ran `git stash`,
which aborts with "You do not have the initial commit yet" and failed the
desktop install at the "Cloning Hermes repository" stage.

- install.ps1: add a `git rev-parse --verify HEAD` probe to the repo-validity
  check so a commit-less checkout is treated as broken and re-cloned fresh.
- install.sh: mirror it at the top of clone_repo() — drop a partial checkout
  with no resolvable HEAD so the fresh-clone path handles it (POSIX parity).

Fixes #40998
2026-06-08 02:18:21 -07:00
Teknium
fd234bad62
fix(install): detect TLS cert-trust failures during npm install on Windows (#40588)
* fix: respect disabled auto-compaction on context overflow

Port from anomalyco/opencode#30749.

When compression.enabled is false, NO automatic compaction trigger may
fire. The proactive token-threshold paths (preflight + post-response
should_compress gate) already honoured the setting, but the three
provider-overflow recovery paths in the agent loop — long-context-tier
429, 413 payload-too-large, and context-overflow — called
_compress_context() unconditionally, silently compressing and rotating
the session against the user's explicit choice.

Add a single guard at the top of the overflow-recovery dispatch: when
compression is disabled and the error is one of those three overflow
classes, surface a terminal error (compaction_disabled: True) telling the
user to /compress manually, /new, switch to a larger-context model, or
reduce attachments. Manual /compress (force=True) is unaffected — it never
enters this loop.

Tests: new TestOverflowWithCompactionDisabled (413 + 400 overflow don't
compress when disabled; control case still compresses when enabled).
Existing overflow-recovery tests updated to enable compaction explicitly
(they verify the recovery fires); fixture defaults flipped to True to
match production (compression.enabled defaults to True).

* fix(install): detect TLS cert-trust failures during npm install on Windows

Corporate MITM proxies and missing root CAs surface as 'unable to get
local issuer certificate' while npm (most often Electron's install.js
postinstall) downloads over HTTPS. The installer surfaced this as an
opaque 'desktop workspace npm install failed (exit 1)', so users
misread it as a permissions/admin-rights problem (issue #38016).

Add a shared Show-NpmCertHint detector and route all three npm-install
failure paths (agent-browser global install, browser-tools workspace,
desktop workspace) through it. On a cert error it prints actionable
NODE_EXTRA_CA_CERTS / strict-ssl remediation; on any other failure it
stays silent.
2026-06-06 09:00:15 -07:00
Teknium
72eb42d9ec
feat(update): stash/restore by default + settable discard for non-interactive updates (reverts #38542, #39568) (#39645)
* Revert "fix(update): require managed marker before destructive clean"

This reverts commit c8e80cd0bf.

* Revert "fix(update): stop stash/restore from clobbering desktop source on managed clones (#38542)"

This reverts commit 8a19884bf3.

* chore(install): keep npm ci desktop-build fix after stash revert

The destructive-clean reverts (#38542/#39568) pulled the desktop
workspace install back to bare `npm install`. The npm ci -> npm install
fallback is orthogonal build-correctness (avoids the Windows
workspace-hoisting flake where install reports up-to-date against a
stale marker while node_modules is empty, breaking tsc -b). Preserve it.

* feat(update): settable stash-or-discard for non-interactive local changes

Adds updates.non_interactive_local_changes (stash | discard, default
stash). Governs ONLY non-interactive updates (desktop/chat app, gateway,
--yes) — interactive terminal updates always stash-and-ask, unchanged.

- config.py: new key under existing updates section; _config_version 26->27.
- main.py: _cmd_update_impl detects non-interactive (gateway/--yes/no-TTY),
  reads the setting; new _discard_stashed_changes() drops the stash
  (stash-and-drop, never reset --hard/clean -fd, so ignored paths survive).
  Post-pull restore site branches on it; the bail-out and up-to-date
  restores always preserve work.
- web_server.py + apps/desktop settings: exposes it as a stash/discard
  select (Advanced section, In-App Update Local Changes).
- docs + tests (discard drops, stash restores, interactive ignores setting,
  missing section defaults to stash).

* fix(install.ps1): stash/restore instead of reset --hard on Windows update

The PR reverted the destructive update path to stash/restore everywhere
except scripts/install.ps1, whose managed-clone update path still ran
`git reset --hard HEAD` before checkout — silently destroying agent-edited
tracked source on Windows (the same #38542 data-loss class the PR fixes).

- Replace `git reset --hard HEAD` with stash-before-checkout +
  restore-after-checkout, mirroring install.sh. Untracked files are
  included so agent-created dirs (e.g. tinker-atropos/) survive.
- Keep `core.autocrlf false` (it prevents the phantom CRLF dirt that made
  the stash necessary; it's also load-bearing for a clean restore).
- Wrap all three checkout modes (Commit/Tag/Branch); Branch case now uses
  `git pull --ff-only` so local commits are never clobbered.
- Only prompt to restore when a real console is attached (UserInteractive
  + non-redirected stdin/stdout + ConsoleHost); the desktop Update button
  and bootstrap have no usable console, so they default to restore and
  never hang on Read-Host.
- On restore conflict or a failed update, the stash is preserved with
  recovery instructions — work is never silently dropped.

Validated on Windows (PowerShell 5.1, git 2.54): AST parse clean;
E2E non-conflicting restore applies+drops cleanly with ignored paths
(node_modules) untouched; conflicting restore preserves the stash.

---------

Co-authored-by: alt-glitch <balyan.sid@gmail.com>
2026-06-05 17:30:10 +05:30
youngstar-eth
338f0b2234 fix(desktop): recover from corrupt Electron cache in bootstrap install (Windows)
Windows counterpart of #39127: scripts/install.ps1 `Install-Desktop` runs
`npm run pack` once and throws on the opaque ENOENT a corrupt cached Electron
download produces, with no recovery. Add `Clear-ElectronBuildCache` plus a
purge-and-retry-once on pack failure, mirroring the install.sh fix: remove the
cached electron-*.zip (%LOCALAPPDATA%\electron\Cache + ELECTRON_CACHE /
electron_config_cache overrides) and stale *-unpacked output, then retry so
@electron/get re-downloads with its own SHASUM verification.

Refs #37544.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-04 19:39:07 -07:00
ethernet
80672754a8 fix(docs): update all install instructions everywhere 2026-06-04 21:07:45 -04:00
ethernet
36f1cd7dea feat(installer): do shallow clones
no need to get the whole repo history :)
2026-06-04 17:49:16 -07:00
Teknium
475ecea3d7
fix(install): cap requires-python at <3.14 and pin UV_PYTHON to the venv (#38535)
uv selects the project Python from requires-python and from the UV_PYTHON
env var, both of which override an already-created venv on the next
'uv sync'. With no upper bound on requires-python, an inherited
UV_PYTHON=3.14 (or a fresh distro whose newest interpreter uv auto-picks)
silently recreated the installer's 3.11 venv at 3.14, where Rust-backed
transitives (pydantic-core) have no cp314 wheel and fall back to a maturin
source build that fails. This bit a Windows/WSL user with UV_PYTHON set in
their shell and a fresh WSL-arch box where uv auto-picked 3.14.

Two layers:
- pyproject: requires-python '>=3.11' -> '>=3.11,<3.14' (+ uv lock regen).
  uv now refuses a 3.14 interpreter with a clear error instead of attempting
  the maturin build. Backstop independent of the installer.
- install.sh / install.ps1: pin UV_PYTHON to the venv interpreter after
  creating it (in both the venv step and the deps step, since bootstrap runs
  those stages as separate processes). An inherited UV_PYTHON can no longer
  hijack the sync/pip tiers, so the install just works regardless of shell env.

Verified E2E: hostile UV_PYTHON=3.14 + uv venv --python 3.11 + uv sync now
installs into 3.11 with pydantic-core's 3.11 wheel; without the re-pin the
capped requires-python produces a legible incompatibility error rather than a
cryptic build failure.
2026-06-03 16:45:47 -07:00
Teknium
8a19884bf3
fix(update): stop stash/restore from clobbering desktop source on managed clones (#38542)
The stash/restore cycle in the update path was observed to clobber
freshly-pulled source files (apps/desktop/ deletion -> Vite
'[UNRESOLVED_ENTRY] Cannot resolve entry module index.html'). On a
managed clone the user never edits the source tree, so any 'dirty' state
is pure git artifact (CRLF renormalization, npm lockfile churn, files
left behind when a directory was deleted upstream such as
apps/bootstrap-installer/). Stashing that and re-applying it after a pull
is fragile and unnecessary.

- hermes update (hermes_cli/main.py): on a non-fork (managed) clone,
  discard working-tree dirt via reset --hard HEAD + clean -fd instead of
  stash/apply. Forks keep the stash machinery so intentional edits
  survive. Also pin core.autocrlf=false on Windows so the dirt is never
  created (mirrors install.ps1 #38239).
- install.sh: replace the update-path stash/restore dance with a hard
  reset to origin/<branch>; the installer is a managed-only entry point.
- install.sh + install.ps1 desktop stage: prefer 'npm ci' (wipes and
  reinstalls node_modules from the lockfile) over bare 'npm install',
  which can report 'up to date' against a stale marker while node_modules
  is empty -- leaving tsc unresolved so 'npm run pack' fails.

Tests: managed clone cleans instead of stashing; fork still stashes;
existing stash tests force the stash path explicitly.
2026-06-03 16:40:13 -07:00
Brooklyn Nicholson
1dca7c6207 fix(install): require Node >=20.19/22.12 for the desktop build
The "Build desktop app" install step failed with an opaque "exit code 1"
on machines with an old Node, and nothing in the logs explained it.

Reproduced: on Node 20.5.1, `npm run pack`'s `vite build` crashes with

  You are using Node.js 20.5.1. Vite requires Node.js version 20.19+ or 22.12+.
  SyntaxError: The requested module 'node:util' does not provide an
  export named 'styleText'

Vite 8 (rolldown) imports node:util.styleText, which doesn't exist before
Node 20.12, so the build dies before producing the app. The installer's
check_node / Test-Node accepted ANY pre-existing Node with no version
floor, so a too-old system Node was used for the build instead of the
bundled Node 22.

Add a version floor (^20.19 || >=22.12) to check_node (install.sh) and
Test-Node (install.ps1): a too-old system Node is replaced with the
Hermes-managed Node 22 LTS, and the desktop stage re-resolves Node so the
build always runs on a satisfying version. Declare the same range in
apps/desktop/package.json engines.

Verified: build succeeds on Node 22, fails on 20.5.1 with the error above;
the floor logic matches Vite's range across boundary versions (20.18/20.19,
21.x, 22.11/22.12).
2026-06-03 09:19:04 -05:00
Teknium
214b7e070f
fix(install.ps1): handle dirty worktree on Windows update (#38239)
Git for Windows defaults to core.autocrlf=true, which renormalizes the
repo's LF-only text files to CRLF in the working tree. On a managed,
never-user-edited clone this makes tracked files (.envrc, AGENTS.md,
agent/*.py, workflows) show as locally modified, so the update path's
bare git checkout aborts with 'Your local changes would be overwritten
by checkout' and the desktop bootstrap fails at stage=repository.

The bash installer already autostashes before checkout; the PowerShell
path had no dirty-tree handling at all and never pinned autocrlf.

Fix: (1) git reset --hard HEAD before fetch/checkout in the update path
to discard any pre-existing dirt, and (2) pin core.autocrlf=false on both
the update and fresh-clone paths so the dirt is never created again.
2026-06-03 06:45:48 -07:00
ethernet
43fd63b4b5 fix(windows): rip out unused submodule support in installer & docker & docs
we have no submodules anymore, so #37702 was kinda right, but we can just delete it entirely.
2026-06-03 03:01:37 -07:00