On this page
How to read this page
The CLI calls each isolated Windows environment a bottle, and prints that word in its own help and arguments. Type the name of an environment wherever a synopsis says <bottle>; in the prose it is called an environment.
- Progress and status lines start with
==>and go to standard error, so piping a command's output only gives you the data. - Failures print
grapple:followed by the reason, and exit non-zero. An unknown command prints the help and exits 1. - Leave out a required argument and you get bash's own message instead, which puts the script path and a line number in front of the usage line. The useful part is everything after the second colon.
- Examples below were captured from a real install. Home-folder paths are written as
/Users/you, and long paths are shortened with…where the middle adds nothing.
Basics
grapple help
grapple help
grapple -h | --helpPrints the full command list, grouped the same way this page is. Running grapple with no arguments does the same. An unrecognised command prints it too, and exits 1.
grapple version
grapple versionPrints the CLI version and nothing else. Worth checking after you set up an alias or a symlink.
$ grapple version
grapple 0.1.0grapple home
grapple homePrints the data directory in use — where engines, environments and downloads live. This honours GRAPPLE_HOME, so it is the reliable way to find out which directory a given shell is pointed at.
$ grapple home
/Users/you/Library/Application Support/GrappleEngines
An engine is a Wine build. The default, grapple-wine, is Grapple's own: Wine 11.0 built from the LGPL source CodeWeavers publishes for CrossOver, with DXMT included. The third-party wine-staging and wine-devel builds can still be installed, and Apple's Game Porting Toolkit tree can be linked in if you have one. Environments each name the engine they run, so you can keep several installed and move one game between them.
When an environment uses DXMT or DXVK, the CLI keeps a separate copy of the engine with that layer's DLLs dropped in, named grapple-wine+dxmt or grapple-wine+dxvk. It is an APFS clone, so it is instant and costs almost nothing on disk, and the base engine stays untouched. Those copies are made for you and are deliberately left out of listings.
Where the engine and D3DMetal come from is not fixed inside the CLI. It reads a small manifest — https://getgrapple.io/components.json, or whatever GRAPPLE_COMPONENTS_URL points at — which names the current version, address, size and sha256 of each one. Every download is checked against that sha256 before anything is unpacked, and a download that does not match is deleted and nothing is installed. If the manifest cannot be fetched the CLI says so and falls back to addresses built into it, so a website outage cannot stop an install.
grapple components
grapple components [--json]Prints exactly what Grapple would download for itself — the engine and D3DMetal — with the version, address, size and sha256 of each, and says whether that list came from the website, from the cached copy, or from the addresses built into this copy of the script. It touches nothing and installs nothing.
$ grapple components
Downloads listed at https://getgrapple.io/components.json
grapple-wine 1.0.0 230 MB sha256 cd69b68ede0c…
https://github.com/…/grapple-wine-1.0.0.tar.xz
d3dmetal 4.0-beta-2 15 MB sha256 73cf568c1430…
https://github.com/…/d3dmetal-4.0-beta-2.tar.xz
Every download is checked against its sha256 before it is unpacked.--json prints one object with a manifest block (the address it read, where the answer came from, and the error if there was one) and a components array of id, kind, version, url, sha256 and bytes.
grapple engine list
grapple engine list [--json]Lists every installed engine with the version its Wine reports, then what you can install and whether D3DMetal is present. Graphics-layer copies and the D3DMetal component are not engines you pick, so they are left out.
$ grapple engine list
gptk wine-7.7 (Game Porting Toolkit 1.1)
grapple-wine wine-11.0
Installable: grapple-wine (default), wine-staging, wine-devel.
D3DMetal (DirectX 9-12): installed, 4.0-beta-2The last line reads not installed (run: grapple d3dmetal install) when the component is missing, and names the version when the installed files record one. --json prints the machine readable form instead — see engine list --json.
grapple engine install
grapple engine install <grapple-wine | wine-staging | wine-devel>Downloads the engine, checks it, finds the Wine tree inside the download, moves it into the engines directory and clears the quarantine flag macOS puts on downloaded files. It finishes by printing the version the new engine reports.
For grapple-wine the address and the expected sha256 come from the components manifest, and the download is thrown away if it does not match. wine-staging and wine-devel are someone else's builds, looked up from the latest release of Gcenx's macOS Wine builds: that address changes with every release, so there is no published checksum to hold them to and the CLI says so instead of pretending it checked.
$ grapple engine install grapple-wine
==> Downloading grapple-wine 1.0.0
==> Checked grapple-wine engine (sha256 cd69b68ede0c…)
==> Extracting
==> Installed grapple-wine: wine-11.0You rarely run this yourself: grapple create and grapple set … engine install a missing engine on their own.
Fails with
engine 'grapple-wine' is already installed— remove it first, or pick another one.unknown engine 'x' (available: grapple-wine, wine-staging, wine-devel, gptk)could not look up the latest Wine release— the release lookup failed, usually a network problem.wine-stagingandwine-develonly.the grapple-wine engine download is not the file Grapple expected— what arrived did not match the publishedsha256. It is deleted and nothing is installed; run the command again.no Wine binaries found in the download— the archive did not containbin/wineserver.- Asking for
gptkis refused on purpose: it is Apple's own Wine tree, which Grapple does not download. The message points you atgrapple engine link gptk <dir>, and atgrapple d3dmetal installif what you actually wanted was DirectX 9-12 on Grapple's own engine.
grapple engine link
grapple engine link gptk <dir>Advanced. Symlinks a Wine tree you installed yourself into the engines directory under the name gptk. Point it at the tree itself or at any folder that contains one up to six levels down, such as an .app bundle. Only gptk can be linked — every other engine is managed by Grapple.
$ grapple engine link gptk /usr/local/opt/game-porting-toolkit
==> Linked gptk -> /usr/local/opt/game-porting-toolkitIf the folder you pass holds D3DMetal's files and no Wine tree at all, the command says so and installs it as the D3DMetal component instead, so an old habit of linking a toolkit's redist folder still does the right thing.
Fails with
only gptk can be linkednot a folder: /pathengine 'gptk' is already installedno Wine binaries (bin/wineserver) found in /path
grapple engine remove
grapple engine remove <name>Deletes an installed engine and any graphics-layer copies made from it (<name>+dxmt, <name>+dxvk). Environments are never touched.
$ grapple engine remove wine-devel
==> Removed engine wine-develFails with
bottle 'games' still uses engine 'wine-devel'— move that environment to another engine first.D3DMetal is a component, not an engine (see: grapple d3dmetal status)
D3DMetal
D3DMetal is Apple's DirectX 9-12 on Metal layer, from the Game Porting Toolkit. Grapple includes it. It is kept as its own versioned component next to the engines rather than inside one, so a single copy serves every environment, an engine stays a plain Wine build, and you can install a new version without rebuilding anything.
grapple d3dmetal status
grapple d3dmetal status [--json]Reports whether the component is installed, which version, where it is, and whether its files are all there. This is the default subcommand: plain grapple d3dmetal does the same.
$ grapple d3dmetal status
D3DMetal 2.1 (DirectX 9-12 on Metal)
path: /Users/you/Library/Application Support/Grapple/engines/d3dmetal/2.1
source: Apple Game Porting Toolkit
files: 28 files present (no checksum list)
layout: /Users/you/…/engines/d3dmetal/<version>, 'current' -> 2.1The files line is a verification result: it counts the files it found, or says N files verified when the install shipped a files.sha256 list and every checksum matched. Other versions you have installed are listed below it as also installed: lines.
When the component is not there:
$ grapple d3dmetal status
D3DMetal: not installed
install: grapple d3dmetal install (downloads it and checks its sha256)
or: grapple d3dmetal install <Game Porting Toolkit redist folder | d3dmetal-<version>.tar.xz>
without it, bottles on graphics=auto use DXMT (DirectX 10/11).If the version says “unknown”
The version comes from a VERSION file in the component, or from the folder it lives in. An install made before Grapple versioned this component has neither, and reports unknown and a path ending in d3dmetal-bridge. It still works. Tidy it with grapple d3dmetal migrate.
grapple d3dmetal install
grapple d3dmetal install [<path>] [--version <v>] [--force]Installs the component from a folder or a d3dmetal-<version>.tar.xz archive. It accepts a mounted Game Porting Toolkit disk image, its redist folder, that folder's lib, or a component directory made earlier — it searches up to six levels down for external/libd3dshared.dylib and works from whatever holds it.
With no path it downloads the version Grapple ships, taking the address and the expected sha256 from the components manifest, and refuses to unpack anything that does not match:
$ grapple d3dmetal install
==> Downloading D3DMetal 4.0-beta-2
==> Checked D3DMetal (sha256 73cf568c1430…)
==> Unpacking d3dmetal-4.0-beta-2.tar.xz
==> Installing D3DMetal 4.0-beta-2
==> D3DMetal 4.0-beta-2 installed: 36 files verified
==> Bottles set to graphics=auto or d3dmetal use it from now on.Setting D3DMETAL_URL overrides that address — and with it the checksum, because nothing published says what your own address should contain. Use it for your own builds and for testing.
The version is taken from --version if you gave one, then from a VERSION file, then from manifest.json, then from the name of the archive or folder — so Game_Porting_Toolkit_4.0_beta_2.dmg and d3dmetal-4.0-beta-2.tar.xz both become 4.0-beta-2. Nothing is copied into place until the files have been checked, and the previous version is only swapped out once the new one verifies.
| Flag | What it does |
|---|---|
| --version <v> | Names this install. Required when none of the sources above says which version it is. |
| --force | Replaces a version that is already installed. Refused while a game is using those files. |
Fails with
not found: /path,not a folder or an archive: /path,could not unpack /path,no D3DMetal files in <name>orno D3DMetal files under /path (expected external/libd3dshared.dylib, …)could not tell which D3DMetal version this is; add --version <v>D3DMetal 2.1 is already installed (…); use --force to replace itD3DMetal 2.1 is in use right now; quit your games firstthe D3DMetal files are incomplete, missing: …— the half-copied install is removed again, so nothing is left broken.unknown option -x (use --version <v>, --force)
grapple d3dmetal migrate
grapple d3dmetal migrate [--version <v>]Moves a pre-versioning d3dmetal-bridge folder into engines/d3dmetal/<version> and leaves a symlink behind at the old path, so environments that were already running keep finding it. One-off housekeeping; safe to run when there is nothing to do.
$ grapple d3dmetal migrate
==> nothing to migrate: /Users/you/…/engines/d3dmetal-bridge is not an old-style folderPass --version when the old folder does not record one, otherwise it is filed under unknown and the command says so.
Fails with
… does not hold D3DMetal files; move it aside yourselfD3DMetal is in use right now; quit your games and run this again…/engines/d3dmetal/2.1 already exists; pass --version <v> for this one
Windows environments
Each environment is an isolated Windows install — its own C: drive, registry, settings and logs — bound to one engine. Keeping a launcher and a game apart, or two games apart, means a broken install can only break itself.
grapple create
grapple create <name> [--engine grapple-wine|wine-staging|wine-devel|gptk] [--winver win10]Creates an environment and boots it once so Windows exists inside it. The engine is installed first if it is missing. First boot takes about a minute.
$ grapple create games
==> Creating bottle 'games' with grapple-wine (first boot takes a minute)
==> Bottle ready: /Users/you/Library/Application Support/Grapple/bottles/games/prefix/drive_cThe settings file it writes starts with graphics=auto, esync=on, msync=on, avx=on, hud=off and retina=off, plus the engine and Windows version you chose. All of them are described in configuration.
| Flag | Default | What it does |
|---|---|---|
| --engine <name> | grapple-wine | The engine to bind this environment to. Downloaded and installed on the spot if missing. |
| --winver <v> | win10 | The Windows version the environment reports, applied with winecfg. |
Fails with
bottle names cannot contain '/' or start with '.'bottle 'games' already existswineboot failed— the boot log is printed to standard error first, so you can see why.unknown option -x
grapple list
grapple list [--json]
grapple ls [--json]One line per environment, with the engine, the graphics layer and the Windows version it reports.
$ grapple list
BOTTLE ENGINE GRAPHICS WINDOWS
gptk-test gptk d3dmetal win10
steam grapple-wine dxvk win10The GRAPHICS column shows what the environment is set to. When that is auto, it also shows what auto resolves to right now, as auto:d3dmetal or auto:dxmt. Environments on the gptk engine always read d3dmetal.
With no environments, only the header row prints. --json adds two fields the table does not have — see list --json.
grapple info
grapple info <bottle>Prints where the environment's C: drive and logs are, then its settings file exactly as it is stored. Keys that are not in the file are simply at their default.
$ grapple info steam
Bottle: steam
C drive: /Users/you/Library/Application Support/Grapple/bottles/steam/prefix/drive_c
Logs: /Users/you/Library/Application Support/Grapple/bottles/steam/logs
Settings:
engine=grapple-wine
winver=win10
esync=on
msync=on
avx=on
retina=off
graphics=dxvk
env.DXVK_ASYNC=1
hud=on
env.WINE_D3D_CONFIG=renderer=vulkanFails with
no bottle named 'games' (see: grapple list)
grapple set
grapple set <bottle> <key> <value>Changes one setting. The value is validated first, then written, then applied: settings that live in the Windows registry (retina, winver) are written into it immediately, and changing engine or graphics stops everything running in the environment and updates the prefix for the new engine.
$ grapple set games graphics d3dmetal
==> games: graphics=d3dmetalThe full list of keys, their accepted values and their defaults is on the configuration page. In short:
keys: engine, graphics (auto|dxmt|dxvk|d3dmetal|wined3d), winver, esync, msync, avx, hud,
retina, dxr (on/off), winedebug, env.VARTwo keys change each other. Setting engine to gptk also sets graphics=d3dmetal, because that engine brings its own. Moving off gptk to an engine while D3DMetal is not installed resets graphics to auto, so the environment is never left pointing at a layer that is not there.
Fails with
unknown setting 'x', followed by the list of keys above.esync must be on or off— and the same formsync,avx,hud,retinaanddxr.graphics must be auto, wined3d, dxmt, dxvk or d3dmetalgptk bottles always use d3dmetalD3DMetal is not installed yet: grapple d3dmetal installbad variable name— anenv.key whose name is not a valid shell variable.unknown engine 'x' (available: grapple-wine, wine-staging, wine-devel, gptk)— settingengineto something that is neither installed nor installable.
grapple unset
grapple unset <bottle> <key>Removes a key so its default applies again, and writes that default back where the setting lives in the registry. If the engine is not installed, the default is applied the next time the environment is used instead, and the command says so.
$ grapple unset games graphics
==> games: graphics unset
$ grapple unset games winver
==> games: winver is not setFails with
'games' is running; quit its programs first (or: grapple kill games)— forengineandgraphicsonly.unknown setting 'x', as forset.
grapple duplicate
grapple duplicate <bottle> <new-name>
grapple dup <bottle> <new-name>Copies an environment, games and all. On an APFS disk this is a clone: instant, and it takes no extra space until one of the two copies changes. Useful before an experiment you might want to undo. The environment must be stopped, and the copy is made under a hidden name so a half-finished one never shows up as an environment.
$ grapple duplicate games games-test
==> Duplicated bottle 'games' to 'games-test'Fails with
bottle 'games-test' already exists'games' is running; quit its programs first (or: grapple kill games)bottle names cannot be empty, contain '/' or start with '.'
grapple rename
grapple rename <bottle> <new-name>
grapple mv <bottle> <new-name>Renames a stopped environment and rewrites any launchers grapple shortcut put in ~/Applications that pointed at it, so double-clicking them still works. Each launcher it changes is printed.
$ grapple rename games library
==> Updated /Users/you/Applications/Steam.app
==> Renamed bottle 'games' to 'library'Fails with
the bottle is already called 'library'bottle 'library' already exists'games' is running; quit its programs first (or: grapple kill games)
grapple userdir
grapple userdir <bottle> [--all]Prints the macOS path of the Windows user profile inside the environment — where saves, settings and AppData live. The profile is named after your macOS account on Wine builds and crossover on CrossOver-derived engines, which is why it is worth asking rather than guessing.
$ grapple userdir steam
/Users/you/Library/Application Support/Grapple/bottles/steam/prefix/drive_c/users/you--all prints every profile it finds, the one the engine uses first. Profiles without an AppData folder are only considered when there is nothing better, and Public is never listed.
Fails with
bottle 'games' has no Windows user profile yet— nothing has run in it.unknown option -x
grapple open
grapple open <bottle>Opens the environment's C: drive in Finder. No output.
grapple delete
grapple delete <bottle>
grapple rm <bottle>Stops everything in the environment and moves the whole folder to the Trash as grapple-<name>-<timestamp>. Nothing is deleted outright, because your saves are in there — empty the Trash yourself once you are sure.
$ grapple delete games-test
==> Moved bottle 'games-test' to the TrashRunning programs
grapple run
grapple run [--detach] [--graphics <layer>] <bottle> <file.exe | .msi | .bat | .lnk | C:\path | epic:<AppName> | gog:<id>> [args...]Runs a program inside the environment with all of its settings applied. The path can be a macOS path, absolute or relative, or a Windows one like C:\Program Files (x86)\Steam\steam.exe. Installers and scripts are handled for you: .msi goes through msiexec /i, .bat and .cmd through cmd /c, .lnk through start. The working directory is set to the program's own folder, because plenty of games load their files relative to it.
It also takes the ids grapple programs prints for launcher games — epic:<AppName> and gog:<id> — and resolves each one to the executable that launcher recorded, so you can start a game without going through its launcher.
Everything the program prints is written to a log named after it, in the environment's logs folder, and — unless you detach — echoed to your terminal as well.
$ grapple run games "C:\Program Files (x86)\Steam\steam.exe"
==> Running steam.exe in 'games' (log: /Users/you/…/bottles/games/logs/steam.exe.log)| Flag | What it does |
|---|---|
| --detach | Start the program in the background and return straight away. Output goes to the log only. This is what grapple steam and the launchers grapple shortcut makes both use. |
| --graphics <layer> | Run this one program on auto, dxmt, dxvk, d3dmetal or wined3d, whatever the environment is set to. The environment's own setting is not changed. This is how grapple epic starts the Epic launcher on DXVK, the only layer it runs on, without dragging the games in that environment onto it. A program started by the one you run inherits its layer. |
Anything after the program is passed to it unchanged.
Fails with
file not found: /path— a macOS path that does not exist. Windows paths are passed straight through to Wine, so a typo in one shows up as a Windows error instead.no bottle named 'games' (see: grapple list)engine 'wine-devel' is not installed (run: grapple engine install wine-devel)D3DMetal is not installed (run: grapple d3dmetal install)— the environment is set to D3DMetal but the component is missing.
grapple programs
grapple programs <bottle> [--json]Lists what you would call a game. Steam titles come from Steam's own manifests, across every library folder Steam knows about, so the names are the real ones and no helper executable can pretend to be a game. Epic games come from the launcher's own manifests and GOG games from the goggame-<id>.info file in every install folder, so those names are the real ones too. After those come Start Menu and Desktop shortcuts and programs installed outside a launcher, found up to three levels inside Program Files and Program Files (x86).
$ grapple programs steam
steam:1374490 RuneScape: DragonwildsSteam games print as steam:<appid>, Epic games as epic:<AppName> and GOG games as gog:<id>, each with the title; everything else prints as a full path. Redistributables, Proton and Steam Linux Runtime entries, installers, updaters, patchers, crash handlers, Steam's own plumbing and Windows' bundled programs are filtered out. This is a heuristic, not a database: something odd can still slip through, and a game that hides its executable deep inside a folder can be missed.
The Steam half of this has been used against real installed games. The GOG and Epic half reads those launchers' own manifest files, and nobody has yet downloaded or installed a game through either launcher on a Mac, so neither those listings nor grapple run <bottle> gog:<id> has been exercised against a real game. If you get there first, tell us.
--json gives one object per entry — see programs --json.
grapple shortcut
grapple shortcut <bottle> <program | steam | gog | epic> [App Name]Writes a small double-clickable app into ~/Applications that runs one command. Pass steam, gog or epic for that launcher, or the path of a program inside the environment. The app is named after the last argument, or after the program's file name, or after the launcher — Steam, GOG Galaxy, Epic Games Launcher.
$ grapple shortcut games steam
==> Created /Users/you/Applications/Steam.appThe launcher is a two-line shell script inside an app bundle that calls the same grapple you ran, by its full path. Move or rename the CLI and the launcher stops working; rename the environment with grapple rename and it is fixed for you. It has no icon of its own.
Fails with
file not found: /path— the same checkgrapple rundoes.no bottle named 'games' (see: grapple list)
grapple tool
grapple tool <bottle> <winecfg | regedit | taskmgr | cmd | explorer | control | uninstaller>Opens one of Wine's built-in Windows tools in the environment: the configuration panel, the registry editor, the task manager, a command prompt, the file explorer, the control panel or the program uninstaller. notepad is accepted too, though the help does not list it.
cmd takes over your terminal and runs in the foreground. The others start in the background with their output discarded, so you get your prompt back immediately.
$ grapple tool games regeditFails with
unknown tool 'x'
grapple winetricks
grapple winetricks <bottle> <verbs...>Runs winetricks -q against the environment, for installing Windows runtimes a game asks for — vcrun2022, d3dx9, corefonts and so on. Winetricks is not bundled; you install it yourself. It is a thin pass-through: Grapple sets the environment up and hands over.
$ grapple winetricks games vcrun2022 corefontsFails with
winetricks not found (install with: brew install winetricks)
grapple kill
grapple kill <bottle>Stops everything running in one environment, and nothing anywhere else. This is the way out of a game that will not quit, and what duplicate and rename ask you to do first.
$ grapple kill games
==> Stopped everything in 'games'If nothing was running it prints ==> Nothing running and exits successfully either way.
Launchers
grapple steam
grapple steam <bottle> [steam args...]Installs the Windows Steam client the first time and launches it every time. On first run it downloads Valve's installer into the cache and runs it silently. On every run it does three things Steam needs to work under Wine on a Mac:
- Swaps Steam's
steamwebhelper.exefor Grapple's wrapper, keeping Valve's binary assteamwebhelper_real.exe. Steam's interface is a Chromium view that paints a black window under Wine unless it runs CPU-rendered in a single process; the wrapper adds those flags. This is re-checked at every launch, because a Steam update puts the original back. - Disables Steam's overlay DLL through
WINEDLLOVERRIDES, because it deadlocks games on DXMT. Anything you already had in that variable is kept. - Starts Steam detached with
-no-cef-sandbox -cef-disable-gpu -noverifyfiles. The last flag stops Steam restoring its own webhelper over the wrapper on every start.
$ grapple steam games
==> Downloading Steam installer
==> Installing Steam (silent)
==> Running steam.exe in 'games' (log: /Users/you/…/bottles/games/logs/steam.exe.log)Arguments after the environment name go to steam.exe unchanged, which is how you start a specific game:
$ grapple steam games -applaunch 1374490Fails with
Steam install failed; try: grapple run games "…/cache/SteamSetup.exe"— the silent install did not producesteam.exe; running the installer with its window visible usually shows why.missing …/steamwebhelper.exe (build it from steamwebhelper-wrapper.c)— the wrapper is not next to the CLI.grapple doctorchecks for this.
GOG and Epic
grapple gog and grapple epic work in the same shape as grapple steam: one command that installs the launcher into an environment the first time and starts it every time after, with their games listed by grapple programs as gog:<id> and epic:<AppName>. Neither has flags of its own; anything after the environment name is handed to the launcher.
What we have tested: both install without you clicking through anything, and both open at their sign-in screen on an Apple Silicon Mac. What we have not: signing in, and downloading, installing or playing a game bought on either store. Those are still yours to find out — on the Free plan, if you want to try before you pay.
grapple gog
grapple gog <bottle> [GOG Galaxy args...]Installs GOG Galaxy into the environment the first time and launches it every time. The first run fetches GOG's full installer — about 340 MB — and runs it silently; GOG's small web installer is only used as a fallback, because it insists on showing you a wizard.
GOG Galaxy's own first start then fails, on any machine: it registers its background service and gives up waiting for it, leaving an error box on screen. Grapple expects that. It waits for the client to finish registering the service, clears the error away and starts GOG Galaxy again, which is the start that works. So the one thing you will see that looks wrong is a GOG error box that disappears by itself. Every later run goes straight to the launcher.
$ grapple gog games
==> Downloading GOG Galaxy (about 340 MB)
==> Installing GOG Galaxy (silent; this takes a few minutes)
==> Restarting GOG Galaxy now that its background service is registered
==> Running GalaxyClient.exe in 'games' (log: /Users/you/…/bottles/games/logs/GalaxyClient.exe.log)Fails with
GOG Galaxy install failed; try: grapple run games "…/cache/setup_galaxy.exe"— the silent install did not produceGalaxyClient.exe; running the installer with its window visible usually shows why.
grapple epic
grapple epic <bottle> [launcher args...]Installs the Epic Games Launcher into the environment the first time and launches it every time. There is more to that first run than the name suggests, so expect it to take a few minutes — about four on the Mac we tested it on.
- The installer Epic ships is a 90 MB package that puts down a bootstrap launcher, whose only job is to download the real one: about 1 GB, on first run only. Left alone under Wine that swap never completes — the bootstrap and Epic's updater service hand the install back and forth for ever — so Grapple lets the download finish, stops the loop and installs the full build itself.
- Epic's launcher refuses to draw anything until its updater service is registered, which Windows does from an elevated relaunch that Wine never reports. Grapple registers it directly instead, which takes a second and no elevation.
- Epic builds its installers with custom actions that hang for ever under Wine rather than failing. Grapple arranges for them to fail; the packages carry on without them.
- The launcher draws its own window with DirectX 11 and is fussy about which graphics layer gives it a device: only DXVK does here. So
grapple epicstarts the launcher itself on DXVK and leaves the environment alone, which means your games keep the layer they want. A game you start from the launcher's own button inherits DXVK from it; one you start withgrapple run <bottle> epic:<AppName>gets the environment's layer.
$ grapple epic games
==> Downloading the Epic Games Launcher (about 90 MB)
==> Installing the Epic Games Launcher (silent)
==> Registering the Epic Games updater service
==> Downloading the rest of the Epic Games Launcher (about 1 GB)
==> Installing it
==> Running EpicGamesLauncher.exe in 'games' (log: /Users/you/…/bottles/games/logs/EpicGamesLauncher.exe.log)If the download is interrupted, run the command again and it picks up where it left off. Every later run skips all of this and goes straight to the launcher.
Fails with
could not download the Epic Games Launcher from …— Epic's installer URL was unreachable.Epic Games Launcher install failed; …— the silent install did not produceEpicGamesLauncher.exe; the message names the command that runs the installer by hand.
Diagnostics
Both commands are covered at length, with what to do about what they tell you, on the troubleshooting page.
grapple doctor
grapple doctor [--json]Checks the install and prints one OK or WARN line per check: Rosetta 2, free disk space, every engine, D3DMetal, the Steam helper and any leftover half-finished copies. It changes nothing. It is also the one command that runs without Rosetta 2, so it can tell you that Rosetta 2 is what you are missing.
$ grapple doctor
OK rosetta: Rosetta 2 is running
OK disk: 130 GB free
WARN engines: none installed (run: grapple engine install grapple-wine)
OK d3dmetal: not installed (graphics=auto uses DXMT; install it for DirectX 9-12)
OK steam-wrapper: /Applications/Grapple.app/Contents/Resources/steamwebhelper.exe
OK partial: no leftover partial engine copiesThat is a fresh install with no engine yet. On a working one the engine lines name each engine and the Wine version it reports:
OK engine grapple-wine: wine-11.0
OK engine grapple-wine+dxvk: wine-11.0
OK d3dmetal: 4.0-beta-2 installed (28 files present (no checksum list))doctor always exits 0, even with warnings. Use --json and read the ok field if you need a pass/fail — see doctor --json.
grapple report
grapple report <bottle> [--out <path.zip>]Collects the diagnostics for one environment into a zip and prints the path it wrote, so you can pipe it somewhere. It gathers the settings file, system information, Grapple's own logs for that environment, the tail of the Steam client's logs, and recent Unreal and Unity game logs and crash reports from the last week — newest first, up to a 20 MB budget. Your macOS user name and home folder are replaced with <user> in every text file before the zip is made.
$ grapple report games
==> Collecting diagnostics for 'games'
==> Wrote /Users/you/Desktop/grapple-report-games-20260923-101500.zip
/Users/you/Desktop/grapple-report-games-20260923-101500.zip| Flag | Default | What it does |
|---|---|---|
| --out <path.zip> | ~/Desktop/grapple-report-<name>-<timestamp>.zip | Where to write the zip. A relative path is resolved against the current directory, and any missing parent folders are created. |
Fails with
--out needs a pathcould not write /path.zipno bottle named 'games' (see: grapple list)
Machine-readable output
Several commands take --json. This is not a convenience bolted on the side: it is the interface the Grapple app itself uses. The app runs the same script you do and parses exactly these objects, which is why they are stable and why anything you build on them sees what the app sees.
Each prints a single line to standard output. Progress and errors stay on standard error, so grapple list --json 2>/dev/null is always either valid JSON or nothing. The samples below are wrapped across lines to fit, and home paths shortened.
grapple list --json
$ grapple list --json
[{"name":"steam","engine":"grapple-wine","graphics":"dxvk","winver":"win10",
"path":"/Users/you/Library/Application Support/Grapple/bottles/steam",
"running":false,
"userdir":"/Users/you/Library/Application Support/Grapple/bottles/steam/prefix/drive_c/users/you"}]| Field | Type | Meaning |
|---|---|---|
| name | string | The environment's name, which is its folder name. |
| engine | string | The engine it is bound to, as stored. Empty if the settings file does not name one, in which case grapple-wine is used. |
| graphics | string | The resolved layer — never auto. What auto currently means (d3dmetal or dxmt) is what you get here. |
| winver | string | The Windows version it reports, e.g. win10. |
| path | string | The environment's folder on disk. |
| running | boolean | True when something is running in it right now. Detected without touching anything. |
| userdir | string | The Windows user profile in use, or empty when the environment has no profile yet. |
With no environments the output is [].
grapple engine list --json
$ grapple engine list --json
[{"name":"gptk","version":"wine-7.7 (Game Porting Toolkit 1.1)","linked":false,
"path":"/Users/you/…/engines/gptk"},
{"name":"grapple-wine","version":"wine-11.0","linked":false,
"path":"/Users/you/…/engines/grapple-wine"}]| Field | Type | Meaning |
|---|---|---|
| name | string | The engine name you pass to create, set and remove. |
| version | string | What its Wine reports. Empty when the engine is present but will not run. |
| linked | boolean | True when this engine is a symlink to a tree elsewhere, i.e. it was added with engine link. |
| path | string | The engine's folder inside the data directory. |
Graphics-layer copies, half-finished copies and the D3DMetal component are left out.
grapple doctor --json
$ grapple doctor --json
{"checks":[{"name":"rosetta","status":"ok","message":"Rosetta 2 is running"},
{"name":"disk","status":"ok","message":"130 GB free"},
{"name":"engine grapple-wine","status":"ok","message":"wine-11.0"},
{"name":"d3dmetal","status":"ok","message":"not installed (graphics=auto uses DXMT; install it for DirectX 9-12)"},
{"name":"steam-wrapper","status":"ok","message":"/Applications/Grapple.app/Contents/Resources/steamwebhelper.exe"},
{"name":"partial","status":"ok","message":"no leftover partial engine copies"}],
"d3dmetal":{"installed":false,"version":"","path":""},
"ok":true}| Field | Type | Meaning |
|---|---|---|
| checks[].name | string | Which check: rosetta, disk, engines, engine <name>, d3dmetal, d3dmetal-layout, steam-wrapper, partial. |
| checks[].status | string | "ok" or "warn". There is no error level: doctor reports, it does not fail. |
| checks[].message | string | The human-readable detail, the same text the plain output prints. |
| d3dmetal.installed | boolean | Whether the D3DMetal component was found. |
| d3dmetal.version | string | Its version, or "unknown" for an install that does not record one. Empty when not installed. |
| d3dmetal.path | string | Where it is. Empty when not installed. |
| ok | boolean | True when no check warned. |
grapple programs --json
$ grapple programs steam --json
[{"type":"steam","name":"RuneScape: Dragonwilds","appid":"1374490","bottle":"steam"}]| Field | Type | Meaning |
|---|---|---|
| type | string | "steam", "epic" or "gog" for a title read from that launcher's own manifests, "program" for a shortcut or an executable found on disk. |
| name | string | The title from the launcher, or the file name without its extension. |
| appid | string | Launcher entries only: the Steam app ID, Epic AppName or GOG id, as a string. Pass it back as steam:/epic:/gog: to grapple run. |
| path | string | The full macOS path of the .exe or .lnk. Steam entries do not carry one; Epic and GOG entries do, when the launcher recorded where it installed the game. |
| bottle | string | The environment it was found in — the name you passed in. |
grapple d3dmetal status --json
Documented above with the command itself, but its --json is worth repeating here: the app reads it to decide whether to offer DirectX 12.
$ grapple d3dmetal status --json
{"installed":true,"version":"4.0-beta-2","path":"/Users/you/…/engines/d3dmetal/4.0-beta-2",
"source":"Apple Game Porting Toolkit","sourceVersion":"","framework":"",
"files":"28 files present (no checksum list)","verified":true,
"layout":"versioned","url":""}| Field | Type | Meaning |
|---|---|---|
| installed | boolean | Whether the component was found. |
| version | string | Its version, or "unknown" when the files do not record one. |
| path | string | Where it is, with symlinks resolved. |
| source | string | Always "Apple Game Porting Toolkit" when installed. |
| sourceVersion | string | The toolkit version from the install's manifest, if it has one. |
| framework | string | The D3DMetal.framework version from the manifest, if it has one. |
| files | string | The verification summary, the same text status prints. |
| verified | boolean | False when files are missing or a checksum did not match. |
| layout | string | "versioned" for engines/d3dmetal/<version>, "legacy" for the older folder. |
| url | string | Where a bare d3dmetal install would fetch from: D3DMETAL_URL when you set it, otherwise the address built into this copy of Grapple. Reported without touching the network. |