Repository navigation
G9 3.1.0
Download
| System | File | Updates |
|---|---|---|
| Windows 10/11 (x64) | G9-Setup-3.1.0.exe — per user, no administrator rights |
Automatic: G9 checks these releases, downloads in the background and asks before installing |
| macOS 12+ (Apple silicon) | G9-3.1.0-mac-arm64.dmg |
G9 tells you when a release is out; download it and replace the app (the app is not signed with an Apple Developer ID yet, so macOS cannot install updates into it) |
| macOS 12+ (Intel) | G9-3.1.0-mac-x64.dmg |
As above |
| Linux (x64) | G9-x86_64.AppImage — keep this file name: updates replace the file in place |
Automatic, like Windows |
| Debian/Ubuntu (x64) | G9_3.1.0_amd64.deb |
G9 tells you; install the new .deb with your package manager |
The installers are not code-signed: Windows SmartScreen and macOS Gatekeeper ask once before the first start. See the README for what to click, and for the browser extension (loaded once, updated by G9 itself).
latest.yml, latest-mac.yml and latest-linux.yml are the update metadata G9 reads; the .zip and .blockmap files are for the updater.
What changed in 3.1.0
Why. G9 is published on GitHub, so a person should be able to download it for their system, and
an installed G9 should find and install new versions by itself — with nothing to configure, never
in the middle of a run, and with the browser extension following the app.
Packages. desktop/scripts/build.mjs (replacing build-win.mjs) builds for the system it runs on:
Windows G9-Setup-<v>.exe (NSIS, per user, as before); macOS G9-<v>-mac-{arm64,x64}.dmg and .zip
(the ZIP is the updater's format); Linux G9-x86_64.AppImage and G9_<v>_amd64.deb, each with its
latest*.yml. What was Windows-only, found by reading every platform branch: the background policies
(registry; now reported as not applicable elsewhere), the browser search (App Paths in the registry;
macOS and Linux got their own candidate lists), the MCP config folders (%APPDATA%; now the system's
own), the extension picker's wording, the login item (Linux has none in Electron; G9 writes an XDG
autostart entry), the app menu (macOS needs one for Cmd+Q and editing shortcuts) and the Chrome for
Testing pin (now pinned for win64, mac-x64, mac-arm64 and linux64).
- macOS is unsigned (the owner's choice: no Apple Developer ID yet). It is signed ad hoc so it
runs on Apple silicon, Gatekeeper asks once, and — because Squirrel.Mac installs only into a
Developer-ID-signed app — updates are manual: G9 checks, downloads nothing, and says where the
release is. An app started from a disk image or Downloads runs translocated; the wizard refuses to
write that path into MCP configs. - The AppImage keeps one name. electron-updater's
AppImageUpdater(read in its source) replaces
$APPIMAGEin place only when the file name has no version; otherwise it writes a new versioned
file and deletes the old one — breaking every MCP entry that named it. So the AppImage is published
asG9-x86_64.AppImage. - An AppImage cannot be named from inside. It runs from a mount that vanishes when it exits, so
MCP entries, the daemon and scheduled runs start$APPIMAGE <G9_HOME>/bin/g9-run.mjs <script>in
Node mode (lib/runtime.mjs). Found in a clean Ubuntu container: the AppImage's AppRun adds
--no-sandboxin front of the arguments when unprivileged user namespaces are off (Ubuntu 24.04,
containers), and Node then exits "bad option: --no-sandbox" (9). G9 passes--no-sandboxafter the
script instead, where AppRun leaves it, and the bootstrap drops it. - The .deb installs to
/opt/G9; replacing it needs root, so it is updated by hand too.
Updates. The default source is now the official releases (updateMode: 'official',
electron-updater's github provider on ImanKari/G9BrowserAgent: releases.atom, /releases/latest,
then that tag's latest*.yml, read from github.com, not the rate-limited API). custom keeps the old
https feed (a 3.0.x settings file with a URL and no mode stays on it); off never checks. New in the
prompt and in Settings: Skip this version (never downloaded again) and Install when I quit G9;
a manual install mode (macOS, .deb) with Open the release page. A 404 or an empty release list now
reads "no G9 release is published at the official releases yet" instead of a stack trace.
The extension follows the app, and now always. The daemon and its clients carry an installId (the
AppImage file or the resource folder) beside the version, so "same version, different install" is seen
too. Found by the real update test: after an update the app refreshed G9_HOME/extension, but the
extension was reconnecting at that moment, so the reload was sent to nobody (reloadSent: 0) and the
browser kept the old version. The daemon now catches it up: when an extension older than the daemon
says hello and G9_HOME/extension already holds the daemon's version, it asks that extension once to
reload, after its calls in flight (at most 60 s); never in a loop (daemon.test).
The real update test (desktop/test/update-e2e.mjs, no fakes). It installs an older build of the
same source (built with --as-version), gives it a temporary G9_HOME and a random port, serves the new
packages from a local feed that can corrupt or cut off a download, and drives the running app through
its own window over the DevTools protocol. Scenarios: corrupt download refused by SHA-512; a download
cut off 50 times still completes; skip; a busy daemon (a launched browser) defers the install; install
when I quit; the new version starts with a new daemon, G9_HOME/extension refreshed and reloaded, no
mismatch; the reload waits for a call in flight; macOS's manual notice; and, against GitHub, a fresh
install with no update settings finds, downloads and installs the published release. Windows, on the
owner's workstation: 3.0.1 → 3.1.0, 20 of 20 checks (installed silently into a temporary folder,
uninstalled at the end). Linux AppImage, in a clean Ubuntu container: 13 of 13. In the pipeline, on hosted machines
(Azure DevOps run 487, 3.0.999 → 3.1.0 through the local feed): Windows 20 of 20, Linux AppImage 20 of
20 (with Edge 153 for the extension), macOS 6 of 6 (the manual path). It also found:
build.mjs staging node_modules through a junction lost electron-updater's dependencies in the
packed app (fs-extra; the older build could not update at all) — now copied, and
verify-artifacts.mjs checks every production dependency is inside each app.asar; and main.mjs
loaded Electron when run by plain Node (the smoke test printed two lines).
CI/CD. azure-pipelines.yml (§7): Validate on Windows, Linux and macOS; Package and the real update
test on each; Release (checks, SHA-256 sums, notes from this entry); on main only, Publish: tag in
Azure, mirror to GitHub, and a GitHub Release whose assets are verified before and after it is made
public (setup/github-release.mjs). The first run on main (488) stopped at its first Publish step,
before anything was tagged or published: PowerShell read "… is $head: bump …" as a scope-qualified
variable and refused to parse the script. It is ${head} now, and every pwsh block of the pipeline
was put through PowerShell's own parser. Publish runs only on main, so no branch run could have
shown it.
Also. The desktop Engines view refreshes the tab list when it opens, so a tab opened since no
longer shows as "Untitled" (seen in the README pictures). The MCP shim connects after initialize (an agent was
registered as "mcp-agent" in a race, seen as a flaky self-test on CI). .gitattributes makes every text
file LF: the extension suite compared file text and failed on a Windows checkout with autocrlf. The
unit runner's PASS counter had a control character where \b was meant, and counted nothing. The live
window.open test waits for the first paint before it clicks (a hosted CI machine received the click
before the button was hit-testable). On the hosted Ubuntu 24.04 machine a browser the daemon launched
aborted at start: without unprivileged user namespaces Chromium falls back to its setuid sandbox
helper, and that image's msedge-sandbox was not root-owned with mode 4755. The pipeline now restores
the helper as the package installs it (so G9's browsers run sandboxed there, as on a desktop), and
engine/launch.js turns that abort into the fix (sandboxHint), with G9_BROWSER_NO_SANDBOX=1 as an
explicit, Linux-only opt-out for containers (engine.test). The README is new, for people who use G9, in English and Persian
(README.fa.md), with pictures from a real run (setup/docs-screenshots.mjs, docs/assets/); the
technical reference moved to docs/REFERENCE.md. License: MIT (LICENSE).
Version 3.1.0 in package.json, extension/manifest.json, desktop/package.json and
desktop/package-lock.json.
SHA-256
5e658d0e782a382dc46d2d4473a03fae746fdd45f3c9e2cd43930f62e5db7a28 G9-3.1.0-mac-arm64.dmg
55b837dde63f89febf61d13496c0e5c24d96744fdf85122f6e1d370842e8df21 G9-3.1.0-mac-arm64.dmg.blockmap
2c8619b1ce7cef2498ee8682802b3cdcb6d9887ca3890511753476acdf0ce414 G9-3.1.0-mac-arm64.zip
73895d7969073c19f1be18ff1fa365c28afe0af9730ef13b138cd090d593f979 G9-3.1.0-mac-arm64.zip.blockmap
c61b242e2eefc36bb9a2b77eb40428ffd04155059d3ba1e4fbac264651ac1f83 G9-3.1.0-mac-x64.dmg
abd8e21365410e4fcc20738d2a3edf6f0cc93cef323d91d041213eac476589cd G9-3.1.0-mac-x64.dmg.blockmap
34c8e2badffa7bb1b8d3721a373856c0f7cb6290402ce6a281890831b8512ea1 G9-3.1.0-mac-x64.zip
2c66b6639595185ddabebe3939dc6f30b53bca689dfec23ea896f1a3590b89c3 G9-3.1.0-mac-x64.zip.blockmap
a6524868d112fd8c8c7276b7f7c304bc3555177705dd7f81c1b3d6aa83c2df6b G9-Setup-3.1.0.exe
4e959888a5a1b2df5ed76ea317d3adcfd1355a8a6024fdcfc6ed8de55675e2f7 G9-Setup-3.1.0.exe.blockmap
097ca22aad26970d602d4ab481de915657fe7fbe711f7f3548b5399b90fdee1a G9-x86_64.AppImage
4b37ca2c3dec1cd8b201369542e7ca7d2f7e2d5904ebbc46e2175cc3c3aebde4 G9_3.1.0_amd64.deb