docs(website): add worked guides and keep Pages publishing manual (#925)
* fix(pages): reserve publishing for the REA website * docs(website): add worked guides and agent prompt examples * ci(website): check relevant PRs and publish only on demand * docs(website): clarify IDA coverage and analysis session lifetime
This commit is contained in:
parent
b9292516f4
commit
4f25ee33dc
|
|
@ -1,30 +1,14 @@
|
|||
name: Documentation site
|
||||
name: Documentation build
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- ".nvmrc"
|
||||
- ".github/workflows/pages.yml"
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "docs/**"
|
||||
- "package.json"
|
||||
- "package-lock.json"
|
||||
- ".nvmrc"
|
||||
- ".github/workflows/pages.yml"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: pages-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
|
||||
group: documentation-build-${{ github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
env:
|
||||
HUSKY: "0"
|
||||
|
|
@ -33,6 +17,7 @@ jobs:
|
|||
build:
|
||||
name: Build documentation
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 10
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
|
|
@ -41,23 +26,3 @@ jobs:
|
|||
cache: npm
|
||||
- run: npm ci
|
||||
- run: npm run docs:build
|
||||
- uses: actions/upload-pages-artifact@v4
|
||||
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
||||
with:
|
||||
path: docs/.vitepress/dist
|
||||
|
||||
deploy:
|
||||
name: Publish documentation
|
||||
if: github.event_name != 'pull_request' && github.ref == 'refs/heads/main'
|
||||
needs: build
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
pages: write
|
||||
id-token: write
|
||||
environment:
|
||||
name: github-pages
|
||||
url: ${{ steps.deployment.outputs.page_url }}
|
||||
steps:
|
||||
- uses: actions/configure-pages@v5
|
||||
- uses: actions/deploy-pages@v4
|
||||
id: deployment
|
||||
|
|
|
|||
|
|
@ -0,0 +1,28 @@
|
|||
name: Website checks
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches: [main]
|
||||
paths:
|
||||
- "website/**"
|
||||
- "scripts/verify-website.py"
|
||||
- ".github/workflows/**"
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: website-check-${{ github.event.pull_request.number || github.ref }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
check:
|
||||
name: Website checks
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
steps:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Check website links and publishing configuration
|
||||
run: python3 scripts/verify-website.py
|
||||
|
|
@ -21,6 +21,8 @@ jobs:
|
|||
steps:
|
||||
- name: Check out the repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Check website links and publishing configuration
|
||||
run: python3 scripts/verify-website.py
|
||||
- name: Configure Pages
|
||||
uses: actions/configure-pages@v5
|
||||
- name: Upload the website
|
||||
|
|
|
|||
|
|
@ -28,7 +28,8 @@
|
|||
{
|
||||
"files": [
|
||||
"scripts/electron-active-hook*.cjs",
|
||||
"tests/conformance/readiness/electron/*.js"
|
||||
"tests/conformance/readiness/electron/*.js",
|
||||
"website/public/examples/notes-electron/*.js"
|
||||
],
|
||||
"rules": {
|
||||
"no-var-requires": "off"
|
||||
|
|
|
|||
|
|
@ -0,0 +1,106 @@
|
|||
"""Check local website references and the sole GitHub Pages publisher."""
|
||||
|
||||
from html.parser import HTMLParser
|
||||
from pathlib import Path
|
||||
import re
|
||||
import sys
|
||||
from urllib.parse import unquote, urlsplit
|
||||
import xml.etree.ElementTree as ET
|
||||
|
||||
|
||||
class HtmlReferences(HTMLParser):
|
||||
"""Collect document IDs and references without fetching external URLs."""
|
||||
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
self.ids = set()
|
||||
self.references = []
|
||||
self.duplicate_ids = []
|
||||
|
||||
def handle_starttag(self, tag, attrs):
|
||||
attributes = dict(attrs)
|
||||
identifier = attributes.get("id")
|
||||
if identifier is not None:
|
||||
if identifier in self.ids:
|
||||
self.duplicate_ids.append(identifier)
|
||||
self.ids.add(identifier)
|
||||
for attribute in ("href", "src"):
|
||||
reference = attributes.get(attribute)
|
||||
if reference is not None:
|
||||
self.references.append(reference)
|
||||
|
||||
|
||||
def check_site(site):
|
||||
"""Check assets, HTML fragment destinations and SVG syntax."""
|
||||
pages = {}
|
||||
errors = []
|
||||
references = 0
|
||||
for path in sorted(site.rglob("*.html")):
|
||||
parser = HtmlReferences()
|
||||
parser.feed(path.read_text(encoding="utf-8"))
|
||||
pages[path] = parser
|
||||
if site / "index.html" not in pages:
|
||||
errors.append("website/public/index.html is missing")
|
||||
|
||||
for path, page in pages.items():
|
||||
label = path.relative_to(site)
|
||||
for identifier in page.duplicate_ids:
|
||||
errors.append(f"{label}: duplicate ID {identifier!r}")
|
||||
for reference in page.references:
|
||||
url = urlsplit(reference)
|
||||
if url.scheme or url.netloc:
|
||||
continue
|
||||
references += 1
|
||||
if url.path.startswith("/"):
|
||||
errors.append(f"{label}: use a relative site reference: {reference}")
|
||||
continue
|
||||
target = (path.parent / unquote(url.path)).resolve() if url.path else path
|
||||
if not target.is_relative_to(site):
|
||||
errors.append(f"{label}: reference leaves the website: {reference}")
|
||||
continue
|
||||
if target.is_dir():
|
||||
target /= "index.html"
|
||||
if not target.is_file():
|
||||
errors.append(f"{label}: missing local target: {reference}")
|
||||
elif target in pages and url.fragment and unquote(url.fragment) not in pages[target].ids:
|
||||
errors.append(f"{label}: missing HTML fragment: {reference}")
|
||||
|
||||
for path in sorted(site.rglob("*.svg")):
|
||||
try:
|
||||
ET.parse(path)
|
||||
except ET.ParseError as error:
|
||||
errors.append(f"{path.relative_to(site)}: invalid SVG XML: {error}")
|
||||
return len(pages), references, errors
|
||||
|
||||
|
||||
def check_publisher(root):
|
||||
"""Detect a second Pages action that could replace the website."""
|
||||
expected = root / ".github/workflows/website-pages.yml"
|
||||
workflows = root / ".github/workflows"
|
||||
deploy_action = re.compile(r"^\s*-?\s*uses:\s*['\"]?actions/deploy-pages@", re.MULTILINE)
|
||||
publishers = [
|
||||
path
|
||||
for path in sorted(workflows.iterdir())
|
||||
if path.suffix in (".yml", ".yaml") and deploy_action.search(path.read_text(encoding="utf-8"))
|
||||
]
|
||||
if publishers != [expected]:
|
||||
actual = ", ".join(str(path.relative_to(root)) for path in publishers) or "none"
|
||||
return [f"Pages must have one publisher, website-pages.yml; found: {actual}"]
|
||||
return []
|
||||
|
||||
|
||||
def main():
|
||||
"""Run the same checks locally, on website PRs and before publishing."""
|
||||
root = Path(__file__).resolve().parent.parent
|
||||
pages, references, errors = check_site(root / "website/public")
|
||||
errors.extend(check_publisher(root))
|
||||
if errors:
|
||||
print("Website checks failed:\n" + "\n".join(errors), file=sys.stderr)
|
||||
return 1
|
||||
print(f"Verified {pages} HTML pages, {references} local references and SVG XML.")
|
||||
print("website-pages.yml is the sole GitHub Pages publisher.")
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
# REA website
|
||||
|
||||
An English static website with explanatory figures and a DX-Ball investigation.
|
||||
An English static website with explanatory figures, worked guides and a DX-Ball investigation.
|
||||
The public files are in `website/public/`. The site uses HTML, CSS and a small
|
||||
script for copying code and following the assembly-to-C comparison; it has no
|
||||
build step or npm dependencies.
|
||||
|
|
@ -20,6 +20,8 @@ Open <http://127.0.0.1:4173/>. Refresh the browser after editing a file.
|
|||
- `public/index.html`: product introduction and DX-Ball overview.
|
||||
- `public/showcase/dx-ball/index.html`: sound-pan investigation and project status.
|
||||
- `public/get-started/index.html`: agent setup, first CLI result and provider guides.
|
||||
- `public/guides/`: a guide hub and native, JavaScript/Electron and browser examples.
|
||||
- `public/examples/`: downloadable Electron source and an interactive Notes browser app.
|
||||
- `public/assets/`: shared styles, interactions, favicon and explanatory figures.
|
||||
|
||||
Navigation and assets use relative paths, so the same files work at the local
|
||||
|
|
@ -29,7 +31,18 @@ root and a GitHub Pages project path such as `/rea/`.
|
|||
|
||||
Keep the copy direct and specific. Explain the task and the result before listing
|
||||
tool names. Setup commands and runtime requirements should match the released
|
||||
package.
|
||||
package. Keep the core learning path inside the site. Source, issue and evidence
|
||||
reference links may point to the corresponding repositories.
|
||||
|
||||
[evidence/guide-examples.md](evidence/guide-examples.md) records the published
|
||||
REA package, example digests and observed results behind the guides. The Notes
|
||||
Electron fixture is for static analysis; Electron is not a prerequisite for
|
||||
following that example. The separate Notes browser app runs in the local preview
|
||||
and constructs a CSV download after fetching its JSON data.
|
||||
|
||||
Agent terminals show example prompts, not transcripts of previous
|
||||
investigations. A short cursor animation starts when the prompt enters view and
|
||||
respects reduced-motion preferences. All prompt text is present without JavaScript.
|
||||
|
||||
DX-Ball figures and findings refer to the linked 7 October 2026 checkpoint,
|
||||
commit `a55dca27ec0a07018c1b2c95ae2be027f7d8c3c4`. Update those links and figures
|
||||
|
|
@ -41,23 +54,47 @@ Evidence. [evidence/dx-ball-sound-pan.md](evidence/dx-ball-sound-pan.md) records
|
|||
their provenance and the scope of the validation claims. The original executable
|
||||
and complete private Evidence records are not website assets.
|
||||
|
||||
The diagrams are maintained as SVG source. Initial layout references were
|
||||
The homepage and DX-Ball overview diagrams are maintained as SVG source. Initial layout references were
|
||||
created with the built-in image generation tool; [figures.md](figures.md) retains
|
||||
their prompts and the current asset notes. Figures provide an overview;
|
||||
REA requests, assembly and C remain selectable HTML text. On narrow
|
||||
screens, the diagrams scroll horizontally and can also be opened at full size.
|
||||
The worked guides use semantic HTML flows that stack vertically on smaller
|
||||
screens. The Electron teaching example uses CommonJS, matching its preload
|
||||
code; the scoped lint override admits `require` only in that example directory.
|
||||
|
||||
## GitHub Pages
|
||||
|
||||
`.github/workflows/website-pages.yml` prepares and deploys only `website/public`.
|
||||
It is manually triggered and restricted to `main`; ordinary pushes and pull
|
||||
requests do not publish the site.
|
||||
It is the sole Pages publisher, manually triggered and restricted to `main`;
|
||||
ordinary pushes and pull requests do not publish the site.
|
||||
|
||||
Website checks run only for pull requests that change `website/`, the verification
|
||||
script or workflow definitions. They check local links, HTML fragments, SVG XML
|
||||
and the single Pages publisher without installing npm dependencies. The same
|
||||
checks run before each manual deployment. You can also run them locally:
|
||||
|
||||
```sh
|
||||
python3 scripts/verify-website.py
|
||||
```
|
||||
|
||||
`.github/workflows/pages.yml` is a separate, manual-only VitePress build. It
|
||||
has no Pages artifact upload, deployment job or deployment permissions. This
|
||||
prevents documentation updates from replacing the public website. The legacy
|
||||
publishing job has been removed, so enabling this build workflow cannot publish
|
||||
the old site.
|
||||
|
||||
After the site is approved and merged, select **GitHub Actions** under the
|
||||
repository's **Settings → Pages → Build and deployment**. Then run **Publish REA
|
||||
website** from the Actions tab on `main`. The workflow uses the `github-pages`
|
||||
environment and the official Pages actions.
|
||||
|
||||
The equivalent CLI command is:
|
||||
|
||||
```sh
|
||||
gh workflow run website-pages.yml --repo morluto/rea --ref main
|
||||
```
|
||||
|
||||
Local development does not change Pages settings or run the deployment workflow.
|
||||
Any environment protection rules are configured separately when publication is
|
||||
approved.
|
||||
|
|
|
|||
|
|
@ -0,0 +1,78 @@
|
|||
# Guide example evidence
|
||||
|
||||
The native guide reuses the recorded DX-Ball observations in
|
||||
[dx-ball-sound-pan.md](dx-ball-sound-pan.md). No new native-provider claim is
|
||||
introduced by this iteration.
|
||||
|
||||
## Notes Electron example
|
||||
|
||||
This six-file, source-owned teaching fixture is in
|
||||
`website/public/examples/notes-electron/`. It is a static analysis target, not
|
||||
an installed Electron distribution. Its graph was obtained using the released
|
||||
`rea-agents` 4.1.0 package, Node.js 24.18.0 on Linux x64, on 7 October 2026:
|
||||
|
||||
```sh
|
||||
rea analyze-javascript-application \
|
||||
/absolute/path/to/website/public/examples/notes-electron --json
|
||||
```
|
||||
|
||||
- Evidence ID: `ev_a7e68cbdbd52ee15a959047774d1164d385e3e5ceb704084395639d97fee093d`.
|
||||
- Root artifact SHA-256: `dae5292f341b4f0dfd5be0846d4cfa0d4821f3e062e970cc1cd568c18c3fb37c`.
|
||||
- Four JavaScript files parsed, zero parse failures.
|
||||
- One exposed API member, one literal IPC channel, one renderer invocation,
|
||||
one main-process handler, one paired invocation, zero ambiguous invocations.
|
||||
- The guide's result excerpt comes from `normalized_result.summary.ipc`.
|
||||
- The `exposes`, `invokes` and `handles` graph edges retain their static
|
||||
inference authority and source locations. The guide's end-to-end explanation
|
||||
also uses the visible source in `renderer.js`, `main.js` and `csv.js`; it does
|
||||
not claim that the complete button-to-file path was observed executing.
|
||||
|
||||
The source was formatted before this result was recorded. Re-run analysis
|
||||
when the example files change.
|
||||
|
||||
## Notes browser example
|
||||
|
||||
The three-file browser app is in `website/public/examples/notes-web/`.
|
||||
A dedicated headless Chrome profile opened it at
|
||||
`http://127.0.0.1:4173/examples/notes-web/`. REA 4.1.0 passively inspected that
|
||||
existing page with an 8-second window, accessibility text and JSON body shapes.
|
||||
A separate Playwright controller clicked the button during the capture and
|
||||
saved the resulting CSV; REA did not perform the click.
|
||||
|
||||
- Evidence ID: `ev_7df243651c257e41f525e4225cbca5427467cf1e7b4b5b624ab41ab5717fe9c6`.
|
||||
- Window: `2026-10-07T09:38:43.492Z` to
|
||||
`2026-10-07T09:38:51.531Z`.
|
||||
- One observed `GET` to `/examples/notes-web/notes.json`.
|
||||
- Status `200`, MIME type `application/json`, script initiator `export.js`.
|
||||
- Body shape: `/notes` array, `/notes/*/id` number, `/notes/*/title` string.
|
||||
- The independent browser run downloaded `notes.csv` and displayed
|
||||
“Exported 2 notes to notes.csv.” CSV construction is explained from source,
|
||||
not inferred solely from the request capture.
|
||||
|
||||
The CLI guide uses a 10-second window to give the reader time to perform the
|
||||
action. Capture only covers requests after attachment. The example request
|
||||
path differs under a project prefix such as `/rea/`, while the relative fetch
|
||||
and export behavior stay the same.
|
||||
|
||||
The guide's follow-up CLI commands were also exercised against the real page:
|
||||
`list-browser-targets` returned its exact page target, and
|
||||
`inspect-web-page --include-script-sources` returned the loaded `export.js`
|
||||
source. Discovery Evidence: `ev_31abb33e7707d47625ac909ab9256c705d03a31ee6bcd643fa96730e72469162`. Script capture
|
||||
Evidence: `ev_aeb777df405c09dc1bbdd4c185a5af2452abaa3cb493683c296c5afa97efbc11`.
|
||||
|
||||
Full local outputs and the browser verification harness are under
|
||||
`/tmp/rea-website-guides/` in the development environment, not website assets.
|
||||
|
||||
## Example source digests
|
||||
|
||||
| File | SHA-256 |
|
||||
| -------------------------------------- | ------------------------------------------------------------------ |
|
||||
| `examples/notes-electron/csv.js` | `6875aeecc1d3e6c1ff5b888a199c26b377d812d5b10e5fb4025e192a206b0018` |
|
||||
| `examples/notes-electron/index.html` | `a31df9b29eb9d479f913c7806364609019eedd1d19cda386cc24706d5068082d` |
|
||||
| `examples/notes-electron/main.js` | `cc5b00911bfe428754d94ca40e8c0f354cfaae86f716873423aa5a62b9e0379a` |
|
||||
| `examples/notes-electron/package.json` | `bfef2a7cba39a989715f56e304ca2578807159f28bdb040b75893bb341caaee7` |
|
||||
| `examples/notes-electron/preload.js` | `3cb814f2a0822c2fef437ba722374a2eabf6c6795905f41656d9c33fe8d5d9c4` |
|
||||
| `examples/notes-electron/renderer.js` | `b24aa22ed969c0849dd9da1102935a6b36b96bae46870234bdea10039dd9a5a0` |
|
||||
| `examples/notes-web/export.js` | `66f5c2501bfca72eb743a1fffbdce9a58d295e69ca66f19562e9b0fa9fc189a6` |
|
||||
| `examples/notes-web/index.html` | `97bb4729dcd838de8adae7a00eb49df0dbe8a7b73becc1c7d2a6662825013064` |
|
||||
| `examples/notes-web/notes.json` | `9fc1155e251cfbed09dadca6b195507072fffe3f602564812d644ab27c422282` |
|
||||
|
|
@ -55,3 +55,22 @@ function initializeStepComparisons() {
|
|||
}
|
||||
|
||||
initializeStepComparisons();
|
||||
|
||||
function initializeAgentTerminals() {
|
||||
if (!("IntersectionObserver" in window)) return;
|
||||
const observer = new IntersectionObserver(
|
||||
(entries) => {
|
||||
entries.forEach((entry) => {
|
||||
if (!entry.isIntersecting) return;
|
||||
entry.target.classList.add("is-visible");
|
||||
observer.unobserve(entry.target);
|
||||
});
|
||||
},
|
||||
{ threshold: 0.25 },
|
||||
);
|
||||
document.querySelectorAll(".agent-terminal").forEach((terminal) => {
|
||||
observer.observe(terminal);
|
||||
});
|
||||
}
|
||||
|
||||
initializeAgentTerminals();
|
||||
|
|
|
|||
|
|
@ -707,6 +707,209 @@ pre code {
|
|||
.small-heading {
|
||||
margin-top: 1.8rem;
|
||||
}
|
||||
.agent-terminal {
|
||||
margin-block: 1.5rem;
|
||||
overflow: hidden;
|
||||
border: 1px solid #303d4a;
|
||||
border-radius: 6px;
|
||||
background: #202933;
|
||||
color: #f1f4f8;
|
||||
}
|
||||
.terminal-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 1rem;
|
||||
padding: 0.65rem 1rem;
|
||||
border-bottom: 1px solid #384551;
|
||||
color: #bdc8d4;
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.terminal-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.7rem;
|
||||
}
|
||||
.terminal-dots {
|
||||
display: flex;
|
||||
gap: 4px;
|
||||
}
|
||||
.terminal-dots i {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: #81909e;
|
||||
}
|
||||
.agent-terminal .copy-button {
|
||||
color: #d6dee7;
|
||||
}
|
||||
.agent-terminal .copy-button:hover {
|
||||
background: #384551;
|
||||
color: white;
|
||||
}
|
||||
.agent-terminal .terminal-prompt {
|
||||
margin: 0;
|
||||
padding: 1.4rem;
|
||||
color: #f1f4f8;
|
||||
font-family: "SFMono-Regular", Consolas, "Liberation Mono", monospace;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.9;
|
||||
}
|
||||
.prompt-symbol {
|
||||
color: #9bbfe5;
|
||||
margin-right: 0.65rem;
|
||||
}
|
||||
.terminal-cursor {
|
||||
display: inline-block;
|
||||
width: 0.5em;
|
||||
height: 1em;
|
||||
margin-left: 0.4em;
|
||||
vertical-align: -0.12em;
|
||||
background: #9bbfe5;
|
||||
}
|
||||
.agent-terminal.is-visible .terminal-cursor {
|
||||
animation: terminal-blink 1s step-end 6;
|
||||
}
|
||||
@keyframes terminal-blink {
|
||||
50% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
.workflow-figure {
|
||||
margin: 1.7rem 0;
|
||||
border: 1px solid var(--line);
|
||||
border-radius: 5px;
|
||||
padding: 1.4rem;
|
||||
background: #fafbfc;
|
||||
}
|
||||
.figure-title {
|
||||
margin: 0 0 1.3rem;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 600;
|
||||
color: var(--ink);
|
||||
}
|
||||
.flow-steps {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 1.9rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
list-style: none;
|
||||
}
|
||||
.flow-steps li {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
min-width: 0;
|
||||
}
|
||||
.flow-steps li:not(:last-child)::after {
|
||||
content: "→";
|
||||
position: absolute;
|
||||
right: -1.4rem;
|
||||
top: 2.1rem;
|
||||
color: #8191a2;
|
||||
}
|
||||
.flow-label {
|
||||
display: block;
|
||||
margin-bottom: 0.65rem;
|
||||
color: var(--muted);
|
||||
font-size: 0.75rem;
|
||||
}
|
||||
.flow-node {
|
||||
display: block;
|
||||
padding: 0.8rem;
|
||||
background: white;
|
||||
border: 1px solid #ced9e5;
|
||||
border-radius: 4px;
|
||||
color: var(--accent);
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
font-weight: 500;
|
||||
}
|
||||
.flow-note {
|
||||
display: block;
|
||||
margin-top: 0.65rem;
|
||||
font-size: 0.8rem;
|
||||
line-height: 1.55;
|
||||
color: var(--muted);
|
||||
}
|
||||
.flow-result {
|
||||
margin: 1.3rem 0 0;
|
||||
padding: 0.9rem 1rem;
|
||||
border-left: 2px solid var(--accent);
|
||||
background: #edf2f8;
|
||||
color: var(--ink);
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.workflow-figure figcaption {
|
||||
margin-top: 1rem;
|
||||
color: var(--muted);
|
||||
font-size: 0.8rem;
|
||||
}
|
||||
.guide-index {
|
||||
padding-bottom: 3rem;
|
||||
}
|
||||
.guide-entry {
|
||||
display: grid;
|
||||
grid-template-columns: 160px minmax(0, 1fr);
|
||||
gap: 2.5rem;
|
||||
padding-block: 2.5rem;
|
||||
border-top: 1px solid var(--line);
|
||||
}
|
||||
.guide-entry .eyebrow {
|
||||
margin: 0;
|
||||
}
|
||||
.guide-entry h2 {
|
||||
font-size: 1.75rem;
|
||||
}
|
||||
.guide-entry p {
|
||||
max-width: 660px;
|
||||
color: var(--muted);
|
||||
}
|
||||
.guide-route {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.5rem;
|
||||
align-items: center;
|
||||
margin-bottom: 1.25rem;
|
||||
font-size: 0.85rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
.guide-route span {
|
||||
padding: 0.2rem 0.55rem;
|
||||
background: var(--soft);
|
||||
border-radius: 3px;
|
||||
color: #414b55;
|
||||
}
|
||||
.example-files {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 0.65rem 1.25rem;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
font-size: 0.9rem;
|
||||
}
|
||||
.example-files li + li {
|
||||
margin: 0;
|
||||
}
|
||||
.example-app {
|
||||
max-width: 720px;
|
||||
padding-block: 3.5rem;
|
||||
}
|
||||
.example-app h1 {
|
||||
font-size: 3rem;
|
||||
}
|
||||
.example-notes {
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
margin-block: 2rem;
|
||||
}
|
||||
.example-notes li {
|
||||
padding-block: 0.8rem;
|
||||
border-bottom: 1px solid var(--line);
|
||||
}
|
||||
.example-status {
|
||||
margin-block: 1.5rem 1rem;
|
||||
}
|
||||
.sr-only {
|
||||
position: absolute;
|
||||
width: 1px;
|
||||
|
|
@ -737,6 +940,17 @@ pre code {
|
|||
.rea-query-pair {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
.flow-steps {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 2rem;
|
||||
}
|
||||
.flow-steps li:not(:last-child)::after {
|
||||
content: "↓";
|
||||
top: auto;
|
||||
bottom: -1.7rem;
|
||||
right: auto;
|
||||
left: 0.85rem;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
|
|
@ -827,6 +1041,13 @@ pre code {
|
|||
grid-template-columns: 1fr;
|
||||
gap: 0.25rem;
|
||||
}
|
||||
.guide-entry {
|
||||
grid-template-columns: 1fr;
|
||||
gap: 0.7rem;
|
||||
}
|
||||
.workflow-figure {
|
||||
padding: 1rem;
|
||||
}
|
||||
.step-selector {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
|
|
@ -839,6 +1060,12 @@ pre code {
|
|||
}
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: reduce) {
|
||||
.agent-terminal.is-visible .terminal-cursor {
|
||||
animation: none;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 360px) {
|
||||
.header-inner {
|
||||
flex-wrap: wrap;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
function toCsv(notes) {
|
||||
const quote = (value) => `"${String(value).replaceAll('"', '""')}"`;
|
||||
return (
|
||||
[
|
||||
"id,title",
|
||||
...notes.map((note) => `${note.id},${quote(note.title)}`),
|
||||
].join("\n") + "\n"
|
||||
);
|
||||
}
|
||||
|
||||
module.exports = { toCsv };
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<title>Notes — Electron analysis example</title>
|
||||
<script src="renderer.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Notes</h1>
|
||||
<button id="export" type="button">Export notes</button>
|
||||
<p id="status" role="status"></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
const { app, BrowserWindow, ipcMain } = require("electron");
|
||||
const { writeFileSync } = require("node:fs");
|
||||
const path = require("node:path");
|
||||
const { toCsv } = require("./csv.js");
|
||||
|
||||
const notes = [{ id: 1, title: "First note" }];
|
||||
|
||||
ipcMain.handle("notes:export", () => {
|
||||
const destination = path.join(app.getPath("downloads"), "notes.csv");
|
||||
writeFileSync(destination, toCsv(notes), "utf8");
|
||||
return destination;
|
||||
});
|
||||
|
||||
app.whenReady().then(() => {
|
||||
const window = new BrowserWindow({
|
||||
webPreferences: { preload: path.join(__dirname, "preload.js") },
|
||||
});
|
||||
window.loadFile("index.html");
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "rea-notes-example",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"main": "main.js"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
const { contextBridge, ipcRenderer } = require("electron");
|
||||
|
||||
contextBridge.exposeInMainWorld("notes", {
|
||||
exportCsv: () => ipcRenderer.invoke("notes:export"),
|
||||
});
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
document.querySelector("#export").addEventListener("click", async () => {
|
||||
const destination = await window.notes.exportCsv();
|
||||
document.querySelector("#status").textContent = `Saved to ${destination}`;
|
||||
});
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
document.querySelector("#export").addEventListener("click", async () => {
|
||||
const status = document.querySelector("#status");
|
||||
try {
|
||||
const response = await fetch("./notes.json");
|
||||
if (!response.ok)
|
||||
throw new Error(`Notes request failed: ${response.status}`);
|
||||
const { notes } = await response.json();
|
||||
const quote = (value) => `"${String(value).replaceAll('"', '""')}"`;
|
||||
const csv =
|
||||
[
|
||||
"id,title",
|
||||
...notes.map((note) => `${note.id},${quote(note.title)}`),
|
||||
].join("\n") + "\n";
|
||||
const url = URL.createObjectURL(new Blob([csv], { type: "text/csv" }));
|
||||
const link = document.createElement("a");
|
||||
link.href = url;
|
||||
link.download = "notes.csv";
|
||||
link.click();
|
||||
window.setTimeout(() => URL.revokeObjectURL(url), 1000);
|
||||
status.textContent = `Exported ${notes.length} notes to notes.csv.`;
|
||||
} catch (error) {
|
||||
status.textContent = error.message;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Notes — REA browser example</title>
|
||||
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="stylesheet" href="../../assets/styles.css" />
|
||||
<script src="export.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<main class="container example-app">
|
||||
<p class="breadcrumb">
|
||||
<a href="../../guides/browser/">Browser observation guide</a> / Example
|
||||
</p>
|
||||
<p class="eyebrow">A small app to inspect</p>
|
||||
<h1>Notes</h1>
|
||||
<p class="lede">
|
||||
Two notes. One CSV export. Observe what happens when you click.
|
||||
</p>
|
||||
<ul class="example-notes">
|
||||
<li>First note</li>
|
||||
<li>Follow the export</li>
|
||||
</ul>
|
||||
<button class="button" type="button" id="export">Export notes</button>
|
||||
<p class="example-status" id="status" role="status" aria-live="polite">
|
||||
Ready to export.
|
||||
</p>
|
||||
<p class="caption">
|
||||
This example loads <code>notes.json</code> and creates a CSV in your
|
||||
browser.
|
||||
</p>
|
||||
</main>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"notes": [
|
||||
{ "id": 1, "title": "First note" },
|
||||
{ "id": 2, "title": "Follow the export" }
|
||||
]
|
||||
}
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../showcase/dx-ball/">DX-Ball</a
|
||||
><a class="nav-start" href="./" aria-current="page">Get started</a
|
||||
><a href="https://github.com/morluto/rea/tree/main/docs">Docs</a
|
||||
><a href="../guides/">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -39,10 +39,6 @@
|
|||
</p>
|
||||
<p class="requirements">
|
||||
You'll need Node.js 22 (22.19+), 24 (24.11+) or 26+, and npm.
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/installation.md"
|
||||
>Full installation guide</a
|
||||
>
|
||||
</p>
|
||||
</div>
|
||||
<div class="article-layout">
|
||||
|
|
@ -59,11 +55,9 @@
|
|||
<section class="article-section" id="agent">
|
||||
<h2>With your agent</h2>
|
||||
<p>
|
||||
Run setup to connect REA to Claude Code, Codex, Cursor or another
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/installation.md#supported-agents"
|
||||
>supported client</a
|
||||
>.
|
||||
Run setup to connect REA to your coding agent. Client choices
|
||||
include Claude Code, Codex, Cursor, Gemini CLI, Windsurf and other
|
||||
supported MCP clients.
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
|
|
@ -154,8 +148,7 @@
|
|||
<pre><code id="install-cli">npm install --global rea-agents</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/javascript-application-workflows.md"
|
||||
<a href="../guides/javascript/"
|
||||
>Continue with the JavaScript application guide</a
|
||||
>
|
||||
</p>
|
||||
|
|
@ -164,47 +157,111 @@
|
|||
<section class="article-section" id="native">
|
||||
<h2>Working with native binaries</h2>
|
||||
<p>
|
||||
Choose a deep-analysis provider for your target and host. If you
|
||||
already use one of these tools, start with its connection guide.
|
||||
REA connects your agent to a local analysis provider. Choose the
|
||||
tool you already use, configure its connection, then ask about
|
||||
your executable or library.
|
||||
</p>
|
||||
<div class="provider-list">
|
||||
<div class="provider">
|
||||
<h3>Ghidra</h3>
|
||||
<div>
|
||||
<p>
|
||||
Use an existing Ghidra installation and JDK. Setup checks
|
||||
their paths and records them for your selected clients.
|
||||
Use an existing Ghidra 12.1.x installation and a 64-bit JDK
|
||||
21 or newer on Linux x64 or macOS x64/arm64. Set the paths
|
||||
before running setup:
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/installation.md#ghidra"
|
||||
>Ghidra setup and supported hosts</a
|
||||
>
|
||||
<details class="evidence-details">
|
||||
<summary>Connect your Ghidra installation</summary>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Ghidra connection · macOS / Linux</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="ghidra-connect"
|
||||
aria-label="Copy ghidra connection · macos / linux"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="ghidra-connect">export GHIDRA_INSTALL_DIR=/absolute/path/to/ghidra_12.1.4_PUBLIC
|
||||
export JAVA_HOME=/absolute/path/to/jdk-21
|
||||
npx -y rea-agents@latest doctor --provider ghidra --json
|
||||
npx rea-agents@latest setup</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Replace both paths with your existing installations. Setup
|
||||
checks them and records valid settings for your selected
|
||||
client. On macOS, use a Ghidra package with the native
|
||||
decompiler matching your host architecture.
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
<div class="provider">
|
||||
<h3>Hopper</h3>
|
||||
<div>
|
||||
<p>
|
||||
Connect Hopper on macOS or Linux. Setup can also install it
|
||||
after you review and approve the installation plan.
|
||||
Connect Hopper on macOS or Linux. Run the setup command
|
||||
above; it detects an existing installation or offers an
|
||||
installation plan for your approval.
|
||||
</p>
|
||||
<p>
|
||||
On macOS, complete Hopper's first-run choice of demo mode or
|
||||
license activation before an unattended analysis.
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/installation.md#hopper"
|
||||
>Hopper installation</a
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
<div class="provider">
|
||||
<h3>IDA</h3>
|
||||
<div>
|
||||
<p>
|
||||
Reuse an existing IDA MCP registration for read-only
|
||||
analysis through an attached GUI or headless database.
|
||||
The verified IDA setups use Windows, with an existing MCP
|
||||
registration for an attached GUI or a headless database.
|
||||
Linux and macOS headless analysis remains unverified.
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/ida-provider.md"
|
||||
>IDA connection guide</a
|
||||
>
|
||||
<details class="evidence-details">
|
||||
<summary>Connect an existing IDA MCP registration</summary>
|
||||
<p>
|
||||
Keep the upstream server's working <code>command</code>,
|
||||
<code>args</code> and optional <code>env</code> in a JSON
|
||||
file. An existing <code>mcpServers</code> object with an
|
||||
<code>ida-pro-mcp</code> entry is also accepted. Point REA
|
||||
at that file before setup:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>IDA connection · Windows PowerShell</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="ida-connect"
|
||||
aria-label="Copy IDA connection for Windows PowerShell"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="ida-connect">$env:REA_IDA_MCP_CONFIG = "C:/analysis/ida-mcp.json"
|
||||
$env:REA_ANALYSIS_PROVIDER = "ida"
|
||||
npx -y rea-agents@latest doctor --provider ida --json
|
||||
npx rea-agents@latest setup</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
The attached profile uses upstream legacy 1.4.0 tools with
|
||||
the original binary already open in IDA. The headless
|
||||
profile uses the upstream database-supervisor API and a
|
||||
registration with <code>"mode": "headless"</code>; real
|
||||
verification covers an IDA 9.3 Windows x64 supervisor.
|
||||
Doctor checks the registration without starting IDA.
|
||||
</p>
|
||||
<p>
|
||||
Give REA the original executable path, with
|
||||
<code>--provider ida</code> in a CLI query. For an agent,
|
||||
include <code>REA_IDA_MCP_CONFIG</code> in the REA
|
||||
registration's environment and select IDA when opening the
|
||||
binary.
|
||||
</p>
|
||||
</details>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -228,8 +285,13 @@
|
|||
</div>
|
||||
<p>
|
||||
Replace <code>main</code> with the function name or address you
|
||||
want to inspect. The first query imports and analyzes the target,
|
||||
so it can take longer than later requests.
|
||||
want to inspect. Each CLI invocation imports and analyzes the
|
||||
target in its own session. Your agent's MCP queries reuse the
|
||||
imported binary while that session is open.
|
||||
</p>
|
||||
<p>
|
||||
<a href="../guides/native/">Follow a native analysis example</a>
|
||||
to see how the function result leads to a recovered calculation.
|
||||
</p>
|
||||
</section>
|
||||
|
||||
|
|
@ -273,12 +335,10 @@
|
|||
<p>The report gives the failed check and a suggested next step.</p>
|
||||
<ul class="reading-list">
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/installation.md"
|
||||
>Installation and troubleshooting</a
|
||||
<a href="../guides/">Choose an investigation guide</a
|
||||
><span
|
||||
>Client configuration, versions and provider
|
||||
requirements.</span
|
||||
>Worked examples for a binary, an Electron app and a
|
||||
browser.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,339 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Browser observation — REA</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Observe a Notes export with REA: select a browser page, capture a click, inspect the request and follow the export script."
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="stylesheet" href="../../assets/styles.css" />
|
||||
<script src="../../assets/main.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
<header class="site-header">
|
||||
<div class="container header-inner">
|
||||
<a class="brand" href="../../" aria-label="REA home"
|
||||
><span class="brand-name">REA</span
|
||||
><span class="brand-caption">Reverse Engineer Anything</span></a
|
||||
>
|
||||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../../showcase/dx-ball/">DX-Ball</a
|
||||
><a class="nav-start" href="../../get-started/">Get started</a
|
||||
><a href="../">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<main id="main" class="container">
|
||||
<div class="page-heading">
|
||||
<p class="breadcrumb">
|
||||
<a href="../../">Home</a> / <a href="../">Guides</a> / Browser
|
||||
observation
|
||||
</p>
|
||||
<h1>See what happens<br />when you click.</h1>
|
||||
<p class="lede">
|
||||
Use a small browser app to connect an action, its network request and
|
||||
the JavaScript that builds a download.
|
||||
</p>
|
||||
</div>
|
||||
<div class="article-layout">
|
||||
<nav class="contents" aria-label="On this page">
|
||||
<p>In this guide</p>
|
||||
<ul>
|
||||
<li><a href="#example">The example</a></li>
|
||||
<li><a href="#prepare">Open the browser</a></li>
|
||||
<li><a href="#ask">Ask your agent</a></li>
|
||||
<li><a href="#capture">Capture from CLI</a></li>
|
||||
<li><a href="#result">Read and follow</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<article class="article" aria-label="Browser observation">
|
||||
<section class="article-section" id="example">
|
||||
<h2>What happens when you click Export?</h2>
|
||||
<p>
|
||||
The
|
||||
<a href="../../examples/notes-web/">Notes browser example</a> has
|
||||
an <strong>Export notes</strong> button. Clicking it loads
|
||||
<code>notes.json</code>, formats the two notes as CSV, and starts
|
||||
a download.
|
||||
</p>
|
||||
<p>
|
||||
We want to see which request the click makes, then use the script
|
||||
to understand how the response becomes a file.
|
||||
</p>
|
||||
<figure class="workflow-figure">
|
||||
<p class="figure-title">One action, captured while it happens</p>
|
||||
<ol class="flow-steps">
|
||||
<li>
|
||||
<span class="flow-label">01 · You</span
|
||||
><span class="flow-node">Click Export notes</span
|
||||
><span class="flow-note"
|
||||
>Perform the action in your browser while the capture is
|
||||
running.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flow-label">02 · REA</span
|
||||
><span class="flow-node">Record the request</span
|
||||
><span class="flow-note"
|
||||
>Capture the request URL, response status and JSON structure
|
||||
from the selected page.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flow-label">03 · Your agent</span
|
||||
><span class="flow-node">Read the export code</span
|
||||
><span class="flow-note"
|
||||
>Connect the observed request to
|
||||
<code>fetch("./notes.json")</code> in
|
||||
<code>export.js</code>.</span
|
||||
>
|
||||
</li>
|
||||
</ol>
|
||||
<p class="flow-result">
|
||||
<code>GET notes.json → 200 → notes[] → CSV</code>
|
||||
</p>
|
||||
<figcaption>
|
||||
The request and response metadata come from the capture. CSV
|
||||
construction is explained by reading the script.
|
||||
</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
<section class="article-section" id="prepare">
|
||||
<h2>Open an observable browser</h2>
|
||||
<p>
|
||||
Start a Chrome-family browser with a dedicated profile and a local
|
||||
debugging port. On Linux, for this site's local preview:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Start the example browser</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="browser-start"
|
||||
aria-label="Copy start the example browser"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="browser-start">google-chrome \
|
||||
--remote-debugging-port=9222 \
|
||||
--user-data-dir=/tmp/rea-notes-browser \
|
||||
http://127.0.0.1:4173/examples/notes-web/</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Use your installed browser's executable path on macOS or Windows.
|
||||
The page URL should point to your local preview or
|
||||
<a href="../../examples/notes-web/">the hosted Notes example</a>.
|
||||
</p>
|
||||
<p>
|
||||
REA connects to this existing browser. You keep control of the
|
||||
page, perform the action, and close the browser when finished.
|
||||
</p>
|
||||
</section>
|
||||
<section class="article-section" id="ask">
|
||||
<h2>Ask your agent to watch one action</h2>
|
||||
<div class="agent-terminal">
|
||||
<div class="terminal-header">
|
||||
<span class="terminal-title"
|
||||
><span class="terminal-dots" aria-hidden="true"
|
||||
><i></i><i></i><i></i></span
|
||||
>Your coding agent</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="browser-prompt"
|
||||
aria-label="Copy example prompt"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<p class="terminal-prompt">
|
||||
<span class="prompt-symbol" aria-hidden="true">›</span
|
||||
><span id="browser-prompt"
|
||||
>Use REA to inspect this page while I click Export notes. What
|
||||
request does it make, and how is the CSV built?</span
|
||||
><span class="terminal-cursor" aria-hidden="true"></span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="caption">
|
||||
Example prompt. Give your agent the debugging endpoint,
|
||||
<code>http://127.0.0.1:9222</code>, and the page URL. Let it start
|
||||
the capture before you click.
|
||||
</p>
|
||||
<p>
|
||||
Your agent discovers the page target, starts an observation
|
||||
window, and reads the resulting evidence. It can then inspect the
|
||||
script behind the export.
|
||||
</p>
|
||||
</section>
|
||||
<section class="article-section" id="capture">
|
||||
<h2>Or capture from the terminal</h2>
|
||||
<p>Find the page's target ID:</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Find the page</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="browser-targets"
|
||||
aria-label="Copy find the page"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="browser-targets">npx -y rea-agents@latest list-browser-targets \
|
||||
http://127.0.0.1:9222 --json</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Choose the target with the Notes example URL. Replace
|
||||
<code>TARGET_ID</code> below with its ID, then run:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Capture the next ten seconds</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="browser-capture"
|
||||
aria-label="Copy capture the next ten seconds"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="browser-capture">npx -y rea-agents@latest inspect-web-page \
|
||||
http://127.0.0.1:9222 TARGET_ID \
|
||||
--observation-ms 10000 \
|
||||
--include-accessibility-text \
|
||||
--include-json-body-shapes --json</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Let the command connect for a few seconds, then click
|
||||
<strong>Export notes</strong> in that browser. The result arrives
|
||||
after the observation window ends.
|
||||
</p>
|
||||
<p>
|
||||
The captured result includes the selected page, network activity
|
||||
during that window, and the shape of the JSON response. Earlier
|
||||
requests are outside this capture.
|
||||
</p>
|
||||
</section>
|
||||
<section class="article-section" id="result">
|
||||
<h2>Read the result, then follow the code</h2>
|
||||
<div class="evidence-table-wrap">
|
||||
<table class="evidence-table">
|
||||
<caption>
|
||||
Recorded with REA 4.1.0 on the local Notes example
|
||||
</caption>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Captured fact</th>
|
||||
<th>Value</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>Request</td>
|
||||
<td><code>GET /examples/notes-web/notes.json</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Response</td>
|
||||
<td><code>200 · application/json</code></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Request initiator</td>
|
||||
<td><code>export.js</code> · script</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Response structure</td>
|
||||
<td>
|
||||
<code>notes[]</code> with <code>id</code> and
|
||||
<code>title</code> fields
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Read the loaded scripts</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="browser-scripts"
|
||||
aria-label="Copy read the loaded scripts"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="browser-scripts">npx -y rea-agents@latest inspect-web-page \
|
||||
http://127.0.0.1:9222 TARGET_ID \
|
||||
--include-script-sources --json</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
The request identifies the data source, and REA records
|
||||
<code>export.js</code> as its initiator. Read the captured script
|
||||
source to see how the data is used:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>export.js · example source excerpt</span>
|
||||
</div>
|
||||
<pre><code>const response = await fetch("./notes.json");
|
||||
const { notes } = await response.json();
|
||||
// Format each note as a CSV row.
|
||||
const url = URL.createObjectURL(new Blob([csv], { type: "text/csv" }));
|
||||
link.href = url;
|
||||
link.download = "notes.csv";
|
||||
link.click();</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
The download is constructed in the page using the JSON response.
|
||||
The CSV formatting step comes from the script; the network capture
|
||||
establishes that this click requested <code>notes.json</code>.
|
||||
</p>
|
||||
<p>
|
||||
<a href="../../examples/notes-web/">Open the example</a> ·
|
||||
<a href="../../examples/notes-web/export.js" download
|
||||
>Download export.js</a
|
||||
>
|
||||
·
|
||||
<a href="../../examples/notes-web/notes.json" download
|
||||
>Download notes.json</a
|
||||
>
|
||||
</p>
|
||||
<blockquote class="prompt">
|
||||
<p>
|
||||
Find the script that requests <code>notes.json</code>. How does
|
||||
it escape titles containing commas or quotation marks?
|
||||
</p>
|
||||
</blockquote>
|
||||
<p>
|
||||
For an installed JavaScript or Electron app, continue with the
|
||||
<a href="../javascript/">application workflow</a> to follow files
|
||||
and process boundaries.
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-inner">
|
||||
<p>REA · Reverse Engineer Anything</p>
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/morluto/rea">Source</a
|
||||
><a href="https://github.com/morluto/rea/blob/main/LICENSE"
|
||||
>MIT license</a
|
||||
><a href="https://discord.gg/GkcryMnJDM">Community</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<p class="sr-only" id="copy-status" role="status" aria-live="polite"></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Guides — REA</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Learn REA through short worked examples: a native calculation, an Electron CSV export and a browser request."
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<link rel="icon" href="../assets/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="stylesheet" href="../assets/styles.css" />
|
||||
<script src="../assets/main.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
<header class="site-header">
|
||||
<div class="container header-inner">
|
||||
<a class="brand" href="../" aria-label="REA home"
|
||||
><span class="brand-name">REA</span
|
||||
><span class="brand-caption">Reverse Engineer Anything</span></a
|
||||
>
|
||||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../showcase/dx-ball/">DX-Ball</a
|
||||
><a class="nav-start" href="../get-started/">Get started</a
|
||||
><a href="./" aria-current="page">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<main id="main" class="container">
|
||||
<div class="page-heading">
|
||||
<p class="breadcrumb"><a href="../">Home</a> / Guides</p>
|
||||
<h1>Start with<br />an example.</h1>
|
||||
<p class="lede">
|
||||
Follow an example from the first prompt to the evidence. Then use the
|
||||
same approach with your own app.
|
||||
</p>
|
||||
</div>
|
||||
<div class="guide-index">
|
||||
<section class="guide-entry">
|
||||
<p class="eyebrow">Native binaries</p>
|
||||
<div>
|
||||
<h2>Recover a sound-pan calculation</h2>
|
||||
<p>
|
||||
Start with a Windows game executable. Inspect instructions, follow
|
||||
a caller, read numeric constants and check the recovered C.
|
||||
</p>
|
||||
<div class="guide-route">
|
||||
<span>Instructions</span> <b aria-hidden="true">→</b>
|
||||
<span>Caller</span> <b aria-hidden="true">→</b>
|
||||
<span>Constants</span> <b aria-hidden="true">→</b>
|
||||
<span>C function</span>
|
||||
</div>
|
||||
<a class="text-link" href="native/">Native analysis guide</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="guide-entry">
|
||||
<p class="eyebrow">JavaScript & Electron</p>
|
||||
<div>
|
||||
<h2>Follow a CSV export</h2>
|
||||
<p>
|
||||
A small Notes app spreads one feature across the renderer, preload
|
||||
and main process. Use REA to connect the API, IPC channel and file
|
||||
writer.
|
||||
</p>
|
||||
<div class="guide-route">
|
||||
<span>Button</span> <b aria-hidden="true">→</b>
|
||||
<span>Preload API</span> <b aria-hidden="true">→</b>
|
||||
<span>IPC</span> <b aria-hidden="true">→</b> <span>CSV file</span>
|
||||
</div>
|
||||
<a class="text-link" href="javascript/">Application workflows</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="guide-entry">
|
||||
<p class="eyebrow">Browser behavior</p>
|
||||
<div>
|
||||
<h2>Observe a click</h2>
|
||||
<p>
|
||||
Open a working Notes example, capture an export request and find
|
||||
the code that turns its response into a download.
|
||||
</p>
|
||||
<div class="guide-route">
|
||||
<span>Your click</span> <b aria-hidden="true">→</b>
|
||||
<span>Captured request</span> <b aria-hidden="true">→</b>
|
||||
<span>Export script</span>
|
||||
</div>
|
||||
<a class="text-link" href="browser/">Browser observation guide</a>
|
||||
</div>
|
||||
</section>
|
||||
<section class="guide-entry">
|
||||
<p class="eyebrow">First investigation</p>
|
||||
<div>
|
||||
<h2>Connect your agent, then ask.</h2>
|
||||
<p>
|
||||
Each guide starts with a plain-language question and follows a
|
||||
concrete example. If you haven't connected REA yet, setup takes
|
||||
you through the client and provider choices.
|
||||
</p>
|
||||
<a class="text-link" href="../get-started/">Get started with REA</a>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-inner">
|
||||
<p>REA · Reverse Engineer Anything</p>
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/morluto/rea">Source</a
|
||||
><a href="https://github.com/morluto/rea/blob/main/LICENSE"
|
||||
>MIT license</a
|
||||
><a href="https://discord.gg/GkcryMnJDM">Community</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<p class="sr-only" id="copy-status" role="status" aria-live="polite"></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,315 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>JavaScript & Electron — REA</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Trace a Notes CSV export through a renderer API, preload and IPC handler with REA. Includes downloadable example files and a verified analysis result."
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="stylesheet" href="../../assets/styles.css" />
|
||||
<script src="../../assets/main.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
<header class="site-header">
|
||||
<div class="container header-inner">
|
||||
<a class="brand" href="../../" aria-label="REA home"
|
||||
><span class="brand-name">REA</span
|
||||
><span class="brand-caption">Reverse Engineer Anything</span></a
|
||||
>
|
||||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../../showcase/dx-ball/">DX-Ball</a
|
||||
><a class="nav-start" href="../../get-started/">Get started</a
|
||||
><a href="../">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<main id="main" class="container">
|
||||
<div class="page-heading">
|
||||
<p class="breadcrumb">
|
||||
<a href="../../">Home</a> / <a href="../">Guides</a> / JavaScript
|
||||
& Electron
|
||||
</p>
|
||||
<h1>Follow a feature<br />across the app.</h1>
|
||||
<p class="lede">
|
||||
Use a CSV export to see how REA connects JavaScript files, exposed
|
||||
APIs and Electron process boundaries.
|
||||
</p>
|
||||
</div>
|
||||
<div class="article-layout">
|
||||
<nav class="contents" aria-label="On this page">
|
||||
<p>In this guide</p>
|
||||
<ul>
|
||||
<li><a href="#example">The example</a></li>
|
||||
<li><a href="#ask">Ask your agent</a></li>
|
||||
<li><a href="#follow">Connect the pieces</a></li>
|
||||
<li><a href="#try">Try the example</a></li>
|
||||
<li><a href="#next">Continue investigating</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<article class="article" aria-label="JavaScript & Electron">
|
||||
<section class="article-section" id="example">
|
||||
<h2>Where does “Export notes” go?</h2>
|
||||
<p>
|
||||
This guide uses a small Electron app. Clicking
|
||||
<strong>Export notes</strong> calls an API in the page, crosses
|
||||
into the main process, and writes a CSV. The behavior is spread
|
||||
across several files.
|
||||
</p>
|
||||
<figure class="workflow-figure">
|
||||
<p class="figure-title">A feature across two processes</p>
|
||||
<ol class="flow-steps">
|
||||
<li>
|
||||
<span class="flow-label">Renderer → preload</span
|
||||
><span class="flow-node"><code>notes.exportCsv()</code></span
|
||||
><span class="flow-note"
|
||||
><code>renderer.js</code> calls the API exposed by
|
||||
<code>preload.js</code>.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flow-label">Between processes</span
|
||||
><span class="flow-node"><code>notes:export</code></span
|
||||
><span class="flow-note"
|
||||
>The preload invokes an IPC channel: a named message to the
|
||||
main process.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flow-label">Main process</span
|
||||
><span class="flow-node">An export handler</span
|
||||
><span class="flow-note"
|
||||
><code>main.js</code> handles that channel and calls the CSV
|
||||
writer.</span
|
||||
>
|
||||
</li>
|
||||
</ol>
|
||||
<p class="flow-result">
|
||||
<code>csv.js</code> formats the notes →
|
||||
<code>main.js</code> writes <code>downloads/notes.csv</code>.
|
||||
</p>
|
||||
<figcaption>
|
||||
REA matches the literal IPC call to its handler and identifies
|
||||
the exposed API. The path is inferred from code; a runtime
|
||||
capture can check what executes.
|
||||
</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
<section class="article-section" id="ask">
|
||||
<h2>Give your agent one feature</h2>
|
||||
<p>
|
||||
<a href="../../get-started/#agent">Connect your agent to REA</a>
|
||||
and give it the app directory or ASAR file:
|
||||
</p>
|
||||
<div class="agent-terminal">
|
||||
<div class="terminal-header">
|
||||
<span class="terminal-title"
|
||||
><span class="terminal-dots" aria-hidden="true"
|
||||
><i></i><i></i><i></i></span
|
||||
>Your coding agent</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="javascript-prompt"
|
||||
aria-label="Copy example prompt"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<p class="terminal-prompt">
|
||||
<span class="prompt-symbol" aria-hidden="true">›</span
|
||||
><span id="javascript-prompt"
|
||||
>Use REA to find what happens when I export notes in this app.
|
||||
Where is the CSV created?</span
|
||||
><span class="terminal-cursor" aria-hidden="true"></span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="caption">
|
||||
Example prompt. Include the absolute path to the application
|
||||
folder or <code>app.asar</code>.
|
||||
</p>
|
||||
<p>
|
||||
REA reads the application files and builds a map of entrypoints,
|
||||
modules and process boundaries. Your agent uses that map to narrow
|
||||
the investigation to the export feature.
|
||||
</p>
|
||||
</section>
|
||||
<section class="article-section" id="follow">
|
||||
<h2>Connect the pieces</h2>
|
||||
<h3>The page calls a small API</h3>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>renderer.js · example source</span>
|
||||
</div>
|
||||
<pre><code>document.querySelector("#export").addEventListener("click", async () => {
|
||||
const destination = await window.notes.exportCsv();
|
||||
document.querySelector("#status").textContent = `Saved to ${destination}`;
|
||||
});</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
The page knows the method name. The implementation lives in the
|
||||
preload, which exposes it to the renderer.
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>preload.js · example source</span>
|
||||
</div>
|
||||
<pre><code>contextBridge.exposeInMainWorld("notes", {
|
||||
exportCsv: () => ipcRenderer.invoke("notes:export"),
|
||||
});</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
REA records <code>notes.exportCsv</code> and the literal channel
|
||||
<code>notes:export</code>. It pairs that invocation with the
|
||||
single matching handler in the main process.
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>main.js · example source</span>
|
||||
</div>
|
||||
<pre><code>ipcMain.handle("notes:export", () => {
|
||||
const destination = path.join(app.getPath("downloads"), "notes.csv");
|
||||
writeFileSync(destination, toCsv(notes), "utf8");
|
||||
return destination;
|
||||
});</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Reading the handler answers the original question:
|
||||
<code>csv.js</code> formats the notes, and the main process writes
|
||||
<code>notes.csv</code> into the user's Downloads folder.
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>REA result · summary.ipc excerpt</span>
|
||||
</div>
|
||||
<pre><code>{
|
||||
"literal_channels": 1,
|
||||
"renderer_transmissions": 1,
|
||||
"main_handlers": 1,
|
||||
"paired_renderer_transmissions": 1,
|
||||
"ambiguous_renderer_transmissions": 0
|
||||
}</code></pre>
|
||||
</div>
|
||||
<p class="caption">
|
||||
Recorded by REA 4.1.0 on the downloadable example below. Source
|
||||
locations accompany the API, channel and handler findings.
|
||||
</p>
|
||||
</section>
|
||||
<section class="article-section" id="try">
|
||||
<h2>Analyze the example yourself</h2>
|
||||
<p>
|
||||
Save these six files together in a folder named
|
||||
<code>notes-example</code>. Static analysis needs Node.js and npm;
|
||||
you can inspect the code without installing or launching Electron.
|
||||
</p>
|
||||
<ul class="example-files">
|
||||
<li>
|
||||
<a href="../../examples/notes-electron/package.json" download
|
||||
>package.json</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../examples/notes-electron/main.js" download
|
||||
>main.js</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../examples/notes-electron/preload.js" download
|
||||
>preload.js</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../examples/notes-electron/renderer.js" download
|
||||
>renderer.js</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../examples/notes-electron/csv.js" download
|
||||
>csv.js</a
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../../examples/notes-electron/index.html" download
|
||||
>index.html</a
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Static application analysis</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="javascript-command"
|
||||
aria-label="Copy static application analysis"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="javascript-command">npx -y rea-agents@latest analyze-javascript-application \
|
||||
/absolute/path/to/notes-example --json</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Replace the path with the folder you saved. Look for
|
||||
<code>normalized_result.summary.ipc</code> and the source
|
||||
locations in <code>normalized_result.graph</code>. The example has
|
||||
one channel and one paired invocation.
|
||||
</p>
|
||||
<p>
|
||||
For your own app, use the same command with its directory or ASAR
|
||||
path. An agent can follow a literal string, API name or IPC
|
||||
channel with REA's feature tracing tools.
|
||||
</p>
|
||||
</section>
|
||||
<section class="article-section" id="next">
|
||||
<h2>Ask the next question</h2>
|
||||
<blockquote class="prompt">
|
||||
<p>
|
||||
Follow <code>notes:export</code> to its handler. Which inputs
|
||||
reach the CSV writer, and what happens if writing the file
|
||||
fails?
|
||||
</p>
|
||||
</blockquote>
|
||||
<p>
|
||||
Use the source locations to inspect those details. Computed
|
||||
channel names and ambiguous handlers can remain unresolved;
|
||||
runtime observations give you another way to investigate them.
|
||||
</p>
|
||||
<ul class="reading-list">
|
||||
<li>
|
||||
<a href="../browser/">Observe a browser export</a
|
||||
><span
|
||||
>Capture a real click and see which request it makes.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a href="../native/">Follow a native function</a
|
||||
><span
|
||||
>Continue into a binary when a feature depends on native
|
||||
code.</span
|
||||
>
|
||||
</li>
|
||||
</ul>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-inner">
|
||||
<p>REA · Reverse Engineer Anything</p>
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/morluto/rea">Source</a
|
||||
><a href="https://github.com/morluto/rea/blob/main/LICENSE"
|
||||
>MIT license</a
|
||||
><a href="https://discord.gg/GkcryMnJDM">Community</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<p class="sr-only" id="copy-status" role="status" aria-live="polite"></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,276 @@
|
|||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>Native analysis — REA</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Recover a native calculation with REA: inspect instructions, follow a caller, read constants and check reconstructed C."
|
||||
/>
|
||||
<meta name="theme-color" content="#ffffff" />
|
||||
<link rel="icon" href="../../assets/favicon.svg" type="image/svg+xml" />
|
||||
<link rel="stylesheet" href="../../assets/styles.css" />
|
||||
<script src="../../assets/main.js" defer></script>
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
<header class="site-header">
|
||||
<div class="container header-inner">
|
||||
<a class="brand" href="../../" aria-label="REA home"
|
||||
><span class="brand-name">REA</span
|
||||
><span class="brand-caption">Reverse Engineer Anything</span></a
|
||||
>
|
||||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="../../showcase/dx-ball/">DX-Ball</a
|
||||
><a class="nav-start" href="../../get-started/">Get started</a
|
||||
><a href="../">Guides</a
|
||||
><a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
</header>
|
||||
<main id="main" class="container">
|
||||
<div class="page-heading">
|
||||
<p class="breadcrumb">
|
||||
<a href="../../">Home</a> / <a href="../">Guides</a> / Native analysis
|
||||
</p>
|
||||
<h1>Recover a calculation.<br />Follow the evidence.</h1>
|
||||
<p class="lede">
|
||||
Use DX-Ball’s sound panning to see how an agent moves from a binary to
|
||||
an explanation and a checked C function.
|
||||
</p>
|
||||
</div>
|
||||
<div class="article-layout">
|
||||
<nav class="contents" aria-label="On this page">
|
||||
<p>In this guide</p>
|
||||
<ul>
|
||||
<li><a href="#example">The example</a></li>
|
||||
<li><a href="#ask">Ask your agent</a></li>
|
||||
<li><a href="#follow">Follow the evidence</a></li>
|
||||
<li><a href="#result">Recover and check</a></li>
|
||||
<li><a href="#try">Use your own binary</a></li>
|
||||
</ul>
|
||||
</nav>
|
||||
<article class="article" aria-label="Native analysis">
|
||||
<section class="article-section" id="example">
|
||||
<h2>A position becomes a sound</h2>
|
||||
<p>
|
||||
In DX-Ball, a brick hit supplies a horizontal screen position. The
|
||||
sound code turns it into a left/right panning value. We want to
|
||||
recover that calculation from the executable.
|
||||
</p>
|
||||
<p>
|
||||
The starting function, <code>0x00406400</code>, has a short
|
||||
decompiler result: a call to <code>__ftol()</code>. REA also
|
||||
returns its instructions and callers, which give the agent a way
|
||||
to investigate the missing arithmetic.
|
||||
</p>
|
||||
<figure class="workflow-figure">
|
||||
<p class="figure-title">
|
||||
Three pieces of evidence, one calculation
|
||||
</p>
|
||||
<ol class="flow-steps">
|
||||
<li>
|
||||
<span class="flow-label">01 · Instructions</span
|
||||
><span class="flow-node">An integer input</span
|
||||
><span class="flow-note"
|
||||
>A stack read supplies the value used by the floating-point
|
||||
instructions.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flow-label">02 · Caller</span
|
||||
><span class="flow-node">A screen coordinate</span
|
||||
><span class="flow-note"
|
||||
>The brick-hit caller passes
|
||||
<code>20 + 30 × tile_x</code>.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<span class="flow-label">03 · Data bytes</span
|
||||
><span class="flow-node">The numeric constants</span
|
||||
><span class="flow-note"
|
||||
>Referenced bytes decode to <code>1.5625</code>,
|
||||
<code>500.0</code> and a scale value.</span
|
||||
>
|
||||
</li>
|
||||
</ol>
|
||||
<p class="flow-result">
|
||||
<code>pan = (x × 1.5625 − 500.0) × scale</code>
|
||||
</p>
|
||||
<figcaption>
|
||||
REA supplies the instructions, call relationship and bytes. The
|
||||
agent interprets them together.
|
||||
</figcaption>
|
||||
</figure>
|
||||
</section>
|
||||
<section class="article-section" id="ask">
|
||||
<h2>Ask about the behavior</h2>
|
||||
<p>
|
||||
After
|
||||
<a href="../../get-started/#agent">connecting REA to your agent</a
|
||||
>, provide the local executable and a question:
|
||||
</p>
|
||||
<div class="agent-terminal">
|
||||
<div class="terminal-header">
|
||||
<span class="terminal-title"
|
||||
><span class="terminal-dots" aria-hidden="true"
|
||||
><i></i><i></i><i></i></span
|
||||
>Your coding agent</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="native-prompt"
|
||||
aria-label="Copy example prompt"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<p class="terminal-prompt">
|
||||
<span class="prompt-symbol" aria-hidden="true">›</span
|
||||
><span id="native-prompt"
|
||||
>Use REA to find how DX-Ball calculates sound panning. Explain
|
||||
the calculation and show the code.</span
|
||||
><span class="terminal-cursor" aria-hidden="true"></span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="caption">
|
||||
Example prompt. Give your agent the path to your local DX-Ball
|
||||
1.07 executable.
|
||||
</p>
|
||||
<p>
|
||||
You can start with a feature name. Your agent uses REA to locate
|
||||
relevant functions, then follows the evidence with more focused
|
||||
queries.
|
||||
</p>
|
||||
</section>
|
||||
<section class="article-section" id="follow">
|
||||
<h2>Follow what REA returns</h2>
|
||||
<h3>Read the input</h3>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>REA instruction excerpt · 0x406409</span>
|
||||
</div>
|
||||
<pre><code>MOV EAX, dword ptr [EBP + 0x8]
|
||||
MOV dword ptr [EBP + -0xc], EAX
|
||||
FILD dword ptr [EBP + -0xc]</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
The instructions load an integer argument and convert it for
|
||||
floating-point arithmetic. This is the input absent from the first
|
||||
pseudocode view.
|
||||
</p>
|
||||
<h3 class="small-heading">Follow the caller</h3>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>REA caller excerpt · 0x411f5b</span>
|
||||
</div>
|
||||
<pre><code>ADD EAX, 0x14
|
||||
PUSH EAX
|
||||
CALL 0x00406400</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
REA identifies the brick-hit caller. Its earlier instructions
|
||||
multiply the tile coordinate by 30; adding <code>0x14</code> (20)
|
||||
gives the brick's screen position.
|
||||
</p>
|
||||
<h3 class="small-heading">Read the referenced constants</h3>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span
|
||||
>REA byte results · interpreted as little-endian doubles</span
|
||||
>
|
||||
</div>
|
||||
<pre><code>0x420068 000000000000f93f → 1.5625
|
||||
0x420070 0000000000407f40 → 500.0
|
||||
0x4210a0 000000000000f03f → 1.0</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
The arithmetic references these addresses. Reading the bytes
|
||||
supplies the actual values; the instruction order tells the agent
|
||||
how they are used.
|
||||
</p>
|
||||
</section>
|
||||
<section class="article-section" id="result">
|
||||
<h2>Write and check the recovered function</h2>
|
||||
<div class="code-block">
|
||||
<div class="code-header"><span>Recovered C · excerpt</span></div>
|
||||
<pre><code>pan = (double)x;
|
||||
pan = pan * 1.5625;
|
||||
pan = pan - 500.0;
|
||||
pan = pan * dxball_pan_scale;
|
||||
return (DxBallInt)pan;</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
With a scale of 1.0, screen positions 0, 320 and 640 map to −500,
|
||||
0 and 500 before integer conversion. The calculation puts the
|
||||
center of the screen at the center of the stereo field.
|
||||
</p>
|
||||
<p>
|
||||
The agent used REA's findings to write the C. Separate checks
|
||||
compared 3,205 inputs against the original x86 function and
|
||||
reproduced all 63 compiled bytes of the complete function.
|
||||
</p>
|
||||
<p>
|
||||
<a href="../../showcase/dx-ball/#sound-pan"
|
||||
>See the full investigation and assembly-to-C comparison</a
|
||||
>
|
||||
</p>
|
||||
</section>
|
||||
<section class="article-section" id="try">
|
||||
<h2>Try it with your own binary</h2>
|
||||
<p>
|
||||
<a href="../../get-started/#native"
|
||||
>Connect a native analysis provider</a
|
||||
>, then give your agent a local executable or library and a
|
||||
feature to investigate. Useful starting questions include “How is
|
||||
this file parsed?” or “Which function handles this menu action?”
|
||||
</p>
|
||||
<p>
|
||||
If you already know a function name or address, you can query it
|
||||
from the terminal. For a configured Ghidra installation:
|
||||
</p>
|
||||
<div class="code-block">
|
||||
<div class="code-header">
|
||||
<span>Function analysis</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="native-command"
|
||||
aria-label="Copy function analysis"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<pre><code id="native-command">npx -y rea-agents@latest function /absolute/path/to/program main \
|
||||
--provider ghidra --json</code></pre>
|
||||
</div>
|
||||
<p>
|
||||
Replace the target path and <code>main</code> with your own
|
||||
function name or address. Each CLI invocation imports and analyzes
|
||||
the binary, then closes its session. Your agent's MCP queries
|
||||
reuse the imported binary while that session is open.
|
||||
</p>
|
||||
<p>
|
||||
Read the pseudocode alongside instructions and references. When a
|
||||
result leaves a call or value unresolved, use that location as the
|
||||
next question.
|
||||
</p>
|
||||
</section>
|
||||
</article>
|
||||
</div>
|
||||
</main>
|
||||
<footer class="site-footer">
|
||||
<div class="container footer-inner">
|
||||
<p>REA · Reverse Engineer Anything</p>
|
||||
<div class="footer-links">
|
||||
<a href="https://github.com/morluto/rea">Source</a
|
||||
><a href="https://github.com/morluto/rea/blob/main/LICENSE"
|
||||
>MIT license</a
|
||||
><a href="https://discord.gg/GkcryMnJDM">Community</a>
|
||||
</div>
|
||||
</div>
|
||||
</footer>
|
||||
<p class="sr-only" id="copy-status" role="status" aria-live="polite"></p>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -24,7 +24,7 @@
|
|||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="showcase/dx-ball/">DX-Ball</a>
|
||||
<a class="nav-start" href="get-started/">Get started</a>
|
||||
<a href="https://github.com/morluto/rea/tree/main/docs">Docs</a>
|
||||
<a href="guides/">Guides</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -150,10 +150,7 @@
|
|||
Inspect functions, strings, references and call relationships in
|
||||
executables and libraries.
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/native-investigation.md"
|
||||
>Native analysis guide</a
|
||||
>
|
||||
<a href="guides/native/">Native analysis guide</a>
|
||||
</div>
|
||||
<div>
|
||||
<h3>JavaScript & Electron</h3>
|
||||
|
|
@ -161,10 +158,7 @@
|
|||
Map modules, routes, IPC and native dependencies from an
|
||||
application folder or ASAR archive.
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/javascript-application-workflows.md"
|
||||
>Application workflows</a
|
||||
>
|
||||
<a href="guides/javascript/">Application workflows</a>
|
||||
</div>
|
||||
<div>
|
||||
<h3>Runtime behavior</h3>
|
||||
|
|
@ -172,10 +166,7 @@
|
|||
Capture selected browser or process activity, then compare the
|
||||
results across runs.
|
||||
</p>
|
||||
<a
|
||||
href="https://github.com/morluto/rea/blob/main/docs/browser-observation.md"
|
||||
>Browser observation guide</a
|
||||
>
|
||||
<a href="guides/browser/">Browser observation guide</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@
|
|||
<nav class="navigation" aria-label="Main navigation">
|
||||
<a href="./" aria-current="page">DX-Ball</a>
|
||||
<a class="nav-start" href="../../get-started/">Get started</a>
|
||||
<a href="https://github.com/morluto/rea/tree/main/docs">Docs</a>
|
||||
<a href="../../guides/">Guides</a>
|
||||
<a href="https://github.com/morluto/rea">GitHub</a>
|
||||
</nav>
|
||||
</div>
|
||||
|
|
@ -125,6 +125,34 @@
|
|||
sound code. The investigation needed to recover how that screen
|
||||
coordinate becomes a left/right panning value.
|
||||
</p>
|
||||
<div class="agent-terminal">
|
||||
<div class="terminal-header">
|
||||
<span class="terminal-title"
|
||||
><span class="terminal-dots" aria-hidden="true"
|
||||
><i></i><i></i><i></i></span
|
||||
>Your coding agent</span
|
||||
><button
|
||||
class="copy-button"
|
||||
type="button"
|
||||
data-copy="dxball-prompt"
|
||||
aria-label="Copy example prompt"
|
||||
>
|
||||
Copy
|
||||
</button>
|
||||
</div>
|
||||
<p class="terminal-prompt">
|
||||
<span class="prompt-symbol" aria-hidden="true">›</span
|
||||
><span id="dxball-prompt"
|
||||
>Use REA to find how DX-Ball calculates sound panning. Explain
|
||||
the calculation and show the code.</span
|
||||
><span class="terminal-cursor" aria-hidden="true"></span>
|
||||
</p>
|
||||
</div>
|
||||
<p class="caption">
|
||||
An example prompt for your coding agent, with the local executable
|
||||
available. The investigation below shows the REA queries and
|
||||
evidence behind the answer.
|
||||
</p>
|
||||
<p>
|
||||
The agent used REA to inspect the function, follow its brick-hit
|
||||
caller and read the constants from the executable. Those results
|
||||
|
|
@ -567,10 +595,11 @@
|
|||
><span>Current source, progress and local setup.</span>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
href="https://github.com/N0zoM1z0/dx-ball/blob/a55dca27ec0a07018c1b2c95ae2be027f7d8c3c4/docs/REA.md"
|
||||
>REA analysis workflow</a
|
||||
><span>Pinned tools, MCP sessions and retained evidence.</span>
|
||||
<a href="../../guides/native/">REA analysis workflow</a
|
||||
><span
|
||||
>Follow the calculation, then use the same approach with your
|
||||
own binary.</span
|
||||
>
|
||||
</li>
|
||||
<li>
|
||||
<a
|
||||
|
|
|
|||
Loading…
Reference in New Issue