A pure-Python library and command-line tool for reading, writing, and patching
Electron .asar archives.
- List archive contents in plain, long, JSON, XML, or YAML format
- Extract an entire archive or a single file to disk
- Replace one file inside an archive without touching any other bytes
- Pack a directory tree into a new
.asararchive - Fully typed,
pathlib-native, requires Python ≥ 3.13
# From source with pip
pip install .
# Or with uv
uv pip install .After installation the pyasar command is available on your PATH.
pyasar <command> [options]
List the files stored in an archive.
# Plain listing (one path per line)
pyasar list app.asar
# With file sizes
pyasar list --long app.asar
pyasar list -l app.asar
# Structured output formats
pyasar list --format json app.asar
pyasar list --format xml app.asar
pyasar list --format yaml app.asarOptions
| Flag | Description |
|---|---|
-f FORMAT, --format FORMAT |
Output format: plain (default), long, json, xml, yaml |
-l, --long |
Shorthand for --format long — shows file sizes |
Example outputs
# plain
src/index.js
src/main.js
package.json
# long
SIZE PATH
--------------------------------------------------
1024 package.json
8192 src/index.js
4096 src/main.js
<!-- xml -->
<archive>
<file path="package.json" size="1024" />
<file path="src/index.js" size="8192" />
</archive># yaml
- path: package.json
size: 1024
unpacked: false
- path: src/index.js
size: 8192
unpacked: falseExtract the entire archive into a directory. The destination must not already exist.
pyasar extract app.asar ./output-dir
pyasar x app.asar ./output-dir # short aliasExtract a single file from an archive to a specific path on disk. Parent directories are created automatically.
pyasar extract-file app.asar src/index.js ./index.js
pyasar xf app.asar src/index.js ./index.js # short aliasReplace one file inside an archive. All other file bytes are left
byte-for-byte identical. By default the original archive is overwritten
in-place; use --output for a non-destructive patch.
# In-place replacement
pyasar replace app.asar src/index.js ./patched-index.js
# Write patched archive to a new file (original untouched)
pyasar replace app.asar src/index.js ./patched-index.js --output patched.asar
# Short aliases
pyasar r app.asar src/index.js ./patched-index.js -o patched.asarOptions
| Flag | Description |
|---|---|
-o OUTPUT, --output OUTPUT |
Write the patched archive to OUTPUT instead of overwriting the original |
Pack a directory tree into a new .asar archive.
pyasar pack ./my-app app.asar
pyasar p ./my-app app.asar # short alias
# Overwrite an existing archive
pyasar pack ./my-app app.asar --force
pyasar pack ./my-app app.asar -fOptions
| Flag | Description |
|---|---|
-f, --force |
Overwrite ARCHIVE if it already exists |
Batch-replace multiple files inside an archive in one atomic operation, driven by a YAML config file. All replacement files are validated before any writing begins — the archive is never partially modified on error.
python main.py patch patch.yamlConfig file format
source: path/to/input.asar # archive to read from
dest: path/to/output.asar # archive to write to (may equal source)
files:
- archive: src/index.js # path inside the archive to replace
source: ./new-index.js # replacement file on disk
- archive: package.json
source: ./package.json| Key | Description |
|---|---|
source |
Path to the input .asar archive |
dest |
Path for the patched output archive — may be the same as source for in-place patching |
files |
List of replacement entries |
files[].archive |
Archive-relative path of the file to replace (e.g. src/index.js) |
files[].source |
Disk path of the replacement file, resolved relative to the config file's directory |
Notes
sourceanddestmay be the same path; the operation is always atomic (all writes go to a temp file first, then the result is renamed into place).- Paths in
files[*].sourceare resolved relative to the config file's directory, so configs work regardless of the working directory. - The original archive is never touched until all validations pass.
Example output
Source : /path/to/app.asar
Dest : /path/to/app-patched.asar
Patches : 2
patched src/index.js ← new-index.js
patched package.json ← package.json
Done — wrote patched archive to '/path/to/app-patched.asar'
The main class in asar.archive. All path arguments accept both str and
pathlib.Path.
from asar import AsarArchive
# Open an existing archive
with AsarArchive.open("app.asar") as archive:
# List all file paths (sorted)
paths: list[str] = archive.list_files()
# Extract everything
archive.extract("./output-dir") # destination must not exist
# Extract a single file
archive.extract_file("src/index.js", "./index.js")
# Replace a file in-place
archive.replace_file("src/index.js", "./patched-index.js")
# Replace a file and write to a new archive
archive.replace_file(
"src/index.js",
"./patched-index.js",
output="patched.asar",
)Pack a directory into an archive
with AsarArchive.compress("./my-app") as archive:
# archive.asarfile is an in-memory BytesIO buffer
archive.asarfile.seek(0)
Path("app.asar").write_bytes(archive.asarfile.read())AsarArchive(filename, asarfile, files, baseoffset)| Parameter | Type | Description |
|---|---|---|
filename |
Path |
Path to the .asar file |
asarfile |
IO[bytes] |
Open binary file object (or BytesIO for in-memory) |
files |
dict |
Parsed header dictionary |
baseoffset |
int |
Absolute byte position where file data begins |
| Method | Description |
|---|---|
AsarArchive.open(filename) |
Open an existing .asar file for reading |
AsarArchive.compress(path) |
Pack a directory into an in-memory archive |
| Method | Description |
|---|---|
list_files() |
Return a sorted list of all archive-relative file paths |
extract(destination) |
Extract the entire archive to destination (must not exist) |
extract_file(archive_path, destination) |
Extract a single file to disk |
replace_file(archive_path, source_path, output=None) |
Replace one file; rewrites archive with updated offsets |
from asar import pack_asar, extract_asar
# Pack a directory into an archive
pack_asar("./my-app", "app.asar")
# Extract an archive into a directory
extract_asar("app.asar", "./output-dir")pyasar/
├── asar/
│ ├── __init__.py # Public exports: AsarArchive, pack_asar, extract_asar
│ ├── archive.py # AsarArchive class + pack_asar / extract_asar
│ └── cli.py # argparse CLI wired to asar.cli:main
├── main.py # Standalone entry-point (python main.py …)
├── pyproject.toml
└── uv.lock
| Requirement | Version |
|---|---|
| Python | ≥ 3.13 |
| PyYAML | ≥ 6.0.3 |
All other dependencies (json, struct, xml.etree.ElementTree, pathlib,
shutil, io, copy, logging) are part of the Python standard library.
This project is released under the MIT License — see the accompanying LICENSE file for the full text.